From 80e1eefd29b2aa84431f3b9c69143a169a8f6aa0 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Wed, 4 Dec 2024 21:07:06 +0530 Subject: [PATCH] Created Appointment creation flow. --- pom.xml | 4 +- .../constants/AppointmentApiConstant.java | 27 + .../constants/GepafinConstant.java | 32 + .../tendermanagement/dao/AppointmentDao.java | 884 ++++++++++++++++++ .../entities/ApplicationEntity.java | 13 + .../entities/CompanyEntity.java | 4 +- .../entities/DocumentEntity.java | 3 + .../tendermanagement/entities/HubEntity.java | 9 + .../enums/UserActionContextEnum.java | 7 +- .../request/AppointmentCreationRequest.java | 42 + .../model/request/AppointmentNdgRequest.java | 20 + .../request/AppointmentVisuraListRequest.java | 14 + .../request/AppointmentVisuraRequest.java | 28 + .../request/CreateAppointmentRequest.java | 17 + .../UploadDocToExternalSystemRequest.java | 23 + .../response/AppointmentCreationResponse.java | 8 + .../response/AppointmentLoginResponse.java | 16 + .../response/DocumentUploadResponse.java | 8 + .../model/response/NdgResponse.java | 8 + .../repositories/HubRepository.java | 10 +- .../service/AppointmentService.java | 16 + .../feignClient/AppointmentApiService.java | 44 + .../service/impl/AppointmentServiceImpl.java | 38 + .../gepafin/tendermanagement/util/Utils.java | 32 + .../web/rest/api/AppointmentApi.java | 60 ++ .../rest/api/impl/AppointmentController.java | 76 ++ src/main/resources/application-dev.properties | 8 + .../resources/application-local.properties | 9 +- .../application-production.properties | 8 + src/main/resources/application.properties | 3 + .../db/changelog/db.changelog-1.0.0.xml | 19 + src/main/resources/message_en.properties | 19 + src/main/resources/message_it.properties | 20 +- 33 files changed, 1519 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java create mode 100644 src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/AppointmentCreationRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/AppointmentNdgRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraListRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/CreateAppointmentRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/request/UploadDocToExternalSystemRequest.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/AppointmentCreationResponse.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/AppointmentLoginResponse.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/DocumentUploadResponse.java create mode 100644 src/main/java/net/gepafin/tendermanagement/model/response/NdgResponse.java create mode 100644 src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java create mode 100644 src/main/java/net/gepafin/tendermanagement/service/feignClient/AppointmentApiService.java create mode 100644 src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java create mode 100644 src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java create mode 100644 src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java diff --git a/pom.xml b/pom.xml index f3424e44..93a592ed 100644 --- a/pom.xml +++ b/pom.xml @@ -88,8 +88,8 @@ com.amazonaws - aws-java-sdk-s3 - 1.12.312 + aws-java-sdk + 1.12.563 diff --git a/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java new file mode 100644 index 00000000..15a3444e --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/constants/AppointmentApiConstant.java @@ -0,0 +1,27 @@ +package net.gepafin.tendermanagement.constants; + +public class AppointmentApiConstant { + + public static final String ODESSA_LOGIN = "/WSGatewayLogin.apiLogin"; + public static final String GET_NDG_BY_VAT_NUMBER = "/WSAnagrafica.getListaNdg"; + public static final String CREATE_VISURA = "/WSAnagrafica.createVisura"; + public static final String GET_VISURA_LIST = "/WSAnagrafica.getVisuraList"; + public static final String GET_APPOINTMENT_TEMPLATE = "/WSCrmConsulenza.getAppuntamentoTemplate?idAppuntamentoTemplate=7"; + public static final String CREATE_APPOINTMENT_FROM_TEMPLATE = "/WSCrmConsulenza.createAppointmentFromTemplate"; + public static final String UPLOAD_APOOINTMENT_DOCUMENT = "/WSDocumentDetail.createStream"; + + //get ndg number + public static final int TARGET_PAGE_SIZE = 1; + public static final int RECORD_PER_PAGE_SIZE = 10; + +//create visura request Body constant + public static final boolean CREA_ANAGRAFICA = Boolean.TRUE; + public static final boolean SALVA_DOCUMENTI = Boolean.TRUE; + public static final String VISURA_PROVIDER = "cerved"; + public static final String VISURA_TYPE = "StandardReport"; + public static final String VISURA_MODE = "visure"; + public static final String COD_AGENTE = "UtenzaAPIPortal"; + public static final boolean IS_FROM_RATING = Boolean.FALSE; + public static final boolean IS_ANAGRAFICA_LEGAME = Boolean.FALSE; + +} diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index dbf39e8f..1cb999b5 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -305,5 +305,37 @@ public class GepafinConstant { public static final String USER_ID = "userId"; public static final String LOGIN_ATTEMPT_ID = "loginAttemptId"; public static final String USER_ACTION_ID = "userActionId"; + + //Appointment + public static final String NDG_IN_PROGRESS = "IN_PROGRESS"; + public static final String NDG_AVAILABLE = "ndg.available"; + public static final String NDG_GENERATION_IS_IN_PROGRESS = "ndg.generation.in.progress"; + public static final String NDG_GENERATED = "NDG_GENERATED"; + public static final String NDG_NOT_FOUND_FOR_APPLICATION = "ndg.not.found.for.this.application.or.invalid"; + public static final String APPOINTMENT_ALREADY_CREATED = "appointment.already.created"; + public static final String EXTERNAL_DOCUMENT_UPLOAD_FAILURE_MSG = "document.not.uploaded.to.external.system.please.try.again"; + public static final String PROVIDE_VALID_APPLICATION_DOC_ID = "provide.valid.application.document.id"; + public static final String DOCUMENT_UPLOADED_SUCCESSFULLY_TO_EXTERNAL_SYSTEM = "document.uploaded.successfully.to.external.system"; + public static final String ERROR_UPLOADING_DOCUMENT = "error.in.uploading.document.check.input"; + public static final String DOCUMENT_ALREADY_UPLOADED = "document.already.uploaded"; + public static final String NDG_NOT_MATCHED_OR_NOT_FOUND = "ndg.not.found.or.not.matched"; + public static final String NO_NDG_FOR_ANOTHER_HUB = "ndg.generation.is.only.for.gepafin"; + public static final String NO_APPOINTMENT_FOR_ANOTHER_HUB = "appointment.creation.is.only.for.gepafin"; + public static final String NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB = "upload.document.is.only.for.gepafin"; + public static final String APPOINTMENT_CREATED = "appointment.created.successfully"; + public static final String DATA_STRING = "data"; + public static final String DOCUMENT_ATTACHMENT_ID_STRING = "documentAttachmentId"; + public static final String TEMP_FILE_PATH = "/tmp/"; + public static final String RICHIESTA_CLIENTE_STRING = "richiestaCliente"; + public static final String ID_STRING = "id"; + public static final String NULL_STRING = "null"; + public static final String NDG_STRING = "ndg"; + public static final String ID_VISURA_STRING = "idVisura"; + public static final String NDG_FETCH_SUCCESSFULLY = "ndg.fetch.successfully"; + public static final String AUTH_JWT_SECRET_KEY = "hTa5qe$af/4',BFs"; + public static final String JWT_ALGO_HEADER = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; + public static final String HMAC_ALGO = "HmacSHA256"; + public static final String ERROR_IN_GENERATING_NDG_TRY_AGAIN = "error.try.again"; + public static final String POLLING_THREAD_NAME = "Ndg-Polling-Thread-"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java new file mode 100644 index 00000000..dd8b970c --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/dao/AppointmentDao.java @@ -0,0 +1,884 @@ +package net.gepafin.tendermanagement.dao; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.FeignException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import net.gepafin.tendermanagement.config.Translator; +import net.gepafin.tendermanagement.constants.AppointmentApiConstant; +import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.entities.ApplicationEntity; +import net.gepafin.tendermanagement.entities.CompanyEntity; +import net.gepafin.tendermanagement.entities.DocumentEntity; +import net.gepafin.tendermanagement.entities.HubEntity; +import net.gepafin.tendermanagement.enums.DocumentSourceTypeEnum; +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.service.ApplicationService; +import net.gepafin.tendermanagement.service.CompanyService; +import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService; +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.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +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; + + @Value("${flagDaFirmare}") + private Boolean flagDaFirmare; + + @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; + + private final Map executorMap = new ConcurrentHashMap<>(); + + public NdgResponse checkNdgForAppointment(Long applicationId) { + + try { + NdgResponse ndgResponseToReturn = new NdgResponse(); + // Validate application, company, and hub + ApplicationEntity application = applicationService.validateApplication(applicationId); + if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) { + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + } + //cloned for old application data + ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + + 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)); + } + + // Check if NDG and idVisura are already present + if (isNdgAndIdVisuraPresent(application)) { + log.info("NDG already exist for applicationId: {}", applicationId); + ndgResponseToReturn.setNdg(application.getNdg()); + return ndgResponseToReturn; + } + + // Authenticate and fetch token if required + if (hub.getAppointmentAuthTokenId() == null && hub.getAreaCode() == null) { + hub = authenticateAndSaveToken(hub); + } + + String authorizationToken = getBearerToken(hub); + + // Try retrieving NDG by VAT number + AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application); + //For testing purpose Commenting it + if (isNdgValid(ndgResponse.getNdg())) { + saveNdgAndIdVisura(application, company, ndgResponse.getNdg(), null); + ndgResponseToReturn.setNdg(application.getNdg()); + return ndgResponseToReturn; + } + + return getNdgResponse(company, authorizationToken, hub, application, ndgResponseToReturn, oldApplicationData); + } catch (FeignException e) { + log.error("Error in feign client call during NDG handling: {}", e.getMessage(), e); + Utils.callException(e.status(), e); + } catch (CustomValidationException e) { + log.info("Custom validation exception: {}", e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Error during NDG handling: {}", e.getMessage(), e); + throw new RuntimeException("Error during fetching NDG."); + } + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); + } + + private NdgResponse getNdgResponse(CompanyEntity company, String authorizationToken, HubEntity hub, ApplicationEntity application, NdgResponse ndgResponseToReturn, + ApplicationEntity oldApplicationData) { + // Create Visura if NDG is not found + AppointmentLoginResponse visuraResponse = createVisura(company, authorizationToken, hub); + if (isNdgValid(visuraResponse.getNdg())) { + saveNdgAndIdVisura(application, company, visuraResponse.getNdg(), visuraResponse.getIdVisura()); + ndgResponseToReturn.setNdg(application.getNdg()); + } else if (visuraResponse.getIdVisura() != null) { + application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS); + applicationRepository.save(application); + + /** This code is responsible for adding a version history log for the "Updating ndg status in application" operation. **/ + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + + startNdgPollingTask(application, company, hub); + throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS)); + } + + return ndgResponseToReturn; + } + + private static String getBearerToken(HubEntity hub) { + + return "Bearer " + hub.getAppointmentAuthTokenId(); + } + + private void startNdgPollingTask(ApplicationEntity application, CompanyEntity company, HubEntity hub) { + //cloned for all data + ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); + + // Check if a thread is already running for this application + if (executorMap.containsKey(application.getId())) { + log.warn("Polling task already running for applicationId: {}", application.getId()); + return; + } + + // Create a dedicated thread (single-threaded executor) for this application + ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName(GepafinConstant.POLLING_THREAD_NAME + application.getId()); + return thread; + }); + executorMap.put(application.getId(), executor); + + // Submit polling task to this thread + executor.submit(() -> { + try { + log.info("Polling task started for applicationId: {} on thread: {}", application.getId(), Thread.currentThread().getName()); + long startTime = System.currentTimeMillis(); + + while (true) { + if (application.getNdg() != null) + break; + try { + String visuraListJson = getVisuraList(application.getIdVisura(), hub.getAppointmentAuthTokenId(), application, hub); + String ndg = parseNdgFromVisuraListResponse(visuraListJson); + + if (isNdgValid(ndg)) { + company.setNdg(ndg); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + application.setNdg(ndg); + applicationRepository.save(application); + companyRepository.save(company); + + log.info("NDG obtained for applicationId: {} and saved successfully.", application.getId()); + break; // Exit the loop after successful NDG retrieval + } else { + log.warn("NDG not found for applicationId: {} in Visura List API response.", application.getId()); + } + + // Check if polling time has exceeded the limit + if (System.currentTimeMillis() - startTime > TimeUnit.HOURS.toMillis(2)) { + log.warn("Polling timed out for applicationId: {}", application.getId()); + break; + } + + // Wait before the next polling attempt + Thread.sleep(TimeUnit.MINUTES.toMillis(1)); + + } catch (InterruptedException e) { + log.warn("Polling task interrupted for applicationId: {}", application.getId()); + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + log.error("Error during NDG polling for applicationId: {}", application.getId(), e); + } + } + } finally { + // Cleanup: Shut down the thread for this application + executor.shutdown(); + executorMap.remove(application.getId()); + log.info("Polling task completed and thread shut down for applicationId: {}", application.getId()); + } + }); + /** This code is responsible for adding a version history log for the "Update application ndgCode and ndgStatus" operation. **/ + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + + /** This code is responsible for adding a version history log for the "Update company ndgCode" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData(company).build()); + } + + private boolean isNdgAndIdVisuraPresent(ApplicationEntity application) { + + String ndg = application.getNdg(); + String idVisura = application.getIdVisura(); + + if (ndg != null && idVisura == null) { + return true; + } else if (ndg == null && idVisura != null) { + return false; + } else + return ndg != null; + } + + private boolean isNdgValid(String ndg) { + + return ndg != null && !ndg.isEmpty(); + } + + private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) { + + //cloned for old application and company data + ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application); + CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company); + + application.setNdg(ndg); + application.setIdVisura(idVisura); + application.setNdgStatus(GepafinConstant.NDG_GENERATED); + company.setNdg(ndg); + companyRepository.save(company); + applicationRepository.save(application); + + /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" operation. **/ + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build()); + + /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData(company).build()); + + log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg()); + } + + private String getVisuraList(String idVisura, String authorizationToken, ApplicationEntity application, HubEntity hub) { + + 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.error("403 Forbidden received while getting visuraList for Ndg code. Regenerating token..."); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub); + return getVisuraList(idVisura, newAuthorizationToken, application, hub); + } catch (Exception e) { + log.error("Failed to fetch Ndg code: {}", e.getMessage(), e); + throw new RuntimeException("Error fetching Ndg List", e); + } + } + + private HubEntity authenticateAndSaveToken(HubEntity hub) { + + HubEntity oldHubData = Utils.getClonedEntityForData(hub); + try { + //code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call + String authJwtToken = Utils.generateAuthTokenForLoginToOdessa(); + log.info("Got the auth for login to odessa {}", authJwtToken); + hub.setAuthToken(authJwtToken); + hubRepository.save(hub); + + /** This code is responsible for adding a version history log for the "Updating auth token for login api in hub" operation. **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub).build()); + + // 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); + + /** This code is responsible for adding a version history log for the "inserting token and areaCode from login odessa response for appointment flow api's" + * operation. **/ + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub).build()); + + log.info("Saved new authToken and areaCode for Hub."); + return hub; + } else { + throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again."); + } + } + // Handle non-OK response + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN)); + } catch (Exception e) { + log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e); + throw new RuntimeException("Authentication failed on Odessa. try again", e); + } + } + + private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) { + + try { + // 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 received while retrieving NDG. Regenerating token..."); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub); + return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application); + } catch (Exception e) { + log.error("Failed to retrieve NDG by VAT number: {}", e.getMessage(), e); + throw new RuntimeException("NDG retrieval failed.", e); + } + } + + private String regenerateTokenAndSave(HubEntity hub) { + + try { + hub = authenticateAndSaveToken(hub); + return "Bearer " + hub.getAppointmentAuthTokenId(); + } catch (Exception e) { + log.error("Failed to regenerate token from Odessa: {}", e.getMessage()); + throw new RuntimeException("Token regeneration failed from Odessa.", e); + } + } + + 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) { + log.error("403 Forbidden received while retrieving NDG. Regenerating token..."); + // Regenerate the token and retry + String newAuthorizationToken = regenerateTokenAndSave(hub); + 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 AppointmentNdgRequest getAppointmentNdgRequest(String 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 + 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)) { + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION)); + } + + // Generate authorization token and fetch template data + String authorizationToken = getBearerToken(hub); + ResponseEntity response = appointmentApiService.getAppointmentTemplateForTemplateCreation(authorizationToken); + + 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, hub.getAreaCode(), + templateRichiestaData); + String appointmentRequestBody = Utils.convertObjectToJson(appointmentCreationRequest); + + // Make API call to create the appointment + ResponseEntity appointmentResponse = appointmentApiService.createAppointment(authorizationToken, context, appointmentRequestBody); + String appointmentId = extractAppointmentIdFromResponse(appointmentResponse); + + if (appointmentId != null) { + // Update application with the appointment ID + application.setAppointmentId(appointmentId); + 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. Regenerating token..."); + regenerateTokenAndSave(hub); + return createAppointment(applicationId, createAppointmentRequest); + } + } + + private String extractAppointmentIdFromResponse(ResponseEntity appointmentResponse) { + + if (appointmentResponse.getBody() != null) { + Map responseBody = (Map) appointmentResponse.getBody(); + 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(); + } + } + } + return null; + } + + public AppointmentCreationRequest parseTemplateResponseData(String jsonResponse) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + JsonNode richiestaClienteArray = rootNode.path(GepafinConstant.DATA_STRING).path(GepafinConstant.RICHIESTA_CLIENTE_STRING); + + AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest(); + AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input(); + + // Map `richiestaCliente` array + List richiestaClienteList = new ArrayList<>(); + if (richiestaClienteArray.isArray()) { + for (JsonNode richiestaNode : richiestaClienteArray) { + richiestaClienteList.add(objectMapper.treeToValue(richiestaNode, AppointmentCreationRequest.RichiestaCliente.class)); + } + } + + input.setRichiestaCliente(richiestaClienteList); + appointmentCreationRequest.setInput(input); + return appointmentCreationRequest; + + } catch (Exception e) { + log.error("Error parsing template response: {}", e.getMessage(), e); + throw new IllegalStateException("Failed to parse template response", e); + } + } + + public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, String 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); + + richiestaClienteList.add(richiestaCliente); + } + + input.setRichiestaCliente(richiestaClienteList); + appointmentCreationRequest.setInput(input); + return appointmentCreationRequest; + } + + public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, Long applicationId) { + + DocumentUploadResponse response = new DocumentUploadResponse(); + DocumentEntity systemDoc = documentDao.validateDocument(documentId); + + ApplicationEntity application = getApplicationEntityForDocument(applicationId, systemDoc); + + //cloned for old document data + DocumentEntity oldDocumentEntity = Utils.getClonedEntityForData(systemDoc); + + if (!docToExternalSystemRequest.getInput().getAttributes().getNdg().equalsIgnoreCase(application.getNdg())) { + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_MATCHED_OR_NOT_FOUND)); + } + + Long hubId = application.getHubId(); + 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(); + log.info("Processing {}", oldUrl); + + String authorizationToken = getBearerToken(hub); + try { + File localFile = downloadFileFromS3(oldUrl); + MultipartFile multipartFile = convertFileToMultipartFile(localFile); + + UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest(); + + UploadDocToExternalSystemRequest.Input input = getUploadDocumentInput(docToExternalSystemRequest); + externalSystemRequest.setInput(input); + + String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest); + ResponseEntity uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest, + multipartFile); + String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody()); + DocumentUploadResponse parsedDocumentUploadResponse = parseDocumentUploadResponse(responseData); + + if (parsedDocumentUploadResponse == null) { + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT)); + } + + systemDoc.setDocumentAttachmentId(parsedDocumentUploadResponse.getDocumentAttachmentId()); + documentRepository.save(systemDoc); + + /** This code is responsible for adding a version history log for the "Update document with document attachment id" operation. **/ + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldDocumentEntity).newData(systemDoc).build()); + + log.info("Document uploaded successfully to external system : {}", parsedDocumentUploadResponse); + response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId()); + return response; + } catch (FeignException.Forbidden forbiddenException) { + log.error("403 Forbidden received while uploading document to external system. Regenerating token..."); + + regenerateTokenAndSave(hub); + return uploadDocumentToExternalSystem(systemDoc.getSourceId(), docToExternalSystemRequest, applicationId); + } catch (Exception e) { + log.error("Exception in uploading document to external system {}", e.getMessage()); + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.EXTERNAL_DOCUMENT_UPLOAD_FAILURE_MSG)); + } + } + private ApplicationEntity getApplicationEntityForDocument(Long applicationId, DocumentEntity systemDoc) { + + if (systemDoc.getDocumentAttachmentId() != null) { + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.DOCUMENT_ALREADY_UPLOADED)); + } + + ApplicationEntity application; + + if (systemDoc.getSource().equalsIgnoreCase(DocumentSourceTypeEnum.APPLICATION.getValue()) && Objects.equals(systemDoc.getSourceId(), applicationId)) { + application = applicationService.validateApplication(systemDoc.getSourceId()); + } else { + throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.PROVIDE_VALID_APPLICATION_DOC_ID)); + } + return application; + } + + private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) { + + UploadDocToExternalSystemRequest.Input input = new UploadDocToExternalSystemRequest.Input(); + input.setIdTipoProtocollo(docToExternalSystemRequest.getInput().getIdTipoProtocollo()); + input.setIdClassificazione(docToExternalSystemRequest.getInput().getIdClassificazione()); + input.setFlagDaFirmare(flagDaFirmare); + 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 = extractS3KeyFromUrl(fileUrl); + File localFile = new File(GepafinConstant.TEMP_FILE_PATH + extractFileName(key)); + + GetObjectRequest getObjectRequest = new GetObjectRequest(OLD_BUCKET, key); + + try (InputStream s3Stream = s3Client.getObject(getObjectRequest).getObjectContent(); FileOutputStream outputStream = new FileOutputStream(localFile)) { + s3Stream.transferTo(outputStream); + } + + log.info("Downloaded file from old S3 bucket: {}", key); + return localFile; + } + + private String extractS3KeyFromUrl(String url) { + + return url.replace(s3Url, ""); + } + + 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/ApplicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java index 6e65bf83..39e26f4b 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java @@ -42,4 +42,17 @@ public class ApplicationEntity extends BaseEntity { @ManyToOne @JoinColumn(name = "USER_WITH_COMPANY_ID") private UserWithCompanyEntity userWithCompany; + + @Column(name = "NDG") + private String ndg; + + @Column(name = "ID_VISURA") + private String idVisura; + + @Column(name = "NDG_STATUS") + private String ndgStatus; + + @Column(name = "APPOINTMENT_ID") + private String appointmentId; + } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java index ed50268f..e3709ef7 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java @@ -62,5 +62,7 @@ public class CompanyEntity extends BaseEntity{ @ManyToOne @JoinColumn(name = "HUB_ID") private HubEntity hub; - + + @Column(name = "NDG") + private String ndg; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java index fc15a82d..d15a65e6 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/DocumentEntity.java @@ -29,4 +29,7 @@ public class DocumentEntity extends BaseEntity{ @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/HubEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java index 2e219acf..52aa6cc2 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/HubEntity.java @@ -54,4 +54,13 @@ public class HubEntity extends BaseEntity{ @Column(name = "EMAIL_SERVICE_CONFIG") private String emailServiceConfig; + + @Column(name = "AUTH_TOKEN") + private String authToken; + + @Column(name = "APPOINTMENT_AUTH_TOKEN_ID") + private String appointmentAuthTokenId; + + @Column(name = "AREA_CODE") + private String areaCode; } diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index 7023b40b..94fabe89 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java @@ -151,7 +151,12 @@ public enum UserActionContextEnum { /** scheduler action context **/ AMENDMENT_EXPIRATION_SCHEDULER("AMENDMENT_EXPIRATION_SCHEDULER"), - EVALUATION_EXPIRATION_SCHEDULER("EVALUATION_EXPIRATION_SCHEDULER"); + EVALUATION_EXPIRATION_SCHEDULER("EVALUATION_EXPIRATION_SCHEDULER"), + + /** appointment action context **/ + CHECK_OR_CREATE_NDG_CODE("CHECK_OR_CREATE_NDG_CODE"), + CREATE_APPOINTMENT("CREATE_APPOINTMENT"), + UPLOAD_DOCUMENT_TO_EXTERNAL_SYSTEM("UPLOAD_DOCUMENT_TO_EXTERNAL_SYSTEM"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentCreationRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentCreationRequest.java new file mode 100644 index 00000000..a877a620 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentCreationRequest.java @@ -0,0 +1,42 @@ +package net.gepafin.tendermanagement.model.request; + +import lombok.Data; + +import java.util.List; + +@Data +public class AppointmentCreationRequest { + + private Input input; + + @Data + public static class Input { + private String id; + private String ndg; + private List richiestaCliente; + } + + @Data + public static class RichiestaCliente { + private String codAbi; + private String codCab; + private Integer durataMesiFinanziamento; + private Integer idMotivazione; + private String idNota; + private String importoAgevolato; + private Double importoBreveTermine; + private String importoMedioLungoTermine; + private String codTipoProdotto; + private String codCategoriaProdotto; + private String codFormaTecnica; + private String codProdotto; + private String codOperazione; + private Nota nota; + } + + @Data + public static class Nota { + private String titolo; + private String testo; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentNdgRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentNdgRequest.java new file mode 100644 index 00000000..639c8b5d --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentNdgRequest.java @@ -0,0 +1,20 @@ +package net.gepafin.tendermanagement.model.request; + +import lombok.Data; + +@Data +public class AppointmentNdgRequest { + private Filter filter; + private Pagination pagination; + + @Data + public static class Filter { + private String partitaIva; + } + + @Data + public static class Pagination { + private int targetPage; + private int recordsPerPage; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraListRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraListRequest.java new file mode 100644 index 00000000..da5b8720 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraListRequest.java @@ -0,0 +1,14 @@ +package net.gepafin.tendermanagement.model.request; + +import lombok.Data; + +@Data +public class AppointmentVisuraListRequest { + + private AppointmentVisuraListRequest.VisuraFilter filter; + + @Data + public static class VisuraFilter { + private String idVisura; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraRequest.java new file mode 100644 index 00000000..59546ee6 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/AppointmentVisuraRequest.java @@ -0,0 +1,28 @@ +package net.gepafin.tendermanagement.model.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.Setter; + +@Data +public class AppointmentVisuraRequest { + private VisuraInput input; + + @Data + public static class VisuraInput { + private String codiceFiscale; + private String partitaIva; + private boolean creaAnagrafica; + private boolean salvaDocumenti; + private String visuraProvider; + private String visuraType; + private String visuraMode; + private String codArea; + private String codAgente; + @JsonProperty("isFromRating") + private boolean isFromRating; + @JsonProperty("isAnagraficaLegame") + private boolean isAnagraficaLegame; + } +} + diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/CreateAppointmentRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/CreateAppointmentRequest.java new file mode 100644 index 00000000..667ba581 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/CreateAppointmentRequest.java @@ -0,0 +1,17 @@ +package net.gepafin.tendermanagement.model.request; + +import lombok.Data; + +@Data +public class CreateAppointmentRequest { + private Double importoBreveTermine; + private Integer durataMesiFinanziamento; + private Nota nota; + + @Data + public static class Nota { + private String titolo; + + private String testo; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/UploadDocToExternalSystemRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/UploadDocToExternalSystemRequest.java new file mode 100644 index 00000000..bd3a3097 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/request/UploadDocToExternalSystemRequest.java @@ -0,0 +1,23 @@ +package net.gepafin.tendermanagement.model.request; + +import lombok.Data; + +@Data +public class UploadDocToExternalSystemRequest { + private Input input; + + @Data + public static class Input { + private Long idTipoProtocollo; + private Long idClassificazione; + private Boolean flagDaFirmare; + private String descrizione; + private Attributes attributes; + + @Data + public static class Attributes { + private String ndg; + private String email; + } + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/AppointmentCreationResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/AppointmentCreationResponse.java new file mode 100644 index 00000000..80dd3ba0 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/AppointmentCreationResponse.java @@ -0,0 +1,8 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; + +@Data +public class AppointmentCreationResponse { + private String appointmentId; +} \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/AppointmentLoginResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/AppointmentLoginResponse.java new file mode 100644 index 00000000..1d3b9c56 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/AppointmentLoginResponse.java @@ -0,0 +1,16 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; + +@Data +public class AppointmentLoginResponse { + + private String tokenId; + private String areaCode; + private Long companyId; + private String codecFiscale; + private String vatNumber; + private String ndg; + private String message; + private String idVisura; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/DocumentUploadResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/DocumentUploadResponse.java new file mode 100644 index 00000000..956ef1cb --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/DocumentUploadResponse.java @@ -0,0 +1,8 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; + +@Data +public class DocumentUploadResponse { + private String documentAttachmentId; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/NdgResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/NdgResponse.java new file mode 100644 index 00000000..991828c5 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/NdgResponse.java @@ -0,0 +1,8 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Data; + +@Data +public class NdgResponse { + private String ndg; +} diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/HubRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/HubRepository.java index cc54295a..e371d148 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/HubRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/HubRepository.java @@ -1,15 +1,17 @@ package net.gepafin.tendermanagement.repositories; import net.gepafin.tendermanagement.entities.HubEntity; - import java.util.Optional; - import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface HubRepository extends JpaRepository { - Optional findByUniqueUuid(String hubUuid); - + Optional findByUniqueUuid(String hubUuid); + + @Query("SELECT h FROM HubEntity h WHERE h.id = :hubId") + HubEntity findByHubId(@Param("hubId") Long hubId); } diff --git a/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java b/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java new file mode 100644 index 00000000..512014f3 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/service/AppointmentService.java @@ -0,0 +1,16 @@ +package net.gepafin.tendermanagement.service; + +import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; +import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; +import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; +import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; +import net.gepafin.tendermanagement.model.response.NdgResponse; + +public interface AppointmentService { + NdgResponse checkNdgForAppointment(HttpServletRequest request, Long applicationId); + + AppointmentCreationResponse createAppointmentForApplication(HttpServletRequest request, Long applicationId, CreateAppointmentRequest createAppointmentRequest); + + DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, Long applicationId); +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/feignClient/AppointmentApiService.java b/src/main/java/net/gepafin/tendermanagement/service/feignClient/AppointmentApiService.java new file mode 100644 index 00000000..b193713b --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/service/feignClient/AppointmentApiService.java @@ -0,0 +1,44 @@ +package net.gepafin.tendermanagement.service.feignClient; + +import net.gepafin.tendermanagement.constants.AppointmentApiConstant; +import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +@FeignClient(value = "appointment-api-service", url = "${appointment.base.url}") +public interface AppointmentApiService { + + @PostMapping(value = AppointmentApiConstant.ODESSA_LOGIN, consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity loginWithOdessa(@RequestHeader("auth") String authToken, @RequestHeader("source") String source, @RequestHeader("context") String context, + @RequestHeader("user") String user, @RequestHeader("password") String password, @RequestBody(required = false) Map body); + + @PostMapping(value = AppointmentApiConstant.GET_NDG_BY_VAT_NUMBER, consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getNdgByVatNumber(@RequestBody AppointmentNdgRequest ndgRequest, @RequestHeader("Authorization") String token); + + @PostMapping(value = AppointmentApiConstant.CREATE_VISURA, consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity createVisura(@RequestBody String visuraRequest, @RequestHeader("Authorization") String token); + + @GetMapping(value = AppointmentApiConstant.GET_VISURA_LIST, consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getVisuraList(@RequestBody String visuraRequest, @RequestHeader("Authorization") String token); + + @GetMapping(value = AppointmentApiConstant.GET_APPOINTMENT_TEMPLATE, consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getAppointmentTemplateForTemplateCreation(@RequestHeader("Authorization") String token); + + @PostMapping(value = AppointmentApiConstant.CREATE_APPOINTMENT_FROM_TEMPLATE, consumes = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity createAppointment(@RequestHeader("Authorization") String token, @RequestHeader("context") String context, String appointmentCreationRequest); + + @PostMapping(value = AppointmentApiConstant.UPLOAD_APOOINTMENT_DOCUMENT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + ResponseEntity uploadDocumentToExternalSystemForAppointment(@RequestHeader("Authorization") String token, @RequestHeader("context") String context, + @RequestPart("input") String uploadDocumentRequest, @RequestPart("file") MultipartFile file); +} + + diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java new file mode 100644 index 00000000..f9c1ba85 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/AppointmentServiceImpl.java @@ -0,0 +1,38 @@ +package net.gepafin.tendermanagement.service.impl; + +import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.dao.AppointmentDao; +import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; +import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; +import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; +import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; +import net.gepafin.tendermanagement.model.response.NdgResponse; +import net.gepafin.tendermanagement.service.AppointmentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AppointmentServiceImpl implements AppointmentService { + + @Autowired + private AppointmentDao appointmentDao; + + @Override + public NdgResponse checkNdgForAppointment(HttpServletRequest request, Long applicationId) { + + return appointmentDao.checkNdgForAppointment(applicationId); + } + + @Override + public AppointmentCreationResponse createAppointmentForApplication(HttpServletRequest request, Long applicationId, CreateAppointmentRequest createAppointmentRequest) { + + return appointmentDao.createAppointment(applicationId, createAppointmentRequest); + } + + @Override + public DocumentUploadResponse uploadDocToExternalSystem(HttpServletRequest request, Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, + Long applicationId) { + + return appointmentDao.uploadDocumentToExternalSystem(documentId, docToExternalSystemRequest, applicationId); + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 7524c306..b7383161 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -51,6 +51,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.crypto.Cipher; +import javax.crypto.Mac; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; @@ -580,4 +581,35 @@ public class Utils { // Clear the RequestContextHolder after task execution RequestContextHolder.resetRequestAttributes(); } + + public static String generateAuthTokenForLoginToOdessa() { + + try { + // Your weak secret key + String secretKey = GepafinConstant.AUTH_JWT_SECRET_KEY; + + // Header + String header = GepafinConstant.JWT_ALGO_HEADER; + String encodedHeader = Base64.getUrlEncoder().withoutPadding().encodeToString(header.getBytes(StandardCharsets.UTF_8)); + + // Payload + String payload = "{\"iat\":" + (System.currentTimeMillis() / 1000) + "}"; + String encodedPayload = Base64.getUrlEncoder().withoutPadding().encodeToString(payload.getBytes(StandardCharsets.UTF_8)); + + // Combine header and payload + String dataToSign = encodedHeader + "." + encodedPayload; + + // Sign the token manually + Mac mac = Mac.getInstance(GepafinConstant.HMAC_ALGO); + SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), GepafinConstant.HMAC_ALGO); + mac.init(secretKeySpec); + byte[] signatureBytes = mac.doFinal(dataToSign.getBytes(StandardCharsets.UTF_8)); + String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes); + + // Return the final JWT + return dataToSign + "." + signature; + } catch (Exception e) { + throw new RuntimeException("Failed to generate JWT token", e); + } + } } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java new file mode 100644 index 00000000..04616f92 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/AppointmentApi.java @@ -0,0 +1,60 @@ +package net.gepafin.tendermanagement.web.rest.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; +import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; +import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; +import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; +import net.gepafin.tendermanagement.model.response.NdgResponse; +import net.gepafin.tendermanagement.model.util.Response; +import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +public interface AppointmentApi { + + @Operation(summary = "API to check or create ndg.", responses = { @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) }) + @GetMapping(value = "/application/{applicationId}/check-ndg", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity> checkNdgForAppointment(HttpServletRequest request, + @Parameter(description = "The application id", required = true) @PathVariable(value = "applicationId", required = true) Long applicationId); + + @Operation(summary = "API to create appointment.", responses = { @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) }) + @PostMapping(value = "/application/{applicationId}", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity> createAppointment(HttpServletRequest request, + @Parameter(description = "The application id", required = true) @PathVariable(value = "applicationId", required = true) Long applicationId, + @RequestBody CreateAppointmentRequest createAppointmentRequest); + + @Operation(summary = "API to Upload document to external system.", responses = { @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) }) + @PostMapping(value = "/application/{applicationId}/document/{documentId}", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity> uploadDocumentToExternalSystem(HttpServletRequest request, + @Parameter(description = "The document id", required = true) @PathVariable(value = "documentId", required = true) Long documentId, + @Parameter(description = "The application id", required = true) @PathVariable(value = "applicationId", required = true) Long applicationId, + @RequestBody UploadDocToExternalSystemRequest docToExternalSystemRequest); +} diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java new file mode 100644 index 00000000..a8986626 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/AppointmentController.java @@ -0,0 +1,76 @@ +package net.gepafin.tendermanagement.web.rest.api.impl; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import net.gepafin.tendermanagement.config.Translator; +import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.enums.UserActionContextEnum; +import net.gepafin.tendermanagement.enums.UserActionLogsEnum; +import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest; +import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest; +import net.gepafin.tendermanagement.model.request.UserActionRequest; +import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse; +import net.gepafin.tendermanagement.model.response.DocumentUploadResponse; +import net.gepafin.tendermanagement.model.response.NdgResponse; +import net.gepafin.tendermanagement.model.util.Response; +import net.gepafin.tendermanagement.service.AppointmentService; +import net.gepafin.tendermanagement.util.LoggingUtil; +import net.gepafin.tendermanagement.web.rest.api.AppointmentApi; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("${openapi.gepafin.base-path:/v1/appointment}") +@Slf4j +public class AppointmentController implements AppointmentApi { + + @Autowired + private AppointmentService appointmentService; + + @Autowired + private LoggingUtil loggingUtil; + + @Override + public ResponseEntity> checkNdgForAppointment(HttpServletRequest request, Long applicationId) { + + /** This code is responsible for creating user action logs for the "checking or creating ndg" operation. **/ + loggingUtil.logUserAction( + UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.INSERT).actionContext(UserActionContextEnum.CHECK_OR_CREATE_NDG_CODE).build()); + + NdgResponse appointmentLoginResponse = appointmentService.checkNdgForAppointment(request, applicationId); + + return ResponseEntity.status(HttpStatus.OK).body(new Response<>(appointmentLoginResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_FETCH_SUCCESSFULLY))); + } + + @Override + public ResponseEntity> createAppointment(HttpServletRequest request, Long applicationId, + CreateAppointmentRequest createAppointmentRequest) { + + /** This code is responsible for creating user action logs for the "create appointment" operation. **/ + loggingUtil.logUserAction( + UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.INSERT).actionContext(UserActionContextEnum.CREATE_APPOINTMENT).build()); + + AppointmentCreationResponse appointmentCreationResponse = appointmentService.createAppointmentForApplication(request, applicationId, createAppointmentRequest); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(new Response<>(appointmentCreationResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.APPOINTMENT_CREATED))); + } + + @Override + public ResponseEntity> uploadDocumentToExternalSystem(HttpServletRequest request, Long documentId, Long applicationId, + UploadDocToExternalSystemRequest docToExternalSystemRequest) { + + /** This code is responsible for creating user action logs for the "Upload document to external system" operation. **/ + loggingUtil.logUserAction( + UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.UPLOAD).actionContext(UserActionContextEnum.UPLOAD_DOCUMENT_TO_EXTERNAL_SYSTEM).build()); + + DocumentUploadResponse documentUploadResponse = appointmentService.uploadDocToExternalSystem(request, documentId, docToExternalSystemRequest, applicationId); + return ResponseEntity.status(HttpStatus.OK) + .body(new Response<>(documentUploadResponse, Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADED_SUCCESSFULLY_TO_EXTERNAL_SYSTEM))); + + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 8fa5c7a6..f99634bc 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -15,3 +15,11 @@ gepafin_email=rinaldo.bonazzo@bflows.net rinaldo_email=rinaldo.bonazzo@bflows.net carlo_email=test@test.test default.hub.uuid=p4lk3bcx1RStqTaIVVbXs + +#Login to Odessa, Appointment Creation, Upload document Configuration +appointment.base.url=https://demo.galileonetwork.it/gateway/rest +appointment.portal.user=UtenzaAPIPortal@621 +appointment.portal.password=u13nzaAP1P0rtal +appointment.portal.source=GEPAFINPORTAL +appointment.portal.context=GEPAFINPORTAL +flagDaFirmare=false \ No newline at end of file diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index 76aca344..37bcabad 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -13,4 +13,11 @@ default_System_Receiver_Email=test@test.test gepafin_email=test@test.test rinaldo_email=test@test.test carlo_email=test@test.test -default.hub.uuid=p4lk3bcx1RStqTaIVVbXs \ No newline at end of file +default.hub.uuid=p4lk3bcx1RStqTaIVVbXs + +appointment.base.url=https://demo.galileonetwork.it/gateway/rest +appointment.portal.user=UtenzaAPIPortal@621 +appointment.portal.password=u13nzaAP1P0rtal +appointment.portal.source=GEPAFINPORTAL +appointment.portal.context=GEPAFINPORTAL +flagDaFirmare=false \ No newline at end of file diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index b3704a3d..2007e166 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -22,3 +22,11 @@ rinaldo_email=rinaldo.bonazzo@bflows.net carlo_email=carlo.mancosu@bflows.net default.hub.uuid=p4lk3bcx1RStqTaIVVbXs # TEST DEPLOY Configuration + +#Login to Odessa, Appointment Creation, Upload document Configuration +appointment.base.url=https://demo.galileonetwork.it/gateway/rest +appointment.portal.user=UtenzaAPIPortal@621 +appointment.portal.password=u13nzaAP1P0rtal +appointment.portal.source=GEPAFINPORTAL +appointment.portal.context=GEPAFINPORTAL +flagDaFirmare=true \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index fb109ba7..60f61aec 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -65,3 +65,6 @@ default.email.signature=Gepafin S.p.a default.hub.pdf.banner=https://mementoresources.s3.amazonaws.com/gepafin/staging/template/gepafin-logo.jpg +#feign client config +spring.cloud.openfeign.client.config.default.connectTimeout=300000 +spring.cloud.openfeign.client.config.default.readTimeout=300000 \ No newline at end of file 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 9c061c37..eb77049e 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 @@ -1972,5 +1972,24 @@ path="db/dump/update_system_email_template_for_updating_amendment_mail_notification_mail_04_12_2024_1.sql"/> + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index d6aa4578..7fee26b5 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -313,3 +313,22 @@ company.id.required.for.preferred.call=Company ID is required when requesting on response.days.not.null=Response days should not be null and greater than zero. application.cannot.approved.or.rejected=Application cannot be approved and rejected because amendment is active. + +#Appointment flow messages +ndg.generated = NDG Generated. +ndg.available = NDG Available. +ndg.generation.in.progress = NDG generation is in progress. +ndg.fetch.successfully = NDG fetch successfully. +appointment.already.created = Appointment Already Created. +ndg.not.found.for.this.application.or.invalid = Ndg not found for this application or invalid. +provide.valid.application.document.id = Provide valid application document id. +document.uploaded.successfully.to.external.system = Document uploaded successfully to external system. +error.in.uploading.document.check.input = Error in uploading document check input data or try again. +document.already.uploaded = Document already uploaded. +document.not.uploaded.to.external.system.please.try.again = Document not uploaded to external system, please try again. +ndg.not.found.or.not.matched = The provided NDG does not match the application NDG, or the NDG has not been generated. +ndg.generation.is.only.for.gepafin = NDG generation is only available for GEPAFIN Hub. +appointment.creation.is.only.for.gepafin = Appointment creation is only allowed for GEPAFIN Hub. +upload.document.is.only.for.gepafin = Document cant be uploaded, this is only available for GEPAFIN Hub. +appointment.created.successfully = Appointment created successfully. +error.try.again = Service call error while performing the operation. Please try again. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 051ed432..a380f93f 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -306,4 +306,22 @@ beneficiary.call.duplicate = Una chiamata preferita con questo ID di chiamata e user.must.be.associated.with.company.to.create.application=Devi essere associato a un'azienda per poter presentare domanda per questa applicazione. company.id.required.for.preferred.call=ID azienda obbligatorio quando si richiedono solo chiamate preferite. response.days.not.null=I giorni di risposta non devono essere nulli e maggiori di zero. -application.cannot.approved.or.rejected=La domanda non può essere approvata o rifiutata perché l'emendamento è attivo. +application.cannot.approved.or.rejected=La domanda non pu� essere approvata o rifiutata perch� l'emendamento � attivo. + +#Appointment flow messages +ndg.available = NDG disponibile. +ndg.generation.in.progress = La generazione NDG � in corso. +ndg.fetch.successfully = Recupero NDG riuscito. +appointment.already.created = Appuntamento gi� creato. +ndg.not.found.for.this.application.or.invalid = NDG non trovato per questa applicazione o non valido. +provide.valid.application.document.id = Fornisci un ID documento applicativo valido. +document.uploaded.successfully.to.external.system = Documento caricato con successo nel sistema esterno. +error.in.uploading.document.check.input = Errore nel caricamento del documento. Controlla i dati inseriti o riprova. +document.already.uploaded = Documento gi� caricato. +document.not.uploaded.to.external.system.please.try.again = Documento non caricato nel sistema esterno, riprova. +ndg.not.found.or.not.matched = L'NDG fornito non corrisponde all'NDG dell'applicazione o non � stato generato. +ndg.generation.is.only.for.gepafin = La generazione dell'NDG � disponibile solo per GEPAFIN. +appointment.creation.is.only.for.gepafin = La creazione degli appuntamenti � consentita solo per GEPAFIN. +upload.document.is.only.for.gepafin = Il documento non pu� essere caricato, questa operazione � disponibile solo per il Hub GEPAFIN. +appointment.created.successfully = Appuntamento creato con successo. +error.try.again = Errore di chiamata di servizio durante l'esecuzione dell'operazione. Riprovare.