diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 57139e52..90988320 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -571,7 +571,7 @@ public class GepafinConstant { public static final String PROTOCOL_EXTERNAL_NUMBER="NUM_PG"; public static final String PROTOCOL_EXTERNAL_DATE="DATA_PG_DT"; public static final String PROTOCOL_DOC_SUFFIX="#noauth"; - + public static final String CREATE_NDG="CHECK_OR_CREATE_NDG_CODE"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index eb3aa66f..38067a66 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -17,6 +17,11 @@ import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.entities.SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum; import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.*; +import net.gepafin.tendermanagement.model.request.ApplicationFormFieldRequestBean; +import net.gepafin.tendermanagement.model.request.ApplicationRequest; +import net.gepafin.tendermanagement.model.request.ApplicationRequestBean; +import net.gepafin.tendermanagement.model.request.EmailLogRequest; +import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.SortBy; import net.gepafin.tendermanagement.repositories.*; @@ -1990,7 +1995,7 @@ public class ApplicationDao { } // Wrap it in ="..." to make Excel treat it as a literal string - return "=\"" + stringValue.replace("\"", "\"\"") + "\""; + return stringValue; } catch (Exception e) { return ""; diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java index 2c4934db..f0fd43cb 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -1,1217 +1,1331 @@ -package net.gepafin.tendermanagement.dao; - -import com.amazonaws.services.s3.AmazonS3Client; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import feign.FeignException; -import io.jsonwebtoken.Claims; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -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; -import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest; -import net.gepafin.tendermanagement.model.request.AppointmentVisuraListRequest; -import net.gepafin.tendermanagement.model.request.AppointmentVisuraRequest; -import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; -import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; -import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; -import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; -import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse; -import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; -import net.gepafin.tendermanagement.model.response.NdgResponse; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; -import net.gepafin.tendermanagement.repositories.CompanyRepository; -import net.gepafin.tendermanagement.repositories.DocumentRepository; -import net.gepafin.tendermanagement.repositories.HubRepository; -import net.gepafin.tendermanagement.repositories.UserRepository; -import net.gepafin.tendermanagement.service.AmazonS3Service; -import net.gepafin.tendermanagement.service.ApplicationService; -import net.gepafin.tendermanagement.service.CompanyService; -import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; -import net.gepafin.tendermanagement.service.impl.ApplicationEvaluationServiceImpl; -import net.gepafin.tendermanagement.util.LoggingUtil; -import net.gepafin.tendermanagement.util.Utils; -import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; -import net.gepafin.tendermanagement.web.rest.api.errors.Status; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockMultipartFile; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -@Slf4j -@Component -public class AppointmentDao { - - @Value("${appointment.portal.user}") - private String user; - - @Value("${appointment.portal.password}") - private String password; - - @Value("${appointment.portal.source}") - private String source; - - @Value("${appointment.portal.context}") - private String context; - - @Value("${default.hub.uuid}") - private String defaultHubUuid; - - @Value("${aws.s3.url}") - private String s3Url; - - @Value("${aws.s3.bucket.name}") - private String OLD_BUCKET; - - @Autowired - private HubRepository hubRepository; - - @Autowired - private AppointmentApiService appointmentApiService; - - @Autowired - private ApplicationService applicationService; - - @Autowired - private CompanyService companyService; - - @Autowired - private ApplicationRepository applicationRepository; - - @Autowired - private CompanyRepository companyRepository; - - @Autowired - private DocumentDao documentDao; - - @Autowired - private AmazonS3Client s3Client; - - @Autowired - private DocumentRepository documentRepository; - - @Autowired - private HttpServletRequest request; - - @Autowired - private LoggingUtil loggingUtil; - - @Autowired - private TokenProvider tokenProvider; - - @Autowired - private NotificationDao notificationDao; - - @Autowired - private UserRepository userRepository; - - @Autowired - private ApplicationEvaluationServiceImpl applicationEvaluationService; - - @Autowired - private AmazonS3Service amazonS3Service; - - @Autowired - private ApplicationDao applicationDao; - - @Autowired - private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; - - @Autowired - private ApplicationEvaluationDao applicationEvaluationDao; - - private final Map executorMap = new ConcurrentHashMap<>(); - - private final ConcurrentHashMap threadForDocumentMap = new ConcurrentHashMap<>(); - - private static final ThreadLocal threadLocalHubId = new ThreadLocal<>(); - - public NdgResponse checkNdgForAppointment(Long applicationId) { - log.info("Starting NDG check for appointment. applicationId: {}", applicationId); - ApplicationEntity application = applicationService.validateApplication(applicationId); - NdgResponse ndgResponse = new NdgResponse(); - if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { - log.warn("NDG generation already in progress. applicationId: {}", applicationId); - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); - } - - if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_GENERATED) && application.getNdg() != null) { - ndgResponse.setNdg(application.getNdg()); - return ndgResponse; - } - - // Update application status - log.info("Updating NDG status to IN_PROGRESS. applicationId: {}", applicationId); - application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS); - applicationRepository.save(application); - - // Start async processing - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - loginToOdessa(hub, application); - startAsyncNdgProcessing(applicationId); - log.info("NDG check initiation completed. applicationId: {}", applicationId); - return ndgResponse; - } - - // 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 body = Collections.emptyMap(); - // ResponseEntity 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 body = Collections.emptyMap(); - // // Perform login API call - // ResponseEntity 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) { - log.info("Starting login to Odessa. HubId: {}, ApplicationId: {}", hub.getId(), application.getId()); - 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 { - String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); - log.info("Got the auth for login to odessa {}", authJwtToken); - hub.setAuthToken(authJwtToken); - hubRepository.save(hub); - Map body = Collections.emptyMap(); - ResponseEntity responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body); - if (responseLogin.getStatusCode() == HttpStatus.OK) { - log.info("Login to Odessa successful. Parsing response. HubId: {}", hub.getId()); - String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); - AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); - - 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 { - log.error("Login response from Odessa missing tokenId. HubId: {}", hub.getId()); - 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 forbidden error."); - - CheckPasswordExpiredOrErrorInResponse(application, forbiddenException); - } catch (Exception e) { - log.error("Failed to authenticate user on Odessa (Attempt {}): {}", attempt, e.getMessage(), e); - } - } - throw new RuntimeException("Max retries exceeded. Failed to login to Odessa."); - } - private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) { - - String responseBody = forbiddenException.contentUTF8(); - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(responseBody); - JsonNode errorsNode = rootNode.path("errors"); - - if (errorsNode.isArray()) { - for (JsonNode error : errorsNode) { - if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { - if (application != null) { - application.setNdgStatus(GepafinConstant.NDG_FAILED); - applicationRepository.save(application); - } - log.warn("Detected PASSWORD_EXPIRED error during Odessa login. ApplicationId: {}", application.getId()); - throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); - } - - 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)); - } - } - } - } - } - } catch (IOException e) { - log.error("Unexpected exception during Odessa login.Error: {}",e.getMessage(), e); - } - } - - private void startAsyncNdgProcessing(Long applicationId) { - // Check if a thread is already running for this application - if (executorMap.containsKey(applicationId)) { - log.warn("Async processing already running for applicationId: {}", applicationId); - return; - } - - // Create a dedicated thread for asynchronous processing - ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { - Thread thread = new Thread(runnable); - thread.setName("AsyncNdgProcessing-" + applicationId); - return thread; - }); - executorMap.put(applicationId, executor); - - executor.submit(() -> { - try { - log.info("Starting async processing for applicationId: {}", applicationId); - processNdgGeneration(applicationId); - } catch (Exception e) { - log.error("Error in async NDG processing for applicationId: {}", applicationId, e); - } finally { - // Cleanup resources - ExecutorService executorToShutdown = executorMap.remove(applicationId); - if (executorToShutdown != null) { - executorToShutdown.shutdown(); - } - log.info("Async processing completed for applicationId: {}", applicationId); - } - }); - } - - private void processNdgGeneration(Long applicationId) { - // Validate application, company, and hub - log.info("Starting NDG generation process for applicationId: {}", applicationId); - ApplicationEntity application = applicationService.validateApplication(applicationId); - CompanyEntity company = companyService.validateCompany(application.getCompanyId()); - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Ndg cannot be created for another Hub, it is default for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB)); - } - - try { - // Authenticate and fetch token if required - if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) { - authenticateAndSaveToken(hub, application); - } - - String authorizationToken = getBearerToken(hub); - - // Try retrieving NDG by VAT number - AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application); - if (isNdgValid(ndgResponse.getNdg())) { - saveNdgAndIdVisura(application, company, ndgResponse.getNdg(), null); - log.info("NDG successfully generated for applicationId: {}", applicationId); - } else { - // If NDG isn't immediately available, start polling - handleNdgPolling(application, company, hub, authorizationToken); - } - } catch (Exception e) { - log.error("Exception occurred during NDG generation. ApplicationId: {}, CompanyId: {}, HubId: {}, Error: {}", - applicationId, company.getId(), hub.getId(), e.getMessage(), e); - } - } - - private void handleNdgPolling(ApplicationEntity application, CompanyEntity company, HubEntity hub, String authorizationToken) { - - try { - log.info("Starting NDG polling for applicationId: {}, CompanyId: {}, HubId: {}", application.getId(),company.getId(), hub.getId()); - long startTime = System.currentTimeMillis(); - - while (true) { - if (application.getNdg() != null) { - log.info("NDG retrieved for applicationId: {}", application.getId()); - break; - } - - try { - // Fetch Visura list and attempt to parse NDG - String visuraListJson = getVisuraList(application.getIdVisura(), authorizationToken, application, hub); - log.debug("Parsing NDG from visura list response | ApplicationId: {}", application.getId()); - String ndg = parseNdgFromVisuraListResponse(visuraListJson); - if (isNdgValid(ndg)) { - // CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); - // ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - - log.info("Valid NDG retrieved: {} | ApplicationId: {}", ndg, application.getId()); - company.setNdg(ndg); - application.setNdg(ndg); - application.setNdgStatus(GepafinConstant.NDG_GENERATED); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - applicationRepository.save(application); - companyRepository.save(company); - ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); -// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); - - Map placeHolders = new HashMap<>(); - placeHolders.put("{{call_name}}", application.getCall().getName()); - String protocolNumber=application.getProtocol().getExternalProtocolNumber(); - if(protocolNumber==null){ - protocolNumber= String.valueOf(application.getProtocol().getProtocolNumber()); - } - placeHolders.put("{{protocol_number}}", protocolNumber); - notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); - notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION); - log.info("NDG saved successfully for applicationId: {}", application.getId()); - break; - } - - // Check if polling has timed out - if (System.currentTimeMillis() - startTime > TimeUnit.HOURS.toMillis(2)) { - log.warn("NDG polling timed out for applicationId: {}", application.getId()); - application.setNdgStatus(GepafinConstant.NDG_FAILED); - applicationRepository.save(application); - break; - } - - // Wait before the next polling attempt - Thread.sleep(TimeUnit.MINUTES.toMillis(15)); - } catch (InterruptedException e) { - log.warn("NDG polling interrupted for applicationId: {}", application.getId()); - Thread.currentThread().interrupt(); - break; - } catch (Exception e) { - log.error("Error during NDG polling for applicationId: {}", application.getId(), e); - } - } - } finally { - log.info("NDG polling completed for applicationId: {}", application.getId()); - } - } - - private static String getBearerToken(HubEntity hub) { - - return "Bearer " + hub.getAppointmentAuthTokenId(); - } - - private boolean isNdgValid(String ndg) { - - return ndg != null && !ndg.isEmpty(); - } - - private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) { - - application.setNdg(ndg); - application.setIdVisura(idVisura); - application.setNdgStatus(GepafinConstant.NDG_GENERATED); - application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); - company.setNdg(ndg); - companyRepository.save(company); - applicationRepository.save(application); - ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); -// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); - Map placeHolders = new HashMap<>(); - placeHolders.put("{{call_name}}", application.getCall().getName()); - String protocolNumber=application.getProtocol().getExternalProtocolNumber(); - if(protocolNumber==null){ - protocolNumber= String.valueOf(application.getProtocol().getProtocolNumber()); - } - placeHolders.put("{{protocol_number}}", protocolNumber); - notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); - notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION); - log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); - } - - private String getVisuraList(String idVisura, String authorizationToken, ApplicationEntity application, HubEntity hub) { - - log.info("Initiating Visura list retrieval | ApplicationId: {}, HubId: {}, IdVisura: {}", application.getId(), hub.getId(), idVisura); - AppointmentVisuraListRequest visuraListRequest = new AppointmentVisuraListRequest(); - AppointmentVisuraListRequest.VisuraFilter filter = new AppointmentVisuraListRequest.VisuraFilter(); - filter.setIdVisura(idVisura); - visuraListRequest.setFilter(filter); - - try { - String requestJson = Utils.convertObjectToJson(visuraListRequest); - ResponseEntity response = appointmentApiService.getVisuraList(requestJson, authorizationToken); - return Utils.convertObjectToJson(response.getBody()); - } catch (FeignException.Forbidden forbiddenException) { - log.warn("403 Forbidden while fetching Visura list. Attempting token regeneration | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); - // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub, application); - return getVisuraList(idVisura, newAuthorizationToken, application, hub); - } catch (Exception e) { - log.error("Error while fetching Visura list | ApplicationId: {}, HubId: {}, Error: {}", application.getId(), hub.getId(), e.getMessage(), e); - throw new RuntimeException("Error fetching Ndg List", e); - } - } - - private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) { - - try { - log.info("Initiating NDG retrieval by VAT number | ApplicationId: {}, HubId: {}, VAT: {}", application.getId(), hub.getId(), vatNumber); - // Prepare the NDG request - AppointmentNdgRequest ndgRequest = getAppointmentNdgRequest(vatNumber); - // Call the API to retrieve NDG - ResponseEntity response = appointmentApiService.getNdgByVatNumber(ndgRequest, authorizationToken); - String responseJson = Utils.convertObjectToJson(response.getBody()); - // Parse and return the NDG response - return parseNdgResponse(responseJson); - } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden during NDG retrieval | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); - logForbiddenError(); - // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub, application); - return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); - } catch (Exception e) { - log.error("Error during NDG retrieval | ApplicationId: {}, HubId: {}, Message: {}", application.getId(), hub.getId(), e.getMessage(), e); - throw new RuntimeException("NDG retrieval failed.", e); - } - } - - private String regenerateTokenAndSave(HubEntity hub, ApplicationEntity application) { - - hub = authenticateAndSaveToken(hub, application); - return "Bearer " + hub.getAppointmentAuthTokenId(); - } - - private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { - - try { - String visuraRequest = getAppointmentVisuraRequest(company, hub.getAreaCode()); - ResponseEntity response = appointmentApiService.createVisura(visuraRequest, authorizationToken); - String responseJson = Utils.convertObjectToJson(response.getBody()); - return parseVisuraResponse(responseJson); - } catch (FeignException.Forbidden forbiddenException) { - logForbiddenError(); - // Regenerate the token and retry - String newAuthorizationToken = regenerateTokenAndSave(hub, null); - return createVisura(company, newAuthorizationToken, hub); - } catch (Exception e) { - log.error("Failed to create Visura for Ndg : {}", e.getMessage()); - throw new RuntimeException("Visura creation failed for Ndg.", e); - } - } - - private static void logForbiddenError() { - - log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); - } - - private static AppointmentNdgRequest getAppointmentNdgRequest(String vatNumber) { - - log.info("Creating Appointment NDG Request | VAT Number: {}", vatNumber); - AppointmentNdgRequest request = new AppointmentNdgRequest(); - AppointmentNdgRequest.Filter filter = new AppointmentNdgRequest.Filter(); - filter.setPartitaIva(vatNumber); - - AppointmentNdgRequest.Pagination pagination = new AppointmentNdgRequest.Pagination(); - pagination.setTargetPage(AppointmentApiConstant.TARGET_PAGE_SIZE); - pagination.setRecordsPerPage(AppointmentApiConstant.RECORD_PER_PAGE_SIZE); - - request.setFilter(filter); - request.setPagination(pagination); - return request; - } - - private static String getAppointmentVisuraRequest(CompanyEntity company, String areaCode) { - - AppointmentVisuraRequest visuraRequest = new AppointmentVisuraRequest(); - AppointmentVisuraRequest.VisuraInput input = new AppointmentVisuraRequest.VisuraInput(); - input.setPartitaIva(company.getVatNumber()); - input.setCodiceFiscale(company.getCodiceFiscale()); - input.setCodArea(areaCode); - input.setVisuraMode(AppointmentApiConstant.VISURA_MODE); - input.setVisuraProvider(AppointmentApiConstant.VISURA_PROVIDER); - input.setCodAgente(AppointmentApiConstant.COD_AGENTE); - input.setAnagraficaLegame(AppointmentApiConstant.IS_ANAGRAFICA_LEGAME); - input.setCreaAnagrafica(AppointmentApiConstant.CREA_ANAGRAFICA); - input.setFromRating(AppointmentApiConstant.IS_FROM_RATING); - input.setSalvaDocumenti(AppointmentApiConstant.SALVA_DOCUMENTI); - input.setVisuraType(AppointmentApiConstant.VISURA_TYPE); - visuraRequest.setInput(input); - return Utils.convertObjectToJson(visuraRequest); - } - - private String parseNdgFromVisuraListResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - - if (dataNode != null && dataNode.isArray() && dataNode.size() > 0) { - JsonNode firstEntry = dataNode.get(0); - JsonNode ndgClienteNode = firstEntry.get("ndgCliente"); - if (ndgClienteNode != null && ndgClienteNode.get("code") != null) { - String code = ndgClienteNode.get("code").asText(); - return normalizeNullValue(code); - } - } - log.warn("NDG not found in Visura List API response."); - return null; - } catch (Exception e) { - log.error("Failed to parse NDG from Visura List API response: {}", e.getMessage(), e); - throw new RuntimeException("Error parsing NDG from Visura List API response", e); - } - } - - public AppointmentLoginResponse parseLoginResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - - if (dataNode != null) { - AppointmentLoginResponse response = new AppointmentLoginResponse(); - response.setTokenId(dataNode.get("tokenId").asText()); - JsonNode areasNode = dataNode.get("areas"); - if (areasNode != null && areasNode.isArray() && areasNode.size() > 0) { - response.setAreaCode(areasNode.get(0).get("code").asText()); - } - response.setCompanyId(dataNode.get("companyId").asLong()); - return response; - } else { - throw new RuntimeException("Invalid JSON structure: Missing 'data' node."); - } - } catch (Exception e) { - throw new RuntimeException("Failed to parse response from loginApi for odessa: " + e.getMessage(), e); - } - } - - public AppointmentLoginResponse parseVisuraResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - - if (dataNode != null) { - AppointmentLoginResponse response = new AppointmentLoginResponse(); - response.setIdVisura(normalizeNullValue(dataNode.get(GepafinConstant.ID_VISURA_STRING).asText())); - response.setNdg(normalizeNullValue(dataNode.get(GepafinConstant.NDG_STRING).asText())); - return response; - } else { - throw new RuntimeException("Invalid JSON structure: Missing 'data' node."); - } - } catch (Exception e) { - throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); - } - } - - public AppointmentLoginResponse parseNdgResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode dataArray = rootNode.get(GepafinConstant.DATA_STRING); - if (dataArray == null || !dataArray.isArray() || dataArray.isEmpty()) { - log.info("NDG data is empty or missing in the response."); - AppointmentLoginResponse emptyResponse = new AppointmentLoginResponse(); - emptyResponse.setNdg(null); - return emptyResponse; - } - JsonNode firstDataEntry = dataArray.get(0); - AppointmentLoginResponse response = new AppointmentLoginResponse(); - if (firstDataEntry.has(GepafinConstant.NDG_STRING)) { - response.setNdg(normalizeNullValue(firstDataEntry.get(GepafinConstant.NDG_STRING).asText())); - } - return response; - } catch (Exception e) { - log.error("Failed to parse response: {}", e.getMessage(), e); - throw new RuntimeException("Failed to parse NDG response.", e); - } - } - - private String normalizeNullValue(String value) { - - return (value == null || GepafinConstant.NULL_STRING.equalsIgnoreCase(value.trim())) ? null : value; - } - - public AppointmentCreationResponse createAppointment(Long applicationId, CreateAppointmentRequest createAppointmentRequest) { - // Validate the application - log.info("Starting appointment creation for applicationId: {}", applicationId); - ApplicationEntity application = applicationService.validateApplication(applicationId); - - AppointmentCreationResponse appointmentCreationResponse = new AppointmentCreationResponse(); - - ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); - HubEntity hub = hubRepository.findByHubId(application.getHubId()); - - // Check hub UUID and enforce constraints - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Appointment cannot be created for another Hub; default is required for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_APPOINTMENT_FOR_ANOTHER_HUB)); - } - - try { - // Pre-check conditions for appointment creation - if (application.getNdg() != null && !Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS) && application.getAppointmentId() != null) { - appointmentCreationResponse.setAppointmentId(application.getAppointmentId()); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_ALREADY_CREATED)); - // return appointmentCreationResponse; - } - - if (application.getNdg() == null && Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS)) { - log.warn("NDG in progress but not available for applicationId: {}", applicationId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); - } - - // Generate authorization token and fetch template data - String authorizationToken = regenerateTokenAndSave(hub, application); - Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); - if (appointmentTemplateId == null) { - log.error("Missing appointment template ID for applicationId: {}", applicationId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_CANNOT_BE_CREATED)); - } - ResponseEntity response = appointmentApiService.getAppointmentTemplateForTemplateCreation(authorizationToken, appointmentTemplateId); - - if (response.getStatusCode() != HttpStatus.OK) { - log.error("Failed to retrieve appointment template for appointment creation. Status: {}", response.getStatusCode()); - throw new IllegalStateException("Failed to retrieve appointment template for appointment creation"); - } - - // Parse template data - String responseDataForTemplate = Utils.convertObjectToJson(response.getBody()); - AppointmentCreationRequest templateRichiestaData = parseTemplateResponseData(responseDataForTemplate); - - // Build the appointment request body - AppointmentCreationRequest appointmentCreationRequest = buildAppointmentCreationRequest(applicationId, createAppointmentRequest, appointmentTemplateId, - templateRichiestaData); - log.info("AppointmentCreationRequest : {}", appointmentCreationRequest); - String appointmentRequestBody = Utils.convertObjectToJson(appointmentCreationRequest); - - // Make API call to create the appointment - log.info("Context:{}, Authorization Token : {}, RequestBody : {}", context, authorizationToken, appointmentRequestBody); - ResponseEntity appointmentResponse = appointmentApiService.createAppointment(authorizationToken, context, appointmentRequestBody); - String appointmentId = extractAppointmentIdFromResponse(appointmentResponse); - - if (appointmentId == null) { - log.error("Failed to extract appointment ID from response for applicationId: {}", applicationId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_NOT_CREATED)); - } - // Update application with the appointment ID - application.setAppointmentId(appointmentId); - application.setStatus(ApplicationStatusTypeEnum.APPOINTMENT.getValue()); - applicationRepository.save(application); - - // Log version history - loggingUtil.addVersionHistory( - VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); - - appointmentCreationResponse.setAppointmentId(appointmentId); - return appointmentCreationResponse; - - } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden received while retrieving template. Attempting to regenerate token and retry. Application ID: {}", applicationId); - regenerateTokenAndSave(hub, application); - return createAppointment(applicationId, createAppointmentRequest); - } - } - - private String extractAppointmentIdFromResponse(ResponseEntity appointmentResponse) { - - if (appointmentResponse.getBody() != null) { - log.info("Appointment API Response : {}", appointmentResponse.getBody()); - try { - Map responseBody = (Map) appointmentResponse.getBody(); - // 1. Try to get appointment ID from data.id - if (responseBody.containsKey(GepafinConstant.DATA_STRING)) { - Map data = (Map) 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> errors = (List>) responseBody.get(GepafinConstant.ERROR_STRING); - if (errors != null && !errors.isEmpty()) { - Map firstError = errors.get(0); - if (firstError.containsKey(GepafinConstant.CAUSE_STRING)) { - Map cause = (Map) 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; - } - - public AppointmentCreationRequest parseTemplateResponseData(String jsonResponse) { - - try { - - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - JsonNode richiesteClienteArray = rootNode.path(GepafinConstant.DATA_STRING).path(GepafinConstant.RICHIESTE_CLIENTE_STRING); - - // Initialize the result object - AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); - AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); - List richiestaClienteList = new ArrayList<>(); - if (!richiesteClienteArray.isArray()) { - log.warn("richiesteCliente array is missing or not an array."); - return new AppointmentCreationRequest(); - } - for (JsonNode richiestaNode : richiesteClienteArray) { - if (richiestaNode.isNull()) - continue; - - AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); - JsonNode prodottoNode = richiestaNode.path(AppointmentApiConstant.PRODOTTO); - String prodottoCode = prodottoNode.path(AppointmentApiConstant.PRODOTTO_CODE).asText(); - - richiestaCliente.setCodProdotto(prodottoCode); - richiestaCliente.setIdMotivazione(getIntValue(richiestaNode)); - richiestaCliente.setCodAbi(getTextValue(richiestaNode, AppointmentApiConstant.COD_ABI)); - richiestaCliente.setCodCab(getTextValue(richiestaNode, AppointmentApiConstant.COD_CAB)); - richiestaCliente.setIdNota(getTextValue(richiestaNode, AppointmentApiConstant.ID_NOTA)); - richiestaCliente.setImportoAgevolato(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_AGEVOLATO)); - richiestaCliente.setImportoMedioLungoTermine(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_MEDIOLUNGO_TERMINE)); - richiestaCliente.setCodTipoProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_TIPO_PRODOTTO)); - richiestaCliente.setCodCategoriaProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_CATEGORIA_PRODOTTO)); - richiestaCliente.setCodFormaTecnica(getTextValue(richiestaNode, AppointmentApiConstant.COD_FORMATECNICA)); - richiestaCliente.setCodOperazione(getTextValue(richiestaNode, AppointmentApiConstant.COD_OPERAZIONE)); - - richiestaClienteList.add(richiestaCliente); - } - - input.setRichiestaCliente(richiestaClienteList); - appointmentCreationRequest.setInput(input); - - return appointmentCreationRequest; - - } catch (JsonProcessingException e) { - log.error("JSON processing error: {}", e.getMessage(), e); - throw new IllegalStateException("Invalid JSON structure in template response", e); - } - } - - private String getTextValue(JsonNode node, String fieldName) { - - return node.path(fieldName).isTextual() ? node.path(fieldName).asText() : null; - } - - private int getIntValue(JsonNode node) { - - return node.path(AppointmentApiConstant.MOTIVAZIONE).path(AppointmentApiConstant.MOTIVAZIONE_ID).asInt(); - } - - public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, Long areaCode, - AppointmentCreationRequest templateRichiestaData) { - - ApplicationEntity application = applicationService.validateApplication(applicationId); - CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota(); - - AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); - AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); - - // Set Input Fields - input.setId(areaCode); - input.setNdg(application.getNdg()); - - // Populate richiestaCliente from template data - List richiestaClienteList = new ArrayList<>(); - for (AppointmentCreationRequest.RichiestaCliente templateRichiesta : templateRichiestaData.getInput().getRichiestaCliente()) { - AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); - BeanUtils.copyProperties(templateRichiesta, richiestaCliente); - - // Add specific `nota` - AppointmentCreationRequest.Nota requestNota = new AppointmentCreationRequest.Nota(); - requestNota.setTitolo(nota.getTitolo()); - requestNota.setTesto(nota.getTesto()); - richiestaCliente.setNota(requestNota); - richiestaCliente.setDurataMesiFinanziamento(createAppointmentRequest.getDurataMesiFinanziamento()); - richiestaCliente.setImportoBreveTermine(createAppointmentRequest.getImportoBreveTermine()); - richiestaClienteList.add(richiestaCliente); - } - - input.setRichiestaCliente(richiestaClienteList); - appointmentCreationRequest.setInput(input); - return appointmentCreationRequest; - } - - public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { - log.info("Initiating upload to external system for documentId: {}", documentId); - // 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, 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(); - response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); - return response; - } - // Check if a thread is already running for this document upload - if (threadForDocumentMap.containsKey(documentId)) { - log.warn("Document upload already running for documentId: {}", documentId); - throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); - } - // Start the upload process in the background - ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { - Thread thread = new Thread(runnable); - thread.setName(GepafinConstant.ASYNC_DOCUMENT_UPLOAD_NAME + documentId); - return thread; - }); - 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, finalApplication); - } catch (Exception e) { - log.error("Error in async document upload for documentId: {}", documentId, e); - } finally { - // Cleanup resources - ExecutorService executorToShutdown = threadForDocumentMap.remove(documentId); - if (executorToShutdown != null) { - executorToShutdown.shutdown(); - threadLocalHubId.remove(); - } - log.info("Async document upload completed for documentId: {}", documentId); - } - }); - return null; - } - - private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) { - log.info("Starting sync document upload for documentId: {}", documentId); - // Synchronous upload logic - DocumentEntity systemDoc = documentDao.validateDocument(documentId); - - Long hubId = threadLocalHubId.get(); - HubEntity hub = hubRepository.findByHubId(hubId); - - if (!hub.getUniqueUuid().equals(defaultHubUuid)) { - log.info("Document cannot be uploaded for another Hub, it is default for Gepafin."); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB)); - } - - log.info("Got Document in system: {}", systemDoc); - String oldUrl = systemDoc.getFilePath(); - String authorizationToken = getBearerToken(hub); - - try { - File localFile = downloadFileFromS3(oldUrl); - MultipartFile multipartFile = convertFileToMultipartFile(localFile); - - UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest(); - externalSystemRequest.setInput(getUploadDocumentInput(docToExternalSystemRequest)); - - String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest); - ResponseEntity uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest, - multipartFile); - - String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); - DocumentUploadResponse parsedResponse = parseDocumentUploadResponse(responseData); - - if (parsedResponse == null) { - log.error("Upload failed: parsed response is null for documentId: {}", documentId); - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT)); - } - - // Save the documentAttachmentId to the database - systemDoc.setDocumentAttachmentId(parsedResponse.getDocumentAttachmentId()); - documentRepository.save(systemDoc); - - log.info("Document uploaded successfully to external system: {}", parsedResponse); - } catch (FeignException.Forbidden forbiddenException) { - log.error("403 Forbidden from external system during upload for documentId: {}. Retrying with new token...", documentId); - 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)); - } - } - - private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) { - - UploadDocToExternalSystemRequest.Input input = new UploadDocToExternalSystemRequest.Input(); - input.setIdTipoProtocollo(docToExternalSystemRequest.getInput().getIdTipoProtocollo()); - input.setIdClassificazione(docToExternalSystemRequest.getInput().getIdClassificazione()); - input.setFlagDaFirmare(docToExternalSystemRequest.getInput().getFlagDaFirmare()); - input.setDescrizione(docToExternalSystemRequest.getInput().getDescrizione()); - - UploadDocToExternalSystemRequest.Input.Attributes attributes = new UploadDocToExternalSystemRequest.Input.Attributes(); - attributes.setNdg(docToExternalSystemRequest.getInput().getAttributes().getNdg()); - attributes.setEmail(docToExternalSystemRequest.getInput().getAttributes().getEmail()); - - input.setAttributes(attributes); - return input; - } - - public static MultipartFile convertFileToMultipartFile(File file) throws IOException { - - FileInputStream input = new FileInputStream(file); - return new MockMultipartFile(file.getName(), file.getName(), MediaType.APPLICATION_OCTET_STREAM_VALUE, input); - } - - private File downloadFileFromS3(String fileUrl) throws Exception { - - String key = amazonS3Service.extractS3KeyFromUrl(fileUrl); - String fileName = extractFileName(key); - String folderPath = key.substring(0, key.lastIndexOf("/")); - File localFile = new File(GepafinConstant.TEMP_FILE_PATH + fileName); - - try (InputStream s3Stream = amazonS3Service.getFile(folderPath, key); FileOutputStream outputStream = new FileOutputStream(localFile)) { - s3Stream.transferTo(outputStream); - } - - log.info("Downloaded file from old S3 bucket: {}", key); - return localFile; - } - - private String extractFileName(String filePath) { - - String[] parts = filePath.split("/"); - return parts[parts.length - 1]; - - } - - public DocumentUploadResponse parseDocumentUploadResponse(String jsonResponse) { - - try { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode rootNode = objectMapper.readTree(jsonResponse); - - // Navigate to the "data" node - JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); - if (dataNode != null) { - DocumentUploadResponse response = new DocumentUploadResponse(); - - // Extract "documentAttachmentId" - JsonNode documentAttachmentIdNode = dataNode.get(GepafinConstant.DOCUMENT_ATTACHMENT_ID_STRING); - if (documentAttachmentIdNode != null) { - response.setDocumentAttachmentId(documentAttachmentIdNode.asText()); - } else { - throw new RuntimeException("Invalid JSON structure: Missing 'documentAttachmentId' node."); - } - - return response; - } else { - return null; - } - } catch (Exception e) { - throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); - } - } +package net.gepafin.tendermanagement.dao; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.FeignException; +import io.jsonwebtoken.Claims; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +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.*; +import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest; +import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest; +import net.gepafin.tendermanagement.model.request.AppointmentVisuraListRequest; +import net.gepafin.tendermanagement.model.request.AppointmentVisuraRequest; +import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; +import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; +import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; +import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; +import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse; +import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; +import net.gepafin.tendermanagement.model.response.NdgResponse; +import net.gepafin.tendermanagement.repositories.ApplicationRepository; +import net.gepafin.tendermanagement.repositories.CompanyRepository; +import net.gepafin.tendermanagement.repositories.DocumentRepository; +import net.gepafin.tendermanagement.repositories.HubRepository; +import net.gepafin.tendermanagement.repositories.UserRepository; +import net.gepafin.tendermanagement.service.AmazonS3Service; +import net.gepafin.tendermanagement.service.ApplicationService; +import net.gepafin.tendermanagement.service.CompanyService; +import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; +import net.gepafin.tendermanagement.service.impl.ApplicationEvaluationServiceImpl; +import net.gepafin.tendermanagement.util.LoggingUtil; +import net.gepafin.tendermanagement.util.Utils; +import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.ScheduledExecutorService; + +@Slf4j +@Component +public class AppointmentDao { + + @Value("${appointment.portal.user}") + private String user; + + @Value("${appointment.portal.password}") + private String password; + + @Value("${appointment.portal.source}") + private String source; + + @Value("${appointment.portal.context}") + private String context; + + @Value("${default.hub.uuid}") + private String defaultHubUuid; + + @Value("${aws.s3.url}") + private String s3Url; + + @Value("${aws.s3.bucket.name}") + private String OLD_BUCKET; + + @Autowired + private HubRepository hubRepository; + + @Autowired + private AppointmentApiService appointmentApiService; + + @Autowired + private ApplicationService applicationService; + + @Autowired + private CompanyService companyService; + + @Autowired + private ApplicationRepository applicationRepository; + + @Autowired + private CompanyRepository companyRepository; + + @Autowired + private DocumentDao documentDao; + + @Autowired + private AmazonS3Client s3Client; + + @Autowired + private DocumentRepository documentRepository; + + @Autowired + private HttpServletRequest request; + + @Autowired + private LoggingUtil loggingUtil; + + @Autowired + private TokenProvider tokenProvider; + + @Autowired + private NotificationDao notificationDao; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ApplicationEvaluationServiceImpl applicationEvaluationService; + + @Autowired + private AmazonS3Service amazonS3Service; + + @Autowired + private ApplicationDao applicationDao; + + @Autowired + private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; + + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + + private final Map executorMap = new ConcurrentHashMap<>(); + + + private final ConcurrentHashMap threadForDocumentMap = new ConcurrentHashMap<>(); + + private static final ThreadLocal threadLocalHubId = new ThreadLocal<>(); + + public NdgResponse checkNdgForAppointment(Long applicationId) { + log.info("Starting NDG check for appointment. applicationId: {}", applicationId); + ApplicationEntity application = applicationService.validateApplication(applicationId); + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + + NdgResponse ndgResponse = new NdgResponse(); + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { + log.warn("NDG generation already in progress. applicationId: {}", applicationId); + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + } + + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_GENERATED) && application.getNdg() != null) { + ndgResponse.setNdg(application.getNdg()); + return ndgResponse; + } + + // Update application status + log.info("Updating NDG status to IN_PROGRESS. applicationId: {}", applicationId); + application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS); + applicationRepository.save(application); + + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + + + // Start async processing + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + loginToOdessa(hub, application); + startAsyncNdgProcessing(applicationId); + log.info("NDG check initiation completed. applicationId: {}", applicationId); + return ndgResponse; + } + + // 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 body = Collections.emptyMap(); + // ResponseEntity 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 body = Collections.emptyMap(); + // // Perform login API call + // ResponseEntity 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) { + log.info("Starting login to Odessa. HubId: {}, ApplicationId: {}", hub.getId(), application.getId()); + 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 { + String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); + log.info("Got the auth for login to odessa {}", authJwtToken); + hub.setAuthToken(authJwtToken); + hubRepository.save(hub); + Map body = Collections.emptyMap(); + ResponseEntity responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body); + if (responseLogin.getStatusCode() == HttpStatus.OK) { + log.info("Login to Odessa successful. Parsing response. HubId: {}", hub.getId()); + String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody()); + AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson); + + 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 { + log.error("Login response from Odessa missing tokenId. HubId: {}", hub.getId()); + 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 forbidden error."); + + CheckPasswordExpiredOrErrorInResponse(application, forbiddenException); + } catch (Exception e) { + log.error("Failed to authenticate user on Odessa (Attempt {}): {}", attempt, e.getMessage(), e); + } + } + throw new RuntimeException("Max retries exceeded. Failed to login to Odessa."); + } + private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) { + + String responseBody = forbiddenException.contentUTF8(); + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(responseBody); + JsonNode errorsNode = rootNode.path("errors"); + + if (errorsNode.isArray()) { + for (JsonNode error : errorsNode) { + if (GepafinConstant.PASSWORD_EXPIRED.equals(error.path("errorCode").asText())) { + if (application != null) { + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + } + log.warn("Detected PASSWORD_EXPIRED error during Odessa login. ApplicationId: {}", application.getId()); + throw new CustomValidationException(Status.FORBIDDEN, Translator.toLocale(GepafinConstant.PASSWORD_EXPIRED_LOGIN_TO_ODESSA)); + } + + 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)); + } + } + } + } + } + } catch (IOException e) { + log.error("Unexpected exception during Odessa login.Error: {}",e.getMessage(), e); + } + } + + private void startAsyncNdgProcessing(Long applicationId) { + // If already polling for this applicationId, do nothing: + if (executorMap.containsKey(applicationId)) { + log.warn("Async processing already running for applicationId: {}", applicationId); + return; + } + ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); + + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> { + Thread t = new Thread(runnable); + t.setName("AsyncNdgProcessing-" + applicationId); + return t; + }); + executorMap.put(applicationId, scheduler); + + // Record the start time so we can stop after 2 hours: + long startTime = System.currentTimeMillis(); + long twoHoursMs = TimeUnit.HOURS.toMillis(2); + long fifteenMin = 15; // in MINUTES + + // We need a reference to cancel the scheduled task from inside itself when we're done: + AtomicReference> futureRef = new AtomicReference<>(); + + Runnable pollingTask = () -> { + RequestContextHolder.setRequestAttributes(requestAttributes, true); + Utils.setHttpServletRequestForThread(request,HttpMethodEnum.POST.getValue(),GepafinConstant.CREATE_NDG, (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID)); + try { + // 1) If 2 hours have already passed, mark as FAILED and shut down: + if (System.currentTimeMillis() - startTime > twoHoursMs) { + ApplicationEntity app = applicationService.validateApplication(applicationId); + log.warn("2-hour timeout reached for applicationId {}. Marking NDG_FAILED.", applicationId); + app.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(app); + + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + return; + } + + // 2) Otherwise, call processNdgGeneration once: + processNdgGeneration(applicationId); + + // 3) After return, check if NDG is now set or timed out. If so, cancel & shut down: + ApplicationEntity updated = applicationService.validateApplication(applicationId); + if (isNdgValid(updated.getNdg())) { + log.info("NDG found for applicationId {}. Shutting down scheduler.", applicationId); + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + } else if (updated.getNdgStatus() != null && updated.getNdgStatus().equals(GepafinConstant.NDG_FAILED)) { + log.info("NDG status is NDG_FAILED for applicationId {}. Shutting down scheduler.", applicationId); + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + } + // Otherwise: no NDG yet, not timed out → next run happens in 15 minutes automatically. + } catch (Exception ex) { + log.error("Unexpected error during scheduled polling for applicationId {}: {}", applicationId, ex.getMessage(), ex); + try { + ApplicationEntity checkApp = applicationService.validateApplication(applicationId); + if (System.currentTimeMillis() - startTime > twoHoursMs) { + log.warn("After exception, 2-hour window passed for applicationId {}. Marking NDG_FAILED.", applicationId); + checkApp.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(checkApp); + + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + } + } catch (Exception ignore) { + futureRef.get().cancel(false); + shutdownScheduler(applicationId); + } + } + }; + + // Schedule pollingTask: run now (delay=0), then every fifteen minutes: + ScheduledFuture future = scheduler.scheduleWithFixedDelay(pollingTask, 0, // initial delay = 0 min → run immediately + fifteenMin, // subsequent runs every 15 minutes + TimeUnit.MINUTES); + futureRef.set(future); + } + + private void shutdownScheduler(Long applicationId) { + + ScheduledExecutorService shed = executorMap.remove(applicationId); + if (shed != null) { + shed.shutdownNow(); + } + log.info("Scheduler shut down for applicationId: {}", applicationId); + } + + private void processNdgGeneration(Long applicationId) { + // Validate application, company, and hub + log.info("Starting NDG generation process for applicationId: {}", applicationId); + ApplicationEntity application = applicationService.validateApplication(applicationId); + CompanyEntity company = companyService.validateCompany(application.getCompanyId()); + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Ndg cannot be created for another Hub, it is default for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB)); + } + + try { + // Authenticate and fetch token if required + if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) { + authenticateAndSaveToken(hub, application); + } + + String authorizationToken = getBearerToken(hub); + + // Try retrieving NDG by VAT number + AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application); + if (isNdgValid(ndgResponse.getNdg())) { + saveNdgAndIdVisura(application, company, ndgResponse.getNdg()); + log.info("NDG successfully generated for applicationId: {}", applicationId); + } else { + log.info("Polling for NDG for applicationId: {}", applicationId); + handleNdgPolling(application, company, hub, authorizationToken); + } + } catch (Exception e) { + log.error("Exception occurred during NDG generation. ApplicationId: {}, CompanyId: {}, HubId: {}, Error: {}", applicationId, company.getId(), hub.getId(), + e.getMessage(), e); + } + } + + private void handleNdgPolling(ApplicationEntity application, CompanyEntity company, HubEntity hub, String authorizationToken) { + + log.info("Starting single‐shot NDG polling attempt for applicationId: {}, CompanyId: {}, HubId: {}", application.getId(), company.getId(), hub.getId()); + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyEntity=Utils.getClonedEntityForData(company); + long startTime = System.currentTimeMillis(); + long twoHoursMs = TimeUnit.HOURS.toMillis(2); + + try { + // 1) If NDG was already populated (perhaps by another thread), skip polling. + if (application.getNdg() != null) { + log.info("NDG already present for applicationId {}. Exiting single‐shot polling.", application.getId()); + return; + } + + // 2) Attempt to create Visura (this may immediately return a valid NDG) + AppointmentLoginResponse visuraResponse = createVisura(company, authorizationToken, hub); + + // 2a) If createVisura gave us a valid NDG, persist & exit + String fetchedNdg = visuraResponse.getNdg(); + if (isNdgValid(fetchedNdg)) { + log.info("Valid NDG retrieved from createVisura(): {} | applicationId: {}", fetchedNdg, application.getId()); + + company.setNdg(fetchedNdg); + application.setNdg(fetchedNdg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + application.setIdVisura(visuraResponse.getIdVisura()); + + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity eval = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeholders = new HashMap<>(); + placeholders.put("{{call_name}}", application.getCall().getName()); + placeholders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + notificationDao.sendNotificationToInstructor(placeholders, eval, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeholders, NotificationTypeEnum.NDG_GENERATION); + + log.info("NDG saved successfully for applicationId: {}", application.getId()); + return; + } + + // 2b) If no immediate NDG, fetch the Visura list JSON and parse out NDG + String visuraListJson = getVisuraList(visuraResponse.getIdVisura(), authorizationToken, application, hub); + log.debug("Parsing NDG from VisuraList JSON for applicationId: {}", application.getId()); + String parsedNdg = parseNdgFromVisuraListResponse(visuraListJson); + + if (isNdgValid(parsedNdg)) { + log.info("Valid NDG parsed from VisuraList: {} | applicationId: {}", parsedNdg, application.getId()); + + company.setNdg(parsedNdg); + application.setNdg(parsedNdg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + application.setIdVisura(visuraResponse.getIdVisura()); + + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity eval = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); + Map placeholders = new HashMap<>(); + placeholders.put("{{call_name}}", application.getCall().getName()); + placeholders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + notificationDao.sendNotificationToInstructor(placeholders, eval, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeholders, NotificationTypeEnum.NDG_GENERATION); + + log.info("NDG saved successfully for applicationId: {}", application.getId()); + return; + } + + // 3) Neither direct API nor parsing yielded a valid NDG. Check timeout. + if (System.currentTimeMillis() - startTime > twoHoursMs) { + log.warn("NDG polling timed out after 2 hours for applicationId: {}", application.getId()); + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + return; + } + + // 4) No NDG yet—just return. The scheduler will retry in 15 minutes. + log.info("No valid NDG yet for applicationId {}. Returning to scheduler—next attempt in 15 minutes.", application.getId()); + } catch (Exception e) { + log.error("Exception during NDG polling for applicationId: {}", application.getId(), e); + // If timeout after exception, mark NDG_FAILED + if (System.currentTimeMillis() - startTime > twoHoursMs) { + log.warn("Example: exiting single‐shot polling due to timeout after exception for applicationId: {}", application.getId()); + application.setNdgStatus(GepafinConstant.NDG_FAILED); + applicationRepository.save(application); + } else { + log.info("Exception occurred but not timed out for applicationId {}. Returning to scheduler for next attempt in 15 minutes.", application.getId()); + } + } + + log.info("NDG polling completed for applicationId: {}", application.getId()); + } + + private static String getBearerToken(HubEntity hub) { + + return "Bearer " + hub.getAppointmentAuthTokenId(); + } + + private boolean isNdgValid(String ndg) { + + return ndg != null && !ndg.isEmpty(); + } + + private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg) { + + ApplicationEntity oldApplication = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyEntity=Utils.getClonedEntityForData(company); + application.setNdg(ndg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setStatus(ApplicationStatusTypeEnum.NDG.getValue()); + company.setNdg(ndg); + companyRepository.save(company); + applicationRepository.save(application); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build()); + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build()); + + ApplicationEvaluationEntity applicationEvaluationEntity = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId()); +// Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.NDG_GENERATION); + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}", application.getCall().getName()); + placeHolders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber())); + notificationDao.sendNotificationToInstructor(placeHolders, applicationEvaluationEntity, NotificationTypeEnum.NDG_GENERATION); + notificationDao.sendNotificationToSuperUser(application, placeHolders, NotificationTypeEnum.NDG_GENERATION); + log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); + } + + private String getVisuraList(String idVisura, String authorizationToken, ApplicationEntity application, HubEntity hub) { + + log.info("Initiating Visura list retrieval | ApplicationId: {}, HubId: {}, IdVisura: {}", application.getId(), hub.getId(), idVisura); + AppointmentVisuraListRequest visuraListRequest = new AppointmentVisuraListRequest(); + AppointmentVisuraListRequest.VisuraFilter filter = new AppointmentVisuraListRequest.VisuraFilter(); + filter.setIdVisura(idVisura); + visuraListRequest.setFilter(filter); + + try { + String requestJson = Utils.convertObjectToJson(visuraListRequest); + ResponseEntity response = appointmentApiService.getVisuraList(requestJson, authorizationToken); + return Utils.convertObjectToJson(response.getBody()); + } catch (FeignException.Forbidden forbiddenException) { + log.warn("403 Forbidden while fetching Visura list. Attempting token regeneration | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, application); + return getVisuraList(idVisura, newAuthorizationToken, application, hub); + } catch (Exception e) { + log.error("Error while fetching Visura list | ApplicationId: {}, HubId: {}, Error: {}", application.getId(), hub.getId(), e.getMessage(), e); + throw new RuntimeException("Error fetching Ndg List", e); + } + } + + private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) { + + try { + log.info("Initiating NDG retrieval by VAT number | ApplicationId: {}, HubId: {}, VAT: {}", application.getId(), hub.getId(), vatNumber); + // Prepare the NDG request + AppointmentNdgRequest ndgRequest = getAppointmentNdgRequest(vatNumber); + // Call the API to retrieve NDG + ResponseEntity response = appointmentApiService.getNdgByVatNumber(ndgRequest, authorizationToken); + String responseJson = Utils.convertObjectToJson(response.getBody()); + // Parse and return the NDG response + return parseNdgResponse(responseJson); + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden during NDG retrieval | ApplicationId: {}, HubId: {}", application.getId(), hub.getId()); + logForbiddenError(); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, application); + return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); + } catch (Exception e) { + log.error("Error during NDG retrieval | ApplicationId: {}, HubId: {}, Message: {}", application.getId(), hub.getId(), e.getMessage(), e); + throw new RuntimeException("NDG retrieval failed.", e); + } + } + + private String regenerateTokenAndSave(HubEntity hub, ApplicationEntity application) { + + hub = authenticateAndSaveToken(hub, application); + return "Bearer " + hub.getAppointmentAuthTokenId(); + } + + private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) { + + try { + String visuraRequest = getAppointmentVisuraRequest(company, hub.getAreaCode()); + ResponseEntity response = appointmentApiService.createVisura(visuraRequest, authorizationToken); + String responseJson = Utils.convertObjectToJson(response.getBody()); + return parseVisuraResponse(responseJson); + } catch (FeignException.Forbidden forbiddenException) { + logForbiddenError(); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub, null); + return createVisura(company, newAuthorizationToken, hub); + } catch (Exception e) { + log.error("Failed to create Visura for Ndg : {}", e.getMessage()); + throw new RuntimeException("Visura creation failed for Ndg.", e); + } + } + + private static void logForbiddenError() { + + log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); + } + + private static AppointmentNdgRequest getAppointmentNdgRequest(String vatNumber) { + + log.info("Creating Appointment NDG Request | VAT Number: {}", vatNumber); + AppointmentNdgRequest request = new AppointmentNdgRequest(); + AppointmentNdgRequest.Filter filter = new AppointmentNdgRequest.Filter(); + filter.setPartitaIva(vatNumber); + + AppointmentNdgRequest.Pagination pagination = new AppointmentNdgRequest.Pagination(); + pagination.setTargetPage(AppointmentApiConstant.TARGET_PAGE_SIZE); + pagination.setRecordsPerPage(AppointmentApiConstant.RECORD_PER_PAGE_SIZE); + + request.setFilter(filter); + request.setPagination(pagination); + return request; + } + + private static String getAppointmentVisuraRequest(CompanyEntity company, String areaCode) { + + AppointmentVisuraRequest visuraRequest = new AppointmentVisuraRequest(); + AppointmentVisuraRequest.VisuraInput input = new AppointmentVisuraRequest.VisuraInput(); + input.setPartitaIva(company.getVatNumber()); + input.setCodiceFiscale(company.getCodiceFiscale()); + input.setCodArea(areaCode); + input.setVisuraMode(AppointmentApiConstant.VISURA_MODE); + input.setVisuraProvider(AppointmentApiConstant.VISURA_PROVIDER); + input.setCodAgente(AppointmentApiConstant.COD_AGENTE); + input.setAnagraficaLegame(AppointmentApiConstant.IS_ANAGRAFICA_LEGAME); + input.setCreaAnagrafica(AppointmentApiConstant.CREA_ANAGRAFICA); + input.setFromRating(AppointmentApiConstant.IS_FROM_RATING); + input.setSalvaDocumenti(AppointmentApiConstant.SALVA_DOCUMENTI); + input.setVisuraType(AppointmentApiConstant.VISURA_TYPE); + visuraRequest.setInput(input); + return Utils.convertObjectToJson(visuraRequest); + } + + private String parseNdgFromVisuraListResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + + if (dataNode != null && dataNode.isArray() && dataNode.size() > 0) { + JsonNode firstEntry = dataNode.get(0); + JsonNode ndgClienteNode = firstEntry.get("ndgCliente"); + if (ndgClienteNode != null && ndgClienteNode.get("code") != null) { + String code = ndgClienteNode.get("code").asText(); + return normalizeNullValue(code); + } + } + log.warn("NDG not found in Visura List API response."); + return null; + } catch (Exception e) { + log.error("Failed to parse NDG from Visura List API response: {}", e.getMessage(), e); + throw new RuntimeException("Error parsing NDG from Visura List API response", e); + } + } + + public AppointmentLoginResponse parseLoginResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + + if (dataNode != null) { + AppointmentLoginResponse response = new AppointmentLoginResponse(); + response.setTokenId(dataNode.get("tokenId").asText()); + JsonNode areasNode = dataNode.get("areas"); + if (areasNode != null && areasNode.isArray() && areasNode.size() > 0) { + response.setAreaCode(areasNode.get(0).get("code").asText()); + } + response.setCompanyId(dataNode.get("companyId").asLong()); + return response; + } else { + throw new RuntimeException("Invalid JSON structure: Missing 'data' node."); + } + } catch (Exception e) { + throw new RuntimeException("Failed to parse response from loginApi for odessa: " + e.getMessage(), e); + } + } + + public AppointmentLoginResponse parseVisuraResponse(String jsonResponse) { + + try { + // Log full raw JSON for debug purposes + log.info("Raw Visura JSON Response: {}", jsonResponse); + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + + if (dataNode != null && dataNode.isObject()) { + AppointmentLoginResponse response = new AppointmentLoginResponse(); + JsonNode idVisuraNode = dataNode.get(GepafinConstant.ID_VISURA_STRING); + JsonNode ndgNode = dataNode.get(GepafinConstant.NDG_STRING); + if (idVisuraNode == null || ndgNode == null) { + log.error("Missing expected fields in 'data' node. JSON: {}", dataNode); + } + response.setIdVisura(normalizeNullValue(idVisuraNode != null ? idVisuraNode.asText() : null)); + response.setNdg(normalizeNullValue(ndgNode != null ? ndgNode.asText() : null)); + return response; + } else { + System.err.println("Invalid JSON: 'data' node is missing or not an object."); + throw new RuntimeException("Invalid JSON structure: Missing or malformed 'data' node."); + } + } catch (Exception e) { + System.err.println("Exception while parsing Visura response: " + e.getMessage()); + throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); + } + } + + public AppointmentLoginResponse parseNdgResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode dataArray = rootNode.get(GepafinConstant.DATA_STRING); + if (dataArray == null || !dataArray.isArray() || dataArray.isEmpty()) { + log.info("NDG data is empty or missing in the response."); + AppointmentLoginResponse emptyResponse = new AppointmentLoginResponse(); + emptyResponse.setNdg(null); + return emptyResponse; + } + JsonNode firstDataEntry = dataArray.get(0); + AppointmentLoginResponse response = new AppointmentLoginResponse(); + if (firstDataEntry.has(GepafinConstant.NDG_STRING)) { + response.setNdg(normalizeNullValue(firstDataEntry.get(GepafinConstant.NDG_STRING).asText())); + } + return response; + } catch (Exception e) { + log.error("Failed to parse response: {}", e.getMessage(), e); + throw new RuntimeException("Failed to parse NDG response.", e); + } + } + + private String normalizeNullValue(String value) { + + return (value == null || GepafinConstant.NULL_STRING.equalsIgnoreCase(value.trim())) ? null : value; + } + + public AppointmentCreationResponse createAppointment(Long applicationId, CreateAppointmentRequest createAppointmentRequest) { + // Validate the application + log.info("Starting appointment creation for applicationId: {}", applicationId); + ApplicationEntity application = applicationService.validateApplication(applicationId); + + AppointmentCreationResponse appointmentCreationResponse = new AppointmentCreationResponse(); + + ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + HubEntity hub = hubRepository.findByHubId(application.getHubId()); + + // Check hub UUID and enforce constraints + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Appointment cannot be created for another Hub; default is required for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_APPOINTMENT_FOR_ANOTHER_HUB)); + } + + try { + // Pre-check conditions for appointment creation + if (application.getNdg() != null && !Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS) && application.getAppointmentId() != null) { + appointmentCreationResponse.setAppointmentId(application.getAppointmentId()); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_ALREADY_CREATED)); + // return appointmentCreationResponse; + } + + if (application.getNdg() == null && Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS)) { + log.warn("NDG in progress but not available for applicationId: {}", applicationId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); + } + + // Generate authorization token and fetch template data + String authorizationToken = regenerateTokenAndSave(hub, application); + Long appointmentTemplateId = application.getCall().getAppointmentTemplateId(); + if (appointmentTemplateId == null) { + log.error("Missing appointment template ID for applicationId: {}", applicationId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_CANNOT_BE_CREATED)); + } + ResponseEntity response = appointmentApiService.getAppointmentTemplateForTemplateCreation(authorizationToken, appointmentTemplateId); + + if (response.getStatusCode() != HttpStatus.OK) { + log.error("Failed to retrieve appointment template for appointment creation. Status: {}", response.getStatusCode()); + throw new IllegalStateException("Failed to retrieve appointment template for appointment creation"); + } + + // Parse template data + String responseDataForTemplate = Utils.convertObjectToJson(response.getBody()); + AppointmentCreationRequest templateRichiestaData = parseTemplateResponseData(responseDataForTemplate); + + // Build the appointment request body + AppointmentCreationRequest appointmentCreationRequest = buildAppointmentCreationRequest(applicationId, createAppointmentRequest, appointmentTemplateId, + templateRichiestaData); + log.info("AppointmentCreationRequest : {}", appointmentCreationRequest); + String appointmentRequestBody = Utils.convertObjectToJson(appointmentCreationRequest); + + // Make API call to create the appointment + log.info("Context:{}, Authorization Token : {}, RequestBody : {}", context, authorizationToken, appointmentRequestBody); + ResponseEntity appointmentResponse = appointmentApiService.createAppointment(authorizationToken, context, appointmentRequestBody); + String appointmentId = extractAppointmentIdFromResponse(appointmentResponse); + + if (appointmentId == null) { + log.error("Failed to extract appointment ID from response for applicationId: {}", applicationId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_NOT_CREATED)); + } + // Update application with the appointment ID + application.setAppointmentId(appointmentId); + application.setStatus(ApplicationStatusTypeEnum.APPOINTMENT.getValue()); + applicationRepository.save(application); + + // Log version history + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + + appointmentCreationResponse.setAppointmentId(appointmentId); + return appointmentCreationResponse; + + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden received while retrieving template. Attempting to regenerate token and retry. Application ID: {}", applicationId); + regenerateTokenAndSave(hub, application); + return createAppointment(applicationId, createAppointmentRequest); + } + } + + private String extractAppointmentIdFromResponse(ResponseEntity appointmentResponse) { + + if (appointmentResponse.getBody() != null) { + log.info("Appointment API Response : {}", appointmentResponse.getBody()); + try { + Map responseBody = (Map) appointmentResponse.getBody(); + // 1. Try to get appointment ID from data.id + if (responseBody.containsKey(GepafinConstant.DATA_STRING)) { + Map data = (Map) 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> errors = (List>) responseBody.get(GepafinConstant.ERROR_STRING); + if (errors != null && !errors.isEmpty()) { + Map firstError = errors.get(0); + if (firstError.containsKey(GepafinConstant.CAUSE_STRING)) { + Map cause = (Map) 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; + } + + public AppointmentCreationRequest parseTemplateResponseData(String jsonResponse) { + + try { + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode richiesteClienteArray = rootNode.path(GepafinConstant.DATA_STRING).path(GepafinConstant.RICHIESTE_CLIENTE_STRING); + + // Initialize the result object + AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); + AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); + List richiestaClienteList = new ArrayList<>(); + if (!richiesteClienteArray.isArray()) { + log.warn("richiesteCliente array is missing or not an array."); + return new AppointmentCreationRequest(); + } + for (JsonNode richiestaNode : richiesteClienteArray) { + if (richiestaNode.isNull()) + continue; + + AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); + JsonNode prodottoNode = richiestaNode.path(AppointmentApiConstant.PRODOTTO); + String prodottoCode = prodottoNode.path(AppointmentApiConstant.PRODOTTO_CODE).asText(); + + richiestaCliente.setCodProdotto(prodottoCode); + richiestaCliente.setIdMotivazione(getIntValue(richiestaNode)); + richiestaCliente.setCodAbi(getTextValue(richiestaNode, AppointmentApiConstant.COD_ABI)); + richiestaCliente.setCodCab(getTextValue(richiestaNode, AppointmentApiConstant.COD_CAB)); + richiestaCliente.setIdNota(getTextValue(richiestaNode, AppointmentApiConstant.ID_NOTA)); + richiestaCliente.setImportoAgevolato(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_AGEVOLATO)); + richiestaCliente.setImportoMedioLungoTermine(getTextValue(richiestaNode, AppointmentApiConstant.IMPORTO_MEDIOLUNGO_TERMINE)); + richiestaCliente.setCodTipoProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_TIPO_PRODOTTO)); + richiestaCliente.setCodCategoriaProdotto(getTextValue(richiestaNode, AppointmentApiConstant.COD_CATEGORIA_PRODOTTO)); + richiestaCliente.setCodFormaTecnica(getTextValue(richiestaNode, AppointmentApiConstant.COD_FORMATECNICA)); + richiestaCliente.setCodOperazione(getTextValue(richiestaNode, AppointmentApiConstant.COD_OPERAZIONE)); + + richiestaClienteList.add(richiestaCliente); + } + + input.setRichiestaCliente(richiestaClienteList); + appointmentCreationRequest.setInput(input); + + return appointmentCreationRequest; + + } catch (JsonProcessingException e) { + log.error("JSON processing error: {}", e.getMessage(), e); + throw new IllegalStateException("Invalid JSON structure in template response", e); + } + } + + private String getTextValue(JsonNode node, String fieldName) { + + return node.path(fieldName).isTextual() ? node.path(fieldName).asText() : null; + } + + private int getIntValue(JsonNode node) { + + return node.path(AppointmentApiConstant.MOTIVAZIONE).path(AppointmentApiConstant.MOTIVAZIONE_ID).asInt(); + } + + public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, Long areaCode, + AppointmentCreationRequest templateRichiestaData) { + + ApplicationEntity application = applicationService.validateApplication(applicationId); + CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota(); + + AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); + AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); + + // Set Input Fields + input.setId(areaCode); + input.setNdg(application.getNdg()); + + // Populate richiestaCliente from template data + List richiestaClienteList = new ArrayList<>(); + for (AppointmentCreationRequest.RichiestaCliente templateRichiesta : templateRichiestaData.getInput().getRichiestaCliente()) { + AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente(); + BeanUtils.copyProperties(templateRichiesta, richiestaCliente); + + // Add specific `nota` + AppointmentCreationRequest.Nota requestNota = new AppointmentCreationRequest.Nota(); + requestNota.setTitolo(nota.getTitolo()); + requestNota.setTesto(nota.getTesto()); + richiestaCliente.setNota(requestNota); + richiestaCliente.setDurataMesiFinanziamento(createAppointmentRequest.getDurataMesiFinanziamento()); + richiestaCliente.setImportoBreveTermine(createAppointmentRequest.getImportoBreveTermine()); + richiestaClienteList.add(richiestaCliente); + } + + input.setRichiestaCliente(richiestaClienteList); + appointmentCreationRequest.setInput(input); + return appointmentCreationRequest; + } + + public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) { + log.info("Initiating upload to external system for documentId: {}", documentId); + // 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, 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(); + response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); + return response; + } + // Check if a thread is already running for this document upload + if (threadForDocumentMap.containsKey(documentId)) { + log.warn("Document upload already running for documentId: {}", documentId); + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS)); + } + // Start the upload process in the background + ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName(GepafinConstant.ASYNC_DOCUMENT_UPLOAD_NAME + documentId); + return thread; + }); + 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, finalApplication); + } catch (Exception e) { + log.error("Error in async document upload for documentId: {}", documentId, e); + } finally { + // Cleanup resources + ExecutorService executorToShutdown = threadForDocumentMap.remove(documentId); + if (executorToShutdown != null) { + executorToShutdown.shutdown(); + threadLocalHubId.remove(); + } + log.info("Async document upload completed for documentId: {}", documentId); + } + }); + return null; + } + + private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) { + log.info("Starting sync document upload for documentId: {}", documentId); + // Synchronous upload logic + DocumentEntity systemDoc = documentDao.validateDocument(documentId); + + Long hubId = threadLocalHubId.get(); + HubEntity hub = hubRepository.findByHubId(hubId); + + if (!hub.getUniqueUuid().equals(defaultHubUuid)) { + log.info("Document cannot be uploaded for another Hub, it is default for Gepafin."); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB)); + } + + log.info("Got Document in system: {}", systemDoc); + String oldUrl = systemDoc.getFilePath(); + String authorizationToken = getBearerToken(hub); + + try { + File localFile = downloadFileFromS3(oldUrl); + MultipartFile multipartFile = convertFileToMultipartFile(localFile); + + UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest(); + externalSystemRequest.setInput(getUploadDocumentInput(docToExternalSystemRequest)); + + String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest); + ResponseEntity uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest, + multipartFile); + + String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); + DocumentUploadResponse parsedResponse = parseDocumentUploadResponse(responseData); + + if (parsedResponse == null) { + log.error("Upload failed: parsed response is null for documentId: {}", documentId); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT)); + } + + // Save the documentAttachmentId to the database + systemDoc.setDocumentAttachmentId(parsedResponse.getDocumentAttachmentId()); + documentRepository.save(systemDoc); + + log.info("Document uploaded successfully to external system: {}", parsedResponse); + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden from external system during upload for documentId: {}. Retrying with new token...", documentId); + 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)); + } + } + + private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) { + + UploadDocToExternalSystemRequest.Input input = new UploadDocToExternalSystemRequest.Input(); + input.setIdTipoProtocollo(docToExternalSystemRequest.getInput().getIdTipoProtocollo()); + input.setIdClassificazione(docToExternalSystemRequest.getInput().getIdClassificazione()); + input.setFlagDaFirmare(docToExternalSystemRequest.getInput().getFlagDaFirmare()); + input.setDescrizione(docToExternalSystemRequest.getInput().getDescrizione()); + + UploadDocToExternalSystemRequest.Input.Attributes attributes = new UploadDocToExternalSystemRequest.Input.Attributes(); + attributes.setNdg(docToExternalSystemRequest.getInput().getAttributes().getNdg()); + attributes.setEmail(docToExternalSystemRequest.getInput().getAttributes().getEmail()); + + input.setAttributes(attributes); + return input; + } + + public static MultipartFile convertFileToMultipartFile(File file) throws IOException { + + FileInputStream input = new FileInputStream(file); + return new MockMultipartFile(file.getName(), file.getName(), MediaType.APPLICATION_OCTET_STREAM_VALUE, input); + } + + private File downloadFileFromS3(String fileUrl) throws Exception { + + String key = amazonS3Service.extractS3KeyFromUrl(fileUrl); + String fileName = extractFileName(key); + String folderPath = key.substring(0, key.lastIndexOf("/")); + File localFile = new File(GepafinConstant.TEMP_FILE_PATH + fileName); + + try (InputStream s3Stream = amazonS3Service.getFile(folderPath, key); FileOutputStream outputStream = new FileOutputStream(localFile)) { + s3Stream.transferTo(outputStream); + } + + log.info("Downloaded file from old S3 bucket: {}", key); + return localFile; + } + + private String extractFileName(String filePath) { + + String[] parts = filePath.split("/"); + return parts[parts.length - 1]; + + } + + public DocumentUploadResponse parseDocumentUploadResponse(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + + // Navigate to the "data" node + JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING); + if (dataNode != null) { + DocumentUploadResponse response = new DocumentUploadResponse(); + + // Extract "documentAttachmentId" + JsonNode documentAttachmentIdNode = dataNode.get(GepafinConstant.DOCUMENT_ATTACHMENT_ID_STRING); + if (documentAttachmentIdNode != null) { + response.setDocumentAttachmentId(documentAttachmentIdNode.asText()); + } else { + throw new RuntimeException("Invalid JSON structure: Missing 'documentAttachmentId' node."); + } + + return response; + } else { + return null; + } + } catch (Exception e) { + throw new RuntimeException("Failed to parse response: " + e.getMessage(), e); + } + } } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java index eb71f5c1..c0112618 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationAmendmentRequestEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ import java.util.List; @Entity @Table(name="application_amendment_request") @Data +@Where(clause = "is_deleted = false") public class ApplicationAmendmentRequestEntity extends BaseEntity { @Column(name = "NOTE") @@ -33,9 +35,6 @@ public class ApplicationAmendmentRequestEntity extends BaseEntity { @Column(name = "FORM_FIELDS") private String formFields; - @Column(name="IS_DELETED") - private Boolean isDeleted=false; - @Column(name = "STATUS") private String status; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java index 50d16557..e2208c77 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -9,6 +10,7 @@ import java.time.LocalDateTime; @Entity @Table(name = "APPLICATION") @Data +@Where(clause = "is_deleted = false") public class ApplicationEntity extends BaseEntity { @Column(name = "USER_ID") @@ -29,9 +31,6 @@ public class ApplicationEntity extends BaseEntity { @ManyToOne @JoinColumn(name = "CALL_ID", nullable = false) private CallEntity call; - - @Column(name="IS_DELETED") - private Boolean isDeleted; @OneToOne @JoinColumn(name = "PROTOCOL_NUMBER") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java index b8b00d3b..ae080d48 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ import java.util.List; @Data @Entity @Table(name = "application_evaluation") +@Where(clause = "is_deleted = false") public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "application_Id") @@ -39,9 +41,6 @@ public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "MOTIVATION") private String motivation; - @Column(name="IS_DELETED") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "assigned_applications_id", nullable = true) private AssignedApplicationsEntity assignedApplicationsEntity; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java index 251fb262..78a881a0 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormEntity.java @@ -2,16 +2,15 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name = "APPLICATION_EVALUATION_FORM") +@Where(clause = "is_deleted = false") public class ApplicationEvaluationFormEntity extends BaseEntity{ private Long applicationId; - @Column(name="IS_DELETED") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "EVALUATION_ID") private ApplicationEvaluationEntity applicationEvaluation; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java index 913d8831..f4324a2a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationFormFieldEntity.java @@ -2,10 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name = "APPLICATION_EVALUATION_FORM_FIELD") +@Where(clause = "is_deleted = false") public class ApplicationEvaluationFormFieldEntity extends BaseEntity { @ManyToOne @@ -18,7 +20,4 @@ public class ApplicationEvaluationFormFieldEntity extends BaseEntity { @Column(name = "FIELD_VALUE") private String fieldValue; - @Column(name="IS_DELETED") - private Boolean isDeleted; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java index a6cc56aa..cf407cda 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormEntity.java @@ -2,11 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; - +import org.hibernate.annotations.Where; @Entity @Table(name = "APPLICATION_FORM") @Data +@Where(clause = "is_deleted = false") public class ApplicationFormEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java index fbab986d..206be527 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormFieldEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -11,6 +12,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class ApplicationFormFieldEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java index 9a90af52..3f50c14b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationSignedDocumentEntity.java @@ -6,10 +6,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "application_signed_document") +@Where(clause = "is_deleted = false") public class ApplicationSignedDocumentEntity extends BaseEntity { @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java index bd26f527..0c80c7af 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/AssignedApplicationsEntity.java @@ -2,13 +2,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Data @Table(name = "assigned_applications") - +@Where(clause = "is_deleted = false") public class AssignedApplicationsEntity extends BaseEntity{ @ManyToOne @@ -27,9 +28,7 @@ public class AssignedApplicationsEntity extends BaseEntity{ @Column(name = "NOTE") private String note; - @Column(name="IS_DELETED") - private Boolean isDeleted=false; - @Column(nullable = false) private LocalDateTime assignedAt; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java index f8427719..16c7e94f 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/BaseEntity.java @@ -21,8 +21,13 @@ public class BaseEntity { @Column(name = "UPDATED_DATE") LocalDateTime updatedDate; - - @PrePersist + + + @Column(name="IS_DELETED") + private Boolean isDeleted = false; + + + @PrePersist public void setCreatedDate() { this.createdDate = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); this.updatedDate = DateTimeUtil.DateServerToUTC(LocalDateTime.now()); diff --git a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java index 2e84dd51..0985c37f 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryEntity.java @@ -7,10 +7,12 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; import jakarta.validation.constraints.Email; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "BENEFICIARY") @Data +@Where(clause = "is_deleted = false") public class BeneficiaryEntity extends BaseEntity { @Email diff --git a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java index 5bbc316d..22333d18 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/BeneficiaryPreferredCallEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -14,6 +15,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class BeneficiaryPreferredCallEntity extends BaseEntity{ @Id @@ -35,9 +37,6 @@ public class BeneficiaryPreferredCallEntity extends BaseEntity{ @Column(name = "STATUS", length = 255) private String status; - @Column(name="IS_DELETED") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "USER_WITH_COMPANY_ID") private UserWithCompanyEntity userWithCompany; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java index 25388138..dc8c05d8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CallEntity.java @@ -5,6 +5,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.AllArgsConstructor; import lombok.Builder; +import org.hibernate.annotations.Where; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -16,6 +17,7 @@ import java.time.LocalTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class CallEntity extends BaseEntity { @Column(name = "NAME", length = 255) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java index 6b14df58..465baf67 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CallTargetAudienceChecklistEntity.java @@ -2,10 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "CALL_TARGET_AUDIENCE_CHECKLIST") @Data +@Where(clause = "is_deleted = false") public class CallTargetAudienceChecklistEntity extends BaseEntity{ @ManyToOne @@ -18,9 +20,5 @@ public class CallTargetAudienceChecklistEntity extends BaseEntity{ @Column(name = "IS_VALIDATED") private Boolean isValidated; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java index 9029649e..a1c3ff45 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CommunicationEntity.java @@ -6,12 +6,14 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Table(name = "communication") @Data +@Where(clause = "is_deleted = false") public class CommunicationEntity extends BaseEntity { @Column(name = "COMMUNICATION_TITLE") @@ -20,9 +22,6 @@ public class CommunicationEntity extends BaseEntity { @Column(name = "COMMUNICATION_COMMENT") private String communicationComment; - @Column(name = "IS_DELETED") - private Boolean isDeleted = false; - @Column(name = "COMMENTED_DATE") private LocalDateTime commentedDate; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java index 15c3457c..1a08fffc 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CompanyDocumentEntity.java @@ -2,12 +2,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Table(name = "COMPANY_DOCUMENT") @Data +@Where(clause = "is_deleted = false") public class CompanyDocumentEntity extends BaseEntity { @Column(name = "FILE_NAME") @@ -25,9 +27,6 @@ public class CompanyDocumentEntity extends BaseEntity { @Column(name="COMPANY_ID") private Long companyId; - @Column(name ="IS_DELETED") - private Boolean isDeleted = false; - @Column(name="UPLOADED_BY") private Long uploadedBy; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java index 88658077..8a6c94e1 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java @@ -8,10 +8,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "COMPANY") @Data +@Where(clause = "is_deleted = false") public class CompanyEntity extends BaseEntity{ @Column(name = "COMPANY_NAME") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java index 1177bbd2..8a65ee5e 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CriteriaFormFieldEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "criteria_form_field") @Data +@Where(clause = "is_deleted = false") public class CriteriaFormFieldEntity extends BaseEntity { private Long callId; @@ -17,8 +19,5 @@ public class CriteriaFormFieldEntity extends BaseEntity { private String formFieldId; private Long evaluationCriteriaId; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java index 25a25d6d..3307e2d0 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/DocumentCategoryEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "document_category") @Data +@Where(clause = "is_deleted = false") public class DocumentCategoryEntity extends BaseEntity { @Column(name = "CATEGORY_NAME") @@ -16,7 +18,4 @@ public class DocumentCategoryEntity extends BaseEntity { @Column(name = "DESCRIPTION") private String description; - @Column(name ="IS_DELETED") - private Boolean isDeleted = false; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java index 08bfd623..8c658411 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java @@ -6,10 +6,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "DOCUMENT") @Data +@Where(clause = "is_deleted = false") public class DocumentEntity extends BaseEntity{ @Column(name = "FILE_NAME", length = 255) private String fileName; @@ -26,9 +28,6 @@ public class DocumentEntity extends BaseEntity{ @Column(name="SOURCE_ID") private Long sourceId; - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; - @Column(name="DOCUMENT_ATTACHMENT_ID") private String documentAttachmentId; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java index 1a582a21..1f8f3fe5 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EmailLogEntity.java @@ -1,12 +1,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Data @Table(name = "email_log") +@Where(clause = "is_deleted = false") public class EmailLogEntity extends BaseEntity{ @Column(name = "email_type", nullable = false, length = 255) @@ -54,9 +56,6 @@ public class EmailLogEntity extends BaseEntity{ @Column(name = "call_id") private Long callId; - @Column(name = "is_deleted") - private Boolean isDeleted; - @ManyToOne @JoinColumn(name = "user_action_id") private UserActionEntity userAction; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java index 8d1c0c69..e7954f62 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationCriteriaEntity.java @@ -5,6 +5,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; import java.math.BigDecimal; @@ -12,6 +13,7 @@ import java.math.BigDecimal; @Entity @Table(name = "EVALUATION_CRITERIA") @Data +@Where(clause = "is_deleted = false") public class EvaluationCriteriaEntity extends BaseEntity { @ManyToOne @@ -24,8 +26,5 @@ public class EvaluationCriteriaEntity extends BaseEntity { @Column(name = "SCORE", nullable = false) private BigDecimal score; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java index 9d3bd3fa..e839814a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/EvaluationFormEntity.java @@ -5,12 +5,15 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; + @Entity @Table(name = "EVALUATION_FORM") @Data @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class EvaluationFormEntity extends BaseEntity{ @@ -24,6 +27,4 @@ public class EvaluationFormEntity extends BaseEntity{ @Column(name = "CONTENT") private String content; - @Column(name="IS_DELETED") - private Boolean isDeleted; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java index 83159e3a..0f8336db 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "expiration_config") @Data +@Where(clause = "is_deleted = false") public class ExpirationConfigEntity extends BaseEntity { @Column(name="INTERVAL_DAYS") @@ -15,7 +17,4 @@ public class ExpirationConfigEntity extends BaseEntity { @Column(name="TYPE") private String type; - - @Column(name="IS_DELETED") - private Boolean isDeleted; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java index 46ebff24..5df715c9 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FaqEntity.java @@ -9,11 +9,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; - +import org.hibernate.annotations.Where; @Entity @Table(name = "FAQ") @Data +@Where(clause = "is_deleted = false") public class FaqEntity extends BaseEntity { @ManyToOne @@ -38,10 +39,7 @@ public class FaqEntity extends BaseEntity { @Column(name = "RESPONSE_DATE") private LocalDateTime responseDate; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; - + @Column(name ="COMPANY_ID") private Long companyId; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java index cdffe410..a8c99131 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FlowDataEntity.java @@ -1,6 +1,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FlowDataEntity extends BaseEntity{ @Column(name = "FORM_ID") @@ -23,4 +25,5 @@ public class FlowDataEntity extends BaseEntity{ @Column(name="CALL_ID") private Long callId; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java index 119af700..4bcab38d 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FlowEdgesEntity.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @@ -11,6 +12,7 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FlowEdgesEntity extends BaseEntity { @Column(name = "SOURCE_ID") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java index 588619a5..5861b7a8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FormEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Builder; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; @Entity @Table(name = "FORM") @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FormEntity extends BaseEntity{ @Column(name = "LABEL", length = 255) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java index b75b0c57..66e1485d 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FormFieldEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Builder; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; @Entity @Table(name = "FORM_FIELD") @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FormFieldEntity extends BaseEntity{ @Column(name = "NAME", length = 255) @@ -31,4 +33,5 @@ public class FormFieldEntity extends BaseEntity{ @Column(name = "SORT_ORDER") private Integer sortOrder; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java index 897a9dfd..93f20e18 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/FormTemplateEntity.java @@ -5,6 +5,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Builder; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Where; @Entity @Table(name = "FORM_TEMPLATE") @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Where(clause = "is_deleted = false") public class FormTemplateEntity extends BaseEntity{ @Column(name = "LABEL", length = 255) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java index 3f55abdf..21201ba5 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/GlobalConfigEntity.java @@ -15,7 +15,4 @@ public class GlobalConfigEntity extends BaseEntity{ @Column(name = "TYPE") private String type; - - @Column(name = "IS_DELETED") - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java index 603b42b4..f59e3d3c 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java @@ -4,12 +4,13 @@ import jakarta.persistence.*; import jakarta.validation.constraints.Size; import lombok.Getter; import lombok.Setter; - +import org.hibernate.annotations.Where; @Entity @Table(name="hub") @Setter @Getter +@Where(clause = "is_deleted = false") public class HubEntity extends BaseEntity{ @Column(name = "COMPANY_NAME") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java index 1bec68ce..ebed1915 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/HubUserEntity.java @@ -3,11 +3,13 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; @Entity @Table(name = "hub_user") @Getter @Setter +@Where(clause = "is_deleted = false") public class HubUserEntity extends BaseEntity{ @ManyToOne diff --git a/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java index bc3e445e..6ad5a652 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/LoginAttemptEntity.java @@ -2,12 +2,14 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; @Entity @Table(name = "LOGIN_ATTEMPT") @Data +@Where(clause = "is_deleted = false") public class LoginAttemptEntity extends BaseEntity{ @Column(name = "USERNAME") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java index 1afe87aa..21475d82 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/LookUpDataEntity.java @@ -3,10 +3,12 @@ package net.gepafin.tendermanagement.entities; import com.fasterxml.jackson.annotation.JsonValue; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "LOOKUP_DATA") @Data +@Where(clause = "is_deleted = false") public class LookUpDataEntity extends BaseEntity{ @Column(name = "TITLE", columnDefinition = "TEXT", nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java index 718a7367..9896140b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/NotificationEntity.java @@ -6,10 +6,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "NOTIFICATION") @Data +@Where(clause = "is_deleted = false") public class NotificationEntity extends BaseEntity { @Column(name = "USER_ID") @@ -24,9 +26,6 @@ public class NotificationEntity extends BaseEntity { @Column(name = "STATUS") private String status; - @Column(name = "IS_DELETED") - private Boolean isDeleted; - @Column(name = "NOTIFICATION_TYPE") private String notificationType; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java index 40d3f220..8a56b9ab 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/NotificationTypeEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name = "NOTIFICATION_TYPE") +@Where(clause = "is_deleted = false") public class NotificationTypeEntity extends BaseEntity { @Column(name = "NOTIFICATION_NAME") @@ -18,7 +20,4 @@ public class NotificationTypeEntity extends BaseEntity { @Column(name = "TITLE") private String title; - - @Column(name="IS_DELETED") - private Boolean isDeleted; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java index e807304a..cc46095a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ProtocolEntity.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; import net.gepafin.tendermanagement.config.LocalTimeAttributeConverter; +import org.hibernate.annotations.Where; import java.time.LocalDateTime; import java.time.LocalTime; @@ -10,6 +11,7 @@ import java.time.LocalTime; @Entity @Table(name = "PROTOCOL") @Data +@Where(clause = "is_deleted = false") public class ProtocolEntity extends BaseEntity { @Column(name = "PROTOCOL_NUMBER", nullable = false) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java index 386b9787..d18020bd 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/RegionEntity.java @@ -6,6 +6,7 @@ import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; import java.math.BigDecimal; @@ -13,6 +14,7 @@ import java.math.BigDecimal; @Table(name = "REGION") @Getter @Setter +@Where(clause = "is_deleted = false") public class RegionEntity extends BaseEntity { @Column(name = "REGION_NAME", length = 255, nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java index a3ff676f..88408904 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/RoleActionContextEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Data @Table(name ="role_action_context") +@Where(clause = "is_deleted = false") public class RoleActionContextEntity extends BaseEntity { @Column(name = "action_context") @@ -16,9 +18,6 @@ public class RoleActionContextEntity extends BaseEntity { @Column(name = "role_id") private Long roleId; - @Column(name="is_deleted") - private Boolean isDeleted; - @Column(name = "is_viewed") private Boolean isViewed; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java index c94b36b9..55091136 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/RoleEntity.java @@ -4,11 +4,13 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; @Entity @Table(name = "ROLE") @Getter @Setter +@Where(clause = "is_deleted = false") public class RoleEntity extends BaseEntity { @Column(name = "ROLE_NAME", length = 255, nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java index 66502bbc..b058aa70 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/S3ConfigEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "s3_path_configuration") @Data +@Where(clause = "is_deleted = false") public class S3ConfigEntity extends BaseEntity { @Column(name = "TYPE") @@ -21,5 +23,6 @@ public class S3ConfigEntity extends BaseEntity { @Column(name = "PARENT_FOLDER") private String parentFolder; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java index 05535e97..a3dcd2c8 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SamlResponseEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "SAML_RESPONSE") @Data +@Where(clause = "is_deleted = false") public class SamlResponseEntity extends BaseEntity{ @Column(name = "AUTHENTICATION_OBJECT") @@ -30,6 +32,5 @@ public class SamlResponseEntity extends BaseEntity{ @Column(name = "TOKEN") private String token; - } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java index 92d97e9f..a4c40863 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java @@ -8,10 +8,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "system_email_template") @Data +@Where(clause = "is_deleted = false") public class SystemEmailTemplatesEntity extends BaseEntity { @@ -32,9 +34,6 @@ public class SystemEmailTemplatesEntity extends BaseEntity { @Column(name = "SYSTEM") private Boolean system; - - @Column(name ="IS_DELETED", nullable = false) - private Boolean isDeleted = false; @Column(name = "email_scenario") private String emailScenario; diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java index 6f85f6ef..f15e173c 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserActionEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "user_action") +@Where(clause = "is_deleted = false") public class UserActionEntity extends BaseEntity { @Column(name = "USER_ID") @@ -40,7 +42,4 @@ public class UserActionEntity extends BaseEntity { @Column(name = "RESPONSE") private String response; - @Column(name = "IS_DELETED", nullable = false) - private Boolean isDeleted = false; - } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java index 9def5dc3..8659d16a 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserCompanyDelegationEntity.java @@ -2,10 +2,12 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "user_company_delegation") +@Where(clause = "is_deleted = false") public class UserCompanyDelegationEntity extends BaseEntity{ @Column(name="USER_ID") diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java index 86147cdf..3dbd21d4 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserEntity.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import org.hibernate.annotations.Where; import net.gepafin.tendermanagement.model.response.EmailSendResponse; import java.time.LocalDateTime; @@ -16,6 +17,7 @@ import java.util.List; @Table(name = "GEPAFIN_USER") @Getter @Setter +@Where(clause = "is_deleted = false") public class UserEntity extends BaseEntity { @Column(name = "PASSWORD", columnDefinition = "TEXT",nullable = true) diff --git a/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java index 65afbe76..9c067b32 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/UserWithCompanyEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Entity @Table(name = "USER_WITH_COMPANY") @Data +@Where(clause = "is_deleted = false") public class UserWithCompanyEntity extends BaseEntity{ @Column(name = "USER_ID") @@ -34,8 +36,5 @@ public class UserWithCompanyEntity extends BaseEntity{ @Column(name = "EMAIL") private String email; - - @Column(name = "IS_DELETED") - private Boolean isDeleted = false; - + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java index 2445fb44..cca7dfc2 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/VersionHistoryEntity.java @@ -4,10 +4,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; +import org.hibernate.annotations.Where; @Data @Entity @Table(name = "version_history") +@Where(clause = "is_deleted = false") public class VersionHistoryEntity extends BaseEntity { @Column(name = "OLD_DATA", columnDefinition = "LONGTEXT") @@ -30,7 +32,4 @@ public class VersionHistoryEntity extends BaseEntity { @Column(name = "USER_ID") private Long userId; - - @Column(name = "IS_DELETED", nullable = false) - private Boolean isDeleted = false; } diff --git a/src/main/java/net/gepafin/tendermanagement/enums/HttpMethodEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/HttpMethodEnum.java new file mode 100644 index 00000000..f71842fb --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/enums/HttpMethodEnum.java @@ -0,0 +1,18 @@ +package net.gepafin.tendermanagement.enums; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum HttpMethodEnum { + POST("POST"), PUT("PUT"), GET("GET"), DELETE("DELETE"); + + private String value; + + HttpMethodEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } +} \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 469d93ba..19feb0e2 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -1052,5 +1052,17 @@ public class Utils { headers.add(org.apache.http.HttpHeaders.USER_AGENT, "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"); return headers; } + + public static void setHttpServletRequestForThread(HttpServletRequest request,String methodType, String remoteUser, Long userActionId) { + MockHttpServletRequest mockRequest = new MockHttpServletRequest(); + mockRequest.setRequestURI(request.getRequestURI()); + mockRequest.setMethod(methodType); + mockRequest.setRemoteUser(remoteUser); // or pass it in if needed + mockRequest.setAttribute(GepafinConstant.USER_ACTION_ID, userActionId); + + ServletRequestAttributes attributes = new ServletRequestAttributes(mockRequest); + RequestContextHolder.setRequestAttributes(attributes, true); + } + } diff --git a/src/main/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml index a911a3ac..f69ef624 100644 --- a/src/main/resources/db/changelog/db.changelog-1.0.0.xml +++ b/src/main/resources/db/changelog/db.changelog-1.0.0.xml @@ -2737,6 +2737,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +