diff --git a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java index c220b176..6cdd9142 100644 --- a/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java +++ b/src/main/java/net/gepafin/tendermanagement/TendermanagementApplication.java @@ -23,7 +23,8 @@ public class TendermanagementApplication { @Override public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**").allowedOrigins("http://localhost:3000").allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD").allowCredentials(true); + registry.addMapping("/**").allowedOrigins("http://localhost:3000", "http://127.0.0.1:5500", "https://bandi-staging.memento.credit", "https://bandi.gepafin.it") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD").allowCredentials(true); } } diff --git a/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java b/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java index 14f2b1bf..850d2451 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java +++ b/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java @@ -63,7 +63,7 @@ public class SamlSuccessHandler implements AuthenticationSuccessHandler { Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) samlAuth.getPrincipal(); Map> userAttributes = principal.getAttributes(); - String token = Utils.generateSecureToken(); + String token = Utils.generateSecureSamlToken(); logger.info("SAML User Attributes: " + userAttributes); // Extracting raw SAML response diff --git a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java index 8c11eac2..462d5cc9 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java +++ b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java @@ -12,6 +12,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -97,7 +98,9 @@ public class SecurityConfig { } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth -> auth + http.csrf(AbstractHttpConfigurer::disable).headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin) + .contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'self' https://bandi-staging.memento.credit https://bandi.gepafin.it"))) + .authorizeHttpRequests(auth -> auth // Allow public access to the login endpoints .requestMatchers("/v1/user/login").permitAll() // JWT-based login .requestMatchers("/v1/user").permitAll() // User registration diff --git a/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java b/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java index 1b90a882..0212f720 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java +++ b/src/main/java/net/gepafin/tendermanagement/config/WebSocketConfig.java @@ -33,7 +33,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/wss").setAllowedOrigins("http://localhost:3000", "http://127.0.0.1:5500/", "https://bandi-staging.memento.credit/**", "https://bandi.gepafin.it/**") + registry.addEndpoint("/wss").setAllowedOrigins("http://localhost:3000", "http://127.0.0.1:5500", "https://bandi-staging.memento.credit", "https://bandi.gepafin.it") .withSockJS(); } } diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 52a2c8ec..8cf8f499 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -305,6 +305,7 @@ 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"; + public static final String RESET_PASSWORD_URL_FORMAT = "/reset-password?token=%s&email=%s"; public static final String VALID_VATNUMBER_MSG = "valid.vatnumber.message"; public static final String ATLEAST_ONE_ID_REQUIRED="atleast.one.id.required"; @@ -368,5 +369,18 @@ public class GepafinConstant { public static final String HUB="hub"; public static final String NAME="name"; + //action log response + public static final String STATUS_CODE_STRING = "statusCode"; + public static final String GET_STATUS_CODE_STRING = "status"; + public static final String MESSAGE_STRING = "message"; + public static final String AMOUNT_ACCEPTED_REQUIRED_WHILE_APPROVING_APPLICATION="amount.accepted.required"; + public static final String CALL_NAME="callName"; + public static final String NUMBER_OF_APPLICATIONS="numberOfApplications"; + public static final String STATUS="status"; + public static final String COUNT="count"; + public static final String APPLICATION_PER_CALL="applicationPerCall"; + public static final String APPLICATION_PER_STATUS="applicationPerStatus"; + + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java index d9b3ed91..2cf5d3ca 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationAmendmentRequestDao.java @@ -306,7 +306,7 @@ public class ApplicationAmendmentRequestDao { ApplicationAmendmentRequestEntity applicationAmendment = saveApplicationAmendmentRequestEntity(applicationAmendmentRequestEntity, null, VersionActionTypeEnum.INSERT); String evaluationStatusType = applicationEvaluationEntity.getStatus(); if (Boolean.FALSE.equals(evaluationStatusType.equals((ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue())))){ - applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); +// applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); //Set Status applicationEvaluationEntity.setStatus(ApplicationEvaluationStatusTypeEnum.SOCCORSO.getValue()); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index 0c25b277..b7602621 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -47,6 +47,7 @@ import jakarta.servlet.http.HttpServletRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; import java.text.MessageFormat; import java.time.LocalDateTime; import java.util.*; @@ -221,7 +222,7 @@ public class ApplicationDao { } public ApplicationEntity createApplicationEntity(UserEntity user, CallEntity call, UserWithCompanyEntity userWithCompany) { - validateDelegation(user,userWithCompany); +// validateDelegation(user,userWithCompany); ApplicationEntity entity = new ApplicationEntity(); entity.setUserId(user.getId()); entity.setCompanyId(userWithCompany.getCompanyId()); @@ -297,7 +298,12 @@ public class ApplicationDao { log.info("Deleting application with ID: {}", id); ApplicationEntity applicationEntity= validateApplication(id); - + if (Boolean.FALSE.equals(ApplicationStatusTypeEnum.DRAFT.getValue().equals(applicationEntity.getStatus()))) { + throw new CustomValidationException( + Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.APPLICATION_NOT_IN_DRAFT_STATUS) + ); + } ApplicationEntity oldApplicationDataEntity = Utils.getClonedEntityForData(applicationEntity); validator.validateUserWithCompany(request, applicationEntity.getCompanyId()); @@ -420,6 +426,10 @@ public class ApplicationDao { if(applicationEntity.getProtocol() != null) { responseBean.setProtocolNumber(applicationEntity.getProtocol().getProtocolNumber()); } + responseBean.setAmountAccepted(applicationEntity.getAmountAccepted()); + responseBean.setAmountRequested(applicationEntity.getAmountRequested()); + responseBean.setDateAccepted(applicationEntity.getDateAccepted()); + responseBean.setDateRejected(applicationEntity.getDateRejected()); return responseBean; } @@ -439,6 +449,10 @@ public class ApplicationDao { response.setCallId(entity.getCall().getId()); response.setCreatedDate(entity.getCreatedDate()); response.setUpdatedDate(entity.getUpdatedDate()); + response.setAmountAccepted(entity.getAmountAccepted()); + response.setAmountRequested(entity.getAmountRequested()); + response.setDateAccepted(entity.getDateAccepted()); + response.setDateRejected(entity.getDateRejected()); if(entity.getProtocol() != null) { response.setProtocolNumber(entity.getProtocol().getProtocolNumber()); } @@ -477,6 +491,30 @@ public class ApplicationDao { List newDocumentIds = validateFileUploadDocuments(applicationFormFieldRequestBean, formEntity); validateFileUploadDocuments(applicationFormFieldRequestBean, formEntity); VersionActionTypeEnum actionType = VersionActionTypeEnum.INSERT; + List contentResponseBeans = formDao.convertFormEntityToFormResponseBean(formEntity).getContent(); + + contentResponseBeans.stream() + .filter(content -> "numberinput".equals(content.getName())) + .map(ContentResponseBean::getSettings) + .flatMap(List::stream) + .filter(setting -> "isRequestedAmount".equals(setting.getName()) && Boolean.TRUE.equals(setting.getValue())) + .findFirst() + .ifPresent(setting -> { + Object fieldValue = applicationFormFieldRequestBean.getFieldValue(); + if(fieldValue!=null) { + if (fieldValue instanceof String) { + try { + BigDecimal amountRequested = new BigDecimal((String) fieldValue); + applicationFormEntity.getApplication().setAmountRequested(amountRequested); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Field value is not a valid number: " + fieldValue, e); + } + } else { + throw new IllegalArgumentException("Field value is not a String: " + fieldValue); + } + } + }); + ApplicationFormFieldEntity oldApplicationFormFieldData = null; @@ -808,6 +846,10 @@ public class ApplicationDao { applicationGetResponseBean.setCallId(applicationEntity.getCall().getId()); applicationGetResponseBean.setCallTitle(applicationEntity.getCall().getName()); applicationGetResponseBean.setCompanyId(applicationEntity.getCompanyId()); + applicationGetResponseBean.setAmountAccepted(applicationEntity.getAmountAccepted()); + applicationGetResponseBean.setAmountRequested(applicationEntity.getAmountRequested()); + applicationGetResponseBean.setDateAccepted(applicationEntity.getDateAccepted()); + applicationGetResponseBean.setDateRejected(applicationEntity.getDateRejected()); if(applicationEntity.getProtocol() != null) { applicationGetResponseBean.setProtocolNumber(applicationEntity.getProtocol().getProtocolNumber()); } @@ -867,7 +909,7 @@ public class ApplicationDao { ProtocolEntity protocolEntity = protocolDao.createProtocolEntity(applicationEntity, protocolNumber, userEntity.getHub().getId(),true); applicationEntity.setProtocol(protocolEntity); applicationEntity.setStatus(ApplicationStatusTypeEnum.SUBMIT.getValue()); - applicationEntity.setSubmissionDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + applicationEntity.setSubmissionDate(protocolEntity.getCreatedDate()); applicationEntity = applicationRepository.save(applicationEntity); Map placeHolders=notificationDao.sendNotificationToBeneficiary(applicationEntity,NotificationTypeEnum.APPLICATION_SUBMISSION); notificationDao.sendNotificationToSuperUser(applicationEntity,placeHolders,NotificationTypeEnum.APPLICATION_SUBMISSION); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 5992314b..7cced04a 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -24,7 +24,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import java.math.BigDecimal; +import java.time.Duration; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -580,6 +583,10 @@ public class ApplicationEvaluationDao { CompanyEntity company = companyService.validateCompany(application.getCompanyId()); response.setCompanyName(company.getCompanyName()); } + response.setAmountAccepted(application.getAmountAccepted()); + response.setAmountRequested(application.getAmountRequested()); + response.setDateAccepted(application.getDateAccepted()); + response.setDateRejected(application.getDateRejected()); } @@ -594,7 +601,8 @@ public class ApplicationEvaluationDao { Optional assignedApplications = assignedApplicationsRepository.findByIdAndIsDeletedFalse(assignedApplicationId); ApplicationEvaluationEntity oldApplicationEvaluation = null; - VersionActionTypeEnum actionType; + ApplicationEntity application = applicationService.validateApplication(assignedApplications.get().getApplication().getId()); + VersionActionTypeEnum actionType = VersionActionTypeEnum.INSERT; if (existingEntityOptional.isPresent()) { entity = existingEntityOptional.get(); oldApplicationEvaluation = Utils.getClonedEntityForData(entity); @@ -610,10 +618,15 @@ public class ApplicationEvaluationDao { entity.setIsDeleted(false); setIfUpdated(entity::getNote, entity::setNote, req.getNote()); setIfUpdated(entity::getMotivation, entity::setMotivation, req.getMotivation()); + if(Boolean.TRUE.equals(req.getApplicationStatus().equals(ApplicationStatusForEvaluation.APPROVED)) && (req.getAmountAccepted()==null || req.getAmountAccepted().compareTo(BigDecimal.ZERO) < 0)){ + throw new CustomValidationException(Status.BAD_REQUEST,Translator.toLocale(GepafinConstant.AMOUNT_ACCEPTED_REQUIRED_WHILE_APPROVING_APPLICATION)); + } + if (req.getAmountAccepted() != null && req.getAmountAccepted().compareTo(BigDecimal.ZERO) > 0) { + application.setAmountAccepted(req.getAmountAccepted()); + } actionType = VersionActionTypeEnum.UPDATE; } else { AssignedApplicationsEntity assignedApplicationsEntity = assignedApplicationsService.validateAssignedApplication(assignedApplicationId); - ApplicationEntity application = applicationService.validateApplication(assignedApplicationsEntity.getApplication().getId()); entity = convertToEntity(user, req, assignedApplicationId); actionType = VersionActionTypeEnum.INSERT; @@ -642,7 +655,6 @@ public class ApplicationEvaluationDao { loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(actionType).oldData(oldApplicationEvaluation).newData(entity).build()); if (status != null) { - ApplicationEntity application = applicationService.validateApplication(assignedApplications.get().getApplication().getId()); AssignedApplicationsEntity assignedApplicationsEntity = assignedApplications.get(); return updateApplicationEvaluationStatus(application, assignedApplicationsEntity, status); } else { @@ -1800,6 +1812,11 @@ public class ApplicationEvaluationDao { existingEntity.setClosingDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); assignedApplicationsEntity.setStatus(AssignedApplicationEnum.CLOSE.getValue()); } + if (existingEntity.getStartDate() != null && existingEntity.getClosingDate() != null) { + long activeDays = ChronoUnit.DAYS.between(existingEntity.getStartDate(), existingEntity.getClosingDate()); + activeDays -= existingEntity.getSuspendedDays() != null ? existingEntity.getSuspendedDays() : 0; + existingEntity.setActiveDays(activeDays); + } entity = applicationEvaluationRepository.save(existingEntity); assignedApplicationsRepository.save(assignedApplicationsEntity); @@ -1814,9 +1831,15 @@ public class ApplicationEvaluationDao { if (Boolean.TRUE.equals(statusType.equals((ApplicationStatusTypeEnum.APPROVED.getValue())))) { + application.setDateAccepted(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + application.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + application = applicationRepository.save(application); emailNotificationDao.sendAdmissibilityNotificationEmailForApprovedApplication(application); } if (Boolean.TRUE.equals(statusType.equals((ApplicationStatusTypeEnum.REJECTED.getValue())))) { + application.setDateRejected(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + application.setUpdatedDate(DateTimeUtil.DateServerToUTC(LocalDateTime.now())); + application = applicationRepository.save(application); emailNotificationDao.sendInadmissibilityEmailForRejectedApplication(application,existingEntity); } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java index 48ec017b..1b3a6798 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDao.java @@ -124,7 +124,6 @@ public class CompanyDao { userWithCompanyEntity.setContactEmail(companyRequest.getContactEmail()); userWithCompanyEntity.setJson(Utils.convertMapIntoJsonString(companyRequest.getVatCheckResponse()) ); UserWithCompanyEntity userWithCompany = userWithCompanyRepository.save(userWithCompanyEntity); - /** This code is responsible for adding a version history log for the "adding user with company" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(userWithCompany).build()); return userWithCompany; @@ -144,6 +143,22 @@ public class CompanyDao { entity.setNumberOfEmployees(request.getNumberOfEmployees()); entity.setAnnualRevenue(request.getAnnualRevenue()); entity.setHub(userEntity.getHub()); + Map vatCheckResponse = request.getVatCheckResponse(); + if (request.getVatCheckResponse() != null) { + if (vatCheckResponse.containsKey("dettaglio")) { + Map dettaglio = (Map) vatCheckResponse.get("dettaglio"); + if (dettaglio != null) { + if (dettaglio.containsKey("codice_ateco")) { + Object codiceAtecoObj = dettaglio.get("codice_ateco"); + String codiceAteco = (codiceAtecoObj != null) ? codiceAtecoObj.toString() : null; + + if (codiceAteco != null) { + entity.setCodiceAteco(codiceAteco); + } + } + } + } + } return entity; } @@ -165,6 +180,7 @@ public class CompanyDao { response.setAnnualRevenue(entity.getAnnualRevenue()); if(userWithCompanyEntity!=null) { response.setIsLegalRepresentant(userWithCompanyEntity.getIsLegalRepresentant()); + response.setCodiceAteco(entity.getCodiceAteco()); } response.setCreatedDate(entity.getCreatedDate()); response.setUpdatedDate(entity.getUpdatedDate()); @@ -189,15 +205,15 @@ public class CompanyDao { setIfUpdated(companyEntity::getCountry, companyEntity::setCountry, companyRequest.getCountry()); setIfUpdated(companyEntity::getNumberOfEmployees, companyEntity::setNumberOfEmployees, companyRequest.getNumberOfEmployees()); setIfUpdated(companyEntity::getAnnualRevenue, companyEntity::setAnnualRevenue, companyRequest.getAnnualRevenue()); - - if(StringUtils.isNotBlank(companyRequest.getVatNumber())) { - CompanyEntity existingCompany = companyRepository.findByVatNumberAndHubId(companyRequest.getVatNumber(), userEntity.getHub().getId()); - if(existingCompany!=null){ - throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.VATNUMBER_ALREADY_EXISTS)); - } - companyEntity.setVatNumber(companyRequest.getVatNumber()); - - } +// +// if(StringUtils.isNotBlank(companyRequest.getVatNumber())) { +// CompanyEntity existingCompany = companyRepository.findByVatNumberAndHubId(companyRequest.getVatNumber(), userEntity.getHub().getId()); +// if(existingCompany!=null){ +// throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.VATNUMBER_ALREADY_EXISTS)); +// } +// companyEntity.setVatNumber(companyRequest.getVatNumber()); +// +// } companyRepository.save(companyEntity); /** This code is responsible for adding a version history log for the "Update company" operation. **/ diff --git a/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java b/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java index 0afc8fc8..8f3a55f1 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/DashboardDao.java @@ -1,23 +1,36 @@ package net.gepafin.tendermanagement.dao; +import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.CompanyEntity; +import net.gepafin.tendermanagement.entities.UserActionEntity; import net.gepafin.tendermanagement.entities.UserEntity; import net.gepafin.tendermanagement.entities.UserWithCompanyEntity; +import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.enums.CallStatusEnum; import net.gepafin.tendermanagement.enums.RoleStatusEnum; import net.gepafin.tendermanagement.enums.UserStatusEnum; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.Widget1; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; -import net.gepafin.tendermanagement.repositories.CallRepository; -import net.gepafin.tendermanagement.repositories.CompanyRepository; -import net.gepafin.tendermanagement.repositories.UserRepository; +import net.gepafin.tendermanagement.repositories.*; import net.gepafin.tendermanagement.service.CompanyService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.List; +import java.util.stream.Collectors; @Component public class DashboardDao { @@ -33,15 +46,24 @@ public class DashboardDao { @Autowired private CompanyRepository companyRepository; - + @Autowired private CompanyService companyService; + @Autowired + private BeneficiaryPreferredCallRepository beneficiaryPreferredCallRepository; + + + @Autowired + private ApplicationEvaluationRepository applicationEvaluationRepository; + + @Autowired + private UserActionsRepository userActionsRepository; public SuperAdminWidgetResponseBean getDashboardWidget(UserEntity requestedUserEntity) { SuperAdminWidgetResponseBean widgetResponseBean = new SuperAdminWidgetResponseBean(); widgetResponseBean.setWidget1(createWidget1(requestedUserEntity)); -// List widgetBars = callRepository.findApplicationsPerCall(); -// widgetResponseBean.setWidgetBars(widgetBars); + Map widgetBars =getStatistics(requestedUserEntity); + widgetResponseBean.setWidgetBars(widgetBars); return widgetResponseBean; } @@ -54,7 +76,8 @@ public class DashboardDao { setSubmittedApplications(widget1, requestedUserEntity); setDraftApplications(widget1, requestedUserEntity); setNumberOfCompanies(widget1, requestedUserEntity); - + setAmountRequested(widget1,requestedUserEntity); + setAmountAccepted(widget1,requestedUserEntity); return widget1; } @@ -71,6 +94,20 @@ public class DashboardDao { } } + private void setAmountRequested(Widget1 widget1, UserEntity requestedUserEntity) { + BigDecimal amountRequested = applicationRepository.findTotalAmountRequestedOfApplication(requestedUserEntity.getHub().getId()); + if (amountRequested != null) { + widget1.setTotalAmountRequested(amountRequested); + } + } + + private void setAmountAccepted(Widget1 widget1, UserEntity requestedUserEntity) { + BigDecimal amountAccepted = applicationRepository.findTotalAmountAcceptedOfApplication(requestedUserEntity.getHub().getId()); + if (amountAccepted != null) { + widget1.setTotalAmountAccepted(amountAccepted); + } + } + private void setRegisteredUsers(Widget1 widget1, UserEntity requestedUserEntity) { Long activeUsers = userRepository.countByStatusAndRoleEntityRoleTypeAndHubId(UserStatusEnum.ACTIVE.getValue(), RoleStatusEnum.ROLE_BENEFICIARY.getValue(), requestedUserEntity.getHub().getId()); @@ -106,14 +143,26 @@ public class DashboardDao { } public BeneficiaryWidgetResponseBean getDashboardWidgetForBeneficiary(UserEntity userEntity, - CompanyEntity company) { + CompanyEntity company) { BeneficiaryWidgetResponseBean beneficiaryWidgetResponseBean = BeneficiaryWidgetResponseBean.builder() .numberOfApplications(0L).numberOfCalls(0L).numberOfIntegratedDocuments(0L).build(); - Long activeCalls = callRepository.countByStatusAndHubId(CallStatusEnum.PUBLISH.getValue(), userEntity.getHub().getId()); - if (activeCalls != null) { - beneficiaryWidgetResponseBean.setNumberOfCalls(activeCalls); + UserWithCompanyEntity userWithCompanyEntity = companyService.getUserWithCompany(userEntity.getId(), company.getId()); + + List preferredCalls = beneficiaryPreferredCallRepository + .findByUserIdAndUserWithCompanyIdAndIsDeletedFalse(userEntity.getId(), userWithCompanyEntity.getId()); + + if (preferredCalls != null && !preferredCalls.isEmpty()) { + List callIds = preferredCalls.stream() + .map(BeneficiaryPreferredCallEntity::getCallId) + .collect(Collectors.toList()); + List callEntities = callRepository.findByIdIn(callIds); + long activeCallsCount = callEntities.stream() + .filter(call -> CallStatusEnum.PUBLISH.getValue().equals(call.getStatus()) + && userEntity.getHub().getId().equals(call.getHub().getId())) + .count(); + + beneficiaryWidgetResponseBean.setNumberOfCalls(activeCallsCount); } - UserWithCompanyEntity userWithCompanyEntity=companyService.getUserWithCompany(userEntity.getId(),company.getId()); Long activeApplication = applicationRepository.countSubmittedApplicationsByUserId(userEntity.getId(), userWithCompanyEntity.getId()); if (activeApplication != null) { @@ -121,4 +170,99 @@ public class DashboardDao { } return beneficiaryWidgetResponseBean; } + + public Map getStatistics(UserEntity requestedUser) { + Map stats = new HashMap<>(); + + // Get applications per call + List applicationsPerCall = applicationRepository.findApplicationsPerCallWithName(requestedUser.getHub().getId()); + stats.put(GepafinConstant.APPLICATION_PER_CALL, applicationsPerCall.stream().map(result -> { + Map callData = new HashMap<>(); + callData.put(GepafinConstant.CALL_NAME, result[0]); // Call name + callData.put(GepafinConstant.NUMBER_OF_APPLICATIONS, result[1]); // Application count + return callData; + }).toList()); + + // Get applications grouped by status + List applicationsByStatus = applicationRepository.findApplicationsByStatus(requestedUser.getHub().getId()); + stats.put(GepafinConstant.APPLICATION_PER_STATUS, applicationsByStatus.stream().map(result -> { + Map statusData = new HashMap<>(); + statusData.put(GepafinConstant.STATUS, result[0]); // Application status + statusData.put(GepafinConstant.NUMBER_OF_APPLICATIONS, result[1]); // Count of applications + return statusData; + }).toList()); + + return stats; + } +// public Page getUserAction(UserEntity requestedUserEntity){ +// Pageable pageable = PageRequest.of(0, 20); // Get the first 20 records +// List roles=List.of(RoleStatusEnum.ROLE_PRE_INSTRUCTOR.getValue(),RoleStatusEnum.ROLE_GEPAFIN_OPERATOR.getValue(),RoleStatusEnum.ROLE_INSTRUCTOR_MANAGER.getValue()); +// Page userActionEntities=userActionsRepository.findActionsByRoleNamesAndHubId(roles,requestedUserEntity.getHub().getId(),pageable); +// return userActionEntities; +// } + public ApplicationWidgetResponseBean getApplicationDetails(UserEntity userEntity) { + ApplicationWidgetResponseBean applicationWidgetResponseBean = initializeResponseBean(); + + Long hubId = userEntity.getHub().getId(); + + setApplicationCounts(applicationWidgetResponseBean, hubId); + calculateEvaluationAverageTime(applicationWidgetResponseBean, hubId); + + return applicationWidgetResponseBean; + } + + private ApplicationWidgetResponseBean initializeResponseBean() { + return ApplicationWidgetResponseBean.builder() + .numberOfApplication(0L) + .numberOfAssignedApplication(0L) + .numberOfAcceptedApplication(0L) + .numberOfApplicationInAmendmentState(0L) + .numberOfDueApplication(0L) + .evaluationAverageTime(BigDecimal.ZERO) + .build(); + } + + private void setApplicationCounts(ApplicationWidgetResponseBean responseBean, Long hubId) { + Long activeApplications = applicationRepository.countApplicationsByHubId(hubId); + if (activeApplications != null) { + responseBean.setNumberOfApplication(activeApplications); + } + + Long assignedApplications = applicationRepository.countAssignedApplicationsByHubId(hubId); + if (assignedApplications != null) { + responseBean.setNumberOfAssignedApplication(assignedApplications); + } + + Long approvedApplications = applicationRepository.countApprovedApplicationsByHubId(hubId); + if (approvedApplications != null) { + responseBean.setNumberOfAcceptedApplication(approvedApplications); + } + + Long soccorsoApplications = applicationRepository.countSoccorsoApplicationsByHubId(hubId); + if (soccorsoApplications != null) { + responseBean.setNumberOfApplicationInAmendmentState(soccorsoApplications); + } + } + + private void calculateEvaluationAverageTime(ApplicationWidgetResponseBean responseBean, Long hubId) { + List applicationIds = applicationRepository.findApplicationIdsByHubId(hubId); + + if (Boolean.FALSE.equals(applicationIds.isEmpty())) { + BigDecimal averageTime = applicationEvaluationRepository.findAverageEvaluationTimeByApplicationIds(applicationIds); + responseBean.setEvaluationAverageTime(averageTime != null ? averageTime : BigDecimal.ZERO); + } + LocalDate twoDaysLater = LocalDate.now().plusDays(2); + + Long dueApplications = applicationEvaluationRepository.countDueApplicationsBetween( + applicationIds, + LocalDate.now(), + twoDaysLater + ); + + if (dueApplications != null) { + responseBean.setNumberOfDueApplication(dueApplications); + } + } + + } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/FlowFormDao.java b/src/main/java/net/gepafin/tendermanagement/dao/FlowFormDao.java index 4a42daf7..faec3489 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/FlowFormDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/FlowFormDao.java @@ -309,6 +309,10 @@ public class FlowFormDao { if(applicationEntity.getProtocol() != null) { nextOrPreviousFormResponse.setProtocolNumber(applicationEntity.getProtocol().getProtocolNumber()); } + nextOrPreviousFormResponse.setAmountAccepted(applicationEntity.getAmountAccepted()); + nextOrPreviousFormResponse.setAmountRequested(applicationEntity.getAmountRequested()); + nextOrPreviousFormResponse.setDateAccepted(applicationEntity.getDateAccepted()); + nextOrPreviousFormResponse.setDateRejected(applicationEntity.getDateRejected()); return nextOrPreviousFormResponse; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java b/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java index 5ec81347..a2e437cd 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/PdfDao.java @@ -109,76 +109,7 @@ public class PdfDao { document.add(new Paragraph(" ")); // Add line break } document.add(new Paragraph("\n")); // Add line break - Font boldSmallFont = new Font(Font.FontFamily.HELVETICA, 10, Font.BOLD,new BaseColor(105, 105, 105)); - // Adding the "Documenti Allegati" section title -// document.add(new Paragraph(" ")); -// -//// pageEvent.setTotalPages(writer.getPageNumber()); -// document.newPage(); -//// pageEvent.setTotalPages(writer.getPageNumber()); -// document.add(new Paragraph("Documenti Allegati", sectionFont)); -// document.add(new Paragraph(" ")); -// -// -//// 1. Autocertificazione possesso Requisiti -// Paragraph p1 = new Paragraph(); -// p1.add(new Chunk("1. ", boldSmallFont)); -// p1.add(new Chunk("Autocertificazione possesso Requisiti ", boldSmallFont)); -// p1.add(new Chunk("ai sensi degli artt. 46 e 47 del DPR 445/2000", smallFont)); -// document.add(p1); -// document.add(new Paragraph(" ")); -// -// -// -//// 2. Informativa Privacy relativa al trattamento dei dati personali -// Paragraph p2 = new Paragraph(); -// p2.add(new Chunk("2. ", boldSmallFont)); -// p2.add(new Chunk("Informativa Privacy relativa al trattamento dei dati personali", boldSmallFont)); -// document.add(p2); -// document.add(new Paragraph(" ")); -// -// -//// 3. Dati richiesti per la valutazione dell’adeguatezza dei flussi finanziari -// Paragraph p3 = new Paragraph(); -// p3.add(new Chunk("3. ", boldSmallFont)); -// p3.add(new Chunk("Dati richiesti per la valutazione dell’adeguatezza dei flussi finanziari prospettici come da tabella di cui all’Appendice 9", boldSmallFont)); -// document.add(p3); -// document.add(new Paragraph(" ")); -// -// -//// 4. Rilevazione Centrale dei Rischi -// Paragraph p4 = new Paragraph(); -// p4.add(new Chunk("4. ", boldSmallFont)); -// p4.add(new Chunk("Rilevazione Centrale dei Rischi riferita agli ultimi 36 mesi disponibili alla data di presentazione della Domanda", boldSmallFont)); -// document.add(p4); -// document.add(new Paragraph(" ")); -// -// -//// 5. Schema di presentazione dei dati di bilancio -// Paragraph p5 = new Paragraph(); -// p5.add(new Chunk("5. ", boldSmallFont)); -// p5.add(new Chunk("Schema di presentazione dei dati di bilancio", boldSmallFont)); -// document.add(p5); -// document.add(new Paragraph(" ")); -// -// -//// 6. Dettagli bilanci in forma abbreviata -// Paragraph p6 = new Paragraph(); -// p6.add(new Chunk("6. ", boldSmallFont)); -// p6.add(new Chunk("Dettagli bilanci in forma abbreviata", boldSmallFont)); -// document.add(p6); -// document.add(new Paragraph(" ")); -// -// -//// 7. Relazione aziendale illustrativa -// Paragraph p7 = new Paragraph(); -// p7.add(new Chunk("7. ", boldSmallFont)); -// p7.add(new Chunk("Relazione aziendale illustrativa", boldSmallFont)); -// document.add(p7); -// document.add(new Paragraph(" ")); -// -// addColoredLines(writer,document,greenColor); document.close(); @@ -488,12 +419,19 @@ public class PdfDao { .orElse(null); // If no match is found, set label to null // Find the form field in the response that matches the contentId if (name.equals("paragraph")){ - String paragraph = content.getSettings().stream() - .filter(setting -> "text".equals(setting.getName())) // Filter settings by name - .map(SettingResponseBean::getValue) // Extract the value from the matching setting - .map(Object::toString) // Convert the value to a string - .findFirst() // Get the first matching value - .orElse(null); +// String paragraph = content.getSettings().stream() +// .filter(setting -> "text".equals(setting.getName())) // Filter settings by name +// .map(SettingResponseBean::getValue) // Extract the value from the matching setting +// .map(Object::toString) // Convert the value to a string +// .findFirst() // Get the first matching value +// .orElse(null); + String paragraph = content.getSettings().stream() + .filter(setting -> "text".equals(setting.getName())) // Filter settings by name + .map(SettingResponseBean::getValue) // Extract the value from the matching setting + .map(value -> value != null ? value.toString() : " ") // Replace null with an empty string + .findFirst() // Get the first matching value + .orElse(null); // Return null if no value is found + Paragraph labelParagraph = new Paragraph(); PdfPCell labelCell = new PdfPCell(PdfUtils.htmlToPdfPCell(paragraph,labelFont)); labelCell.setBorder(Rectangle.NO_BORDER); diff --git a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java index 7e35957b..0ffa0b42 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/UserDao.java @@ -6,21 +6,15 @@ import net.gepafin.tendermanagement.config.SamlSuccessHandler; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.*; -import net.gepafin.tendermanagement.enums.RoleStatusEnum; -import net.gepafin.tendermanagement.enums.UserActionContextEnum; -import net.gepafin.tendermanagement.enums.UserActionLogsEnum; -import net.gepafin.tendermanagement.enums.UserStatusEnum; -import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; +import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.*; -import net.gepafin.tendermanagement.model.response.CompanyResponse; -import net.gepafin.tendermanagement.model.response.RoleResponseBean; -import net.gepafin.tendermanagement.model.response.UserSamlResponse; -import net.gepafin.tendermanagement.model.response.UserResponseBean; +import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.JWTToken; import net.gepafin.tendermanagement.repositories.BeneficiaryRepository; import net.gepafin.tendermanagement.repositories.UserRepository; import net.gepafin.tendermanagement.service.HubService; import net.gepafin.tendermanagement.service.RoleService; +import net.gepafin.tendermanagement.service.SystemEmailTemplatesService; import net.gepafin.tendermanagement.service.impl.AuthenticationService; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; @@ -39,6 +33,7 @@ import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static net.gepafin.tendermanagement.util.Utils.setIfUpdated; @@ -90,6 +85,18 @@ public class UserDao { @Autowired private HttpServletRequest request; + @Autowired + private SystemEmailTemplatesService systemEmailTemplatesService; + + @Autowired + private EmailLogDao emailLogDao; + + @Autowired + private EmailNotificationDao emailNotificationDao; + + @Value("${fe.base.url}") + private String feBaseUrl; + public JWTToken createUser(HttpServletRequest request, String tempToken, UserReq userReq) { if (StringUtils.isEmpty(userReq.getHubUuid())) { @@ -120,9 +127,35 @@ public class UserDao { /** This code is responsible for adding a version history log for the "Create user" operation. **/ loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).newData(userEntity).build()); + if(beneficiary == null){ + sendEmailToOnboardingUser(userEntity); + } return token; } - + public void sendEmailToOnboardingUser(UserEntity userEntity){ + SystemEmailTemplateResponse emailTemplate = systemEmailTemplatesService.retrieveTemplateByTypeAndCall( + SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum.USER_ONBOARDING, userEntity.getHub(), null); + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest(emailTemplate.getEmailScenario(), RecipientTypeEnum.USER, userEntity.getId(), userEntity.getEmail(), + userEntity.getId(), null, null, null); + String firstName = userEntity.getFirstName() != null ? userEntity.getFirstName() : ""; + String lastName = userEntity.getLastName() != null ? userEntity.getLastName() : ""; + String userName = String.join(" ", firstName, lastName).trim(); + String subject = Utils.replacePlaceholders(emailTemplate.getSubject(), Map.of( + "{{user_name}}", userName + )); + String body = Utils.replacePlaceholders(emailTemplate.getHtmlContent(), Map.of( + "{{user_name}}", userName, + "{{user_email}}", userEntity.getEmail() + )); + + emailNotificationDao.sendMail( + userEntity.getHub().getId(), + subject, + body, + List.of(userEntity.getEmail()), + emailLogRequest + ); + } private BeneficiaryEntity createBeneficiary(RoleEntity roleEntity, UserReq userReq, HubEntity hub) { BeneficiaryEntity beneficiaryEntity = null; if (RoleStatusEnum.ROLE_BENEFICIARY.getValue().equals(roleEntity.getRoleType())) { @@ -157,6 +190,8 @@ public class UserDao { userReq.setHubUuid(userEntity.getHub().getUniqueUuid()); }else { samlSuccessHandler.validateToken(tempToken, userReq.getCodiceFiscale(), userReq.getHubUuid()); + RoleEntity roleEntity = roleDao.getRoleByType(RoleStatusEnum.ROLE_BENEFICIARY); + userReq.setRoleId(roleEntity.getId()); } if (Boolean.FALSE.equals(Utils.isValidEmail(userReq.getEmail()))) { @@ -164,12 +199,10 @@ public class UserDao { Translator.toLocale(GepafinConstant.VALIDATE_EMAIL)); } log.info("Creating user with email: {}", userReq.getEmail()); - if (userRepository.existsByEmailIgnoreCaseAndHubUniqueUuid(userReq.getEmail(), userReq.getHubUuid())) { - log.error("User creation failed: Email {} already exists", userReq.getEmail()); - throw new CustomValidationException(Status.VALIDATION_ERROR, - Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); - } - if (Boolean.FALSE.equals(StringUtils.isEmpty(userReq.getCodiceFiscale())) + RoleEntity roleEntity = roleService.validateRole(userReq.getRoleId()); + validateDuplicateEmail(userReq.getEmail(), userReq.getHubUuid(), roleEntity.getRoleType()); + + if (Boolean.FALSE.equals(StringUtils.isEmpty(userReq.getCodiceFiscale())) && userRepository.existsByBeneficiaryCodiceFiscaleAndHubId(userReq.getCodiceFiscale(), hub.getId())) { log.error("User creation failed: CodiceFiscale {} already exists", userReq.getCodiceFiscale()); throw new CustomValidationException(Status.VALIDATION_ERROR, @@ -192,7 +225,30 @@ public class UserDao { } } - private void validatePassword(String password, String confirmPassword, String tempToken) { + private void validateDuplicateEmail(String email, String hubUuid, String roleType) { + String beneficiaryRoleType = RoleStatusEnum.ROLE_BENEFICIARY.getValue(); + + if (beneficiaryRoleType.equals(roleType)) { + Boolean beneficiaryExistsInHub = userRepository.existsByEmailIgnoreCaseForBeneficiaries( + email, hubUuid, beneficiaryRoleType); + + if (Boolean.TRUE.equals(beneficiaryExistsInHub)) { + throw new CustomValidationException(Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); + } + } else { + Boolean existsForNonBeneficiaries = userRepository.existsByEmailIgnoreCaseForNonBeneficiaries( + email, hubUuid, beneficiaryRoleType); + + if (Boolean.TRUE.equals(existsForNonBeneficiaries)) { + throw new CustomValidationException(Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.EMAIL_ALREADY_EXISTS)); + } + } + } + + + private void validatePassword(String password, String confirmPassword, String tempToken) { if (StringUtils.isEmpty(password) || StringUtils.isEmpty(confirmPassword)) { if(tempToken == null) { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.VALIDATE_PASSWORD)); @@ -219,7 +275,7 @@ public class UserDao { log.info("Current user details: {}", userEntity); log.info("New user details: {}", userReq); String newStatus = userReq.getStatus() != null ? userReq.getStatus().getValue() : null; - if (Boolean.FALSE.equals(userEntity.getStatus().equals(newStatus))) { + if (newStatus!=null && Boolean.FALSE.equals(userEntity.getStatus().equals(newStatus))) { userEntity.setStatus(newStatus); } setIfUpdated(userEntity::getFirstName, userEntity::setFirstName, userReq.getFirstName()); @@ -228,15 +284,19 @@ public class UserDao { setIfUpdated(userEntity::getAddress, userEntity::setAddress, userReq.getAddress()); setIfUpdated(userEntity::getPhoneNumber, userEntity::setPhoneNumber, userReq.getPhoneNumber()); setIfUpdated(userEntity::getDateOfBirth, userEntity::setDateOfBirth, userReq.getDateOfBirth()); - setIfUpdated(userEntity.getBeneficiary()::getCodiceFiscale, userEntity.getBeneficiary()::setCodiceFiscale, userReq.getCodiceFiscale()); - setIfUpdated(userEntity.getBeneficiary()::getMarketing, userEntity.getBeneficiary()::setMarketing, userReq.getMarketing()); - setIfUpdated(userEntity.getBeneficiary()::getOffers, userEntity.getBeneficiary()::setOffers, userReq.getOffers()); - setIfUpdated(userEntity.getBeneficiary()::getThirdParty, userEntity.getBeneficiary()::setThirdParty, userReq.getThirdParty()); if (userReq.getRoleId() != null) { RoleEntity roleEntity = roleDao.validateRole(userReq.getRoleId()); - setIfUpdated(userEntity::getRoleEntity, userEntity::setRoleEntity, roleEntity); + if((userEntity.getRoleEntity().getRoleType().equals(RoleStatusEnum.ROLE_INSTRUCTOR_MANAGER.getValue()) && roleEntity.getRoleType().equals(RoleStatusEnum.ROLE_PRE_INSTRUCTOR.getValue())) || (userEntity.getRoleEntity().getRoleType().equals(RoleStatusEnum.ROLE_PRE_INSTRUCTOR.getValue()) && roleEntity.getRoleType().equals(RoleStatusEnum.ROLE_INSTRUCTOR_MANAGER.getValue()))) { + setIfUpdated(userEntity::getRoleEntity, userEntity::setRoleEntity, roleEntity); + } + } + if(userEntity.getBeneficiary()!=null) { + setIfUpdated(userEntity.getBeneficiary()::getCodiceFiscale, userEntity.getBeneficiary()::setCodiceFiscale, userReq.getCodiceFiscale()); + setIfUpdated(userEntity.getBeneficiary()::getMarketing, userEntity.getBeneficiary()::setMarketing, userReq.getMarketing()); + setIfUpdated(userEntity.getBeneficiary()::getOffers, userEntity.getBeneficiary()::setOffers, userReq.getOffers()); + setIfUpdated(userEntity.getBeneficiary()::getThirdParty, userEntity.getBeneficiary()::setThirdParty, userReq.getThirdParty()); + setIfUpdated(userEntity.getBeneficiary()::getEmailPec, userEntity.getBeneficiary()::setEmailPec, userReq.getEmailPec()); } - setIfUpdated(userEntity.getBeneficiary()::getEmailPec, userEntity.getBeneficiary()::setEmailPec, userReq.getEmailPec()); userEntity = userRepository.save(userEntity); /** This code is responsible for adding a version history log for the "Update user details" operation **/ @@ -263,7 +323,7 @@ public class UserDao { userEntity.setAddress(userReq.getAddress()); userEntity.setPhoneNumber(userReq.getPhoneNumber()); userEntity.setDateOfBirth(userReq.getDateOfBirth()); - } + } return userRepository.save(userEntity); } @@ -362,25 +422,83 @@ public class UserDao { return user; } - public String initiatePasswordReset(InitiatePasswordResetReq resetReq) { - UserEntity user = userRepository - .findByEmailIgnoreCaseAndHubUniqueUuid(resetReq.getEmail(), resetReq.getHubUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); + public void initiatePasswordReset(InitiatePasswordResetReq resetReq) { + UserEntity user = userRepository.findUserExcludingRoleType( + resetReq.getEmail(), + resetReq.getHubUuid(), + RoleStatusEnum.ROLE_BENEFICIARY.getValue() + ).orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + UserEntity oldUserEntity = Utils.getClonedEntityForData(user); String token = Utils.generateSecureToken(); user.setResetPasswordToken(token); userRepository.save(user); + + /** This code is responsible for adding a version history log for the "Initiate password reset request" operation **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); + log.info("Password reset token generated for user: {}", resetReq.getEmail()); - return token; + + sendResetPasswordTokenEmail(user, token); + } + public void sendResetPasswordTokenEmail(UserEntity user, String token) { + + SystemEmailTemplateResponse emailTemplate = systemEmailTemplatesService.retrieveTemplateByTypeAndCall( + SystemEmailTemplatesEntity.SystemEmailTemplatesEntityTypeEnum.PASSWORD_RESET, user.getHub(), null); + String redirectUrl = feBaseUrl; + if (Boolean.FALSE.equals(StringUtils.isEmpty(user.getHub().getDomainName()))) { + redirectUrl = user.getHub().getDomainName(); + } + + EmailLogRequest emailLogRequest = emailLogDao.createEmailLogRequest( + emailTemplate.getEmailScenario(), + RecipientTypeEnum.USER, + user.getId(), + user.getEmail(), + user.getId(), + null, + null, + null); + redirectUrl = String.format( + user.getHub().getDomainName() + GepafinConstant.RESET_PASSWORD_URL_FORMAT, + token, + user.getEmail() + ); + String firstName = user.getFirstName() != null ? user.getFirstName() : ""; + String lastName = user.getLastName() != null ? user.getLastName() : ""; + String userName = String.join(" ", firstName, lastName).trim(); + String subject = Utils.replacePlaceholders(emailTemplate.getSubject(), Map.of( + "{{user_name}}", userName + )); + String body = Utils.replacePlaceholders(emailTemplate.getHtmlContent(), Map.of( + "{{user_name}}", userName, + "{{reset_password_link}}", redirectUrl + )); + emailNotificationDao.sendMail( + user.getHub().getId(), + subject, + body, + List.of(user.getEmail()), + emailLogRequest + ); + + log.info("Password reset token email sent to: {}", user.getEmail()); } public Boolean resetPassword(ResetPasswordReq resetPasswordReq) { - UserEntity user = userRepository - .findByEmailIgnoreCaseAndHubUniqueUuid(resetPasswordReq.getEmail(), resetPasswordReq.getHubUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); - + UserEntity user = userRepository.findUserExcludingRoleType( + resetPasswordReq.getEmail(), + resetPasswordReq.getHubUuid(), + RoleStatusEnum.ROLE_BENEFICIARY.getValue() + ).orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + + UserEntity oldUserEntity = Utils.getClonedEntityForData(user); if (!resetPasswordReq.getNewPassword().equals(resetPasswordReq.getConfirmPassword())) { log.info("User creation failed: Passwords do not match for email {}", user.getEmail()); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH)); @@ -395,25 +513,36 @@ public class UserDao { user.setPassword(passwordEncoder.encode(resetPasswordReq.getNewPassword())); user.setResetPasswordToken(null); userRepository.save(user); + + /** This code is responsible for adding a version history log for the "Reset Password " operation **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); + log.info("Password successfully reset for user: {}", resetPasswordReq.getEmail()); return true; } - public Boolean changePassword(UserEntity userEntity, ChangePasswordRequest request) { + public Boolean changePassword(UserEntity userEntity, ChangePasswordRequest changePasswordRequest) { UserEntity user = userRepository - .findByEmailIgnoreCaseAndHubUniqueUuid(request.getEmail(), userEntity.getHub().getUniqueUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); - - if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { + .findUserExcludingRoleType(changePasswordRequest.getEmail(), userEntity.getHub().getUniqueUuid(),RoleStatusEnum.ROLE_BENEFICIARY.getValue()) + .orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + UserEntity oldUserEntity = Utils.getClonedEntityForData(userEntity); + if (!passwordEncoder.matches(changePasswordRequest.getPassword(), user.getPassword())) { throw new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.CURRENT_PASSWORD_INCORRECT)); } - if (!request.getNewPassword().equals(request.getConfirmPassword())) { + if (!changePasswordRequest.getNewPassword().equals(changePasswordRequest.getConfirmPassword())) { log.info("User creation failed: Passwords do not match for email {}", user.getEmail()); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.PASSWORD_DOESNT_MATCH)); } - user.setPassword(passwordEncoder.encode(request.getNewPassword())); + user.setPassword(passwordEncoder.encode(changePasswordRequest.getNewPassword())); userRepository.save(user); + + /** This code is responsible for adding a version history log for the "Change user password" operation **/ + loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldUserEntity).newData(user).build()); + + return true; } public void logout(HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java b/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java index 85af1388..4d74fb71 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/VatCheckDao.java @@ -68,25 +68,46 @@ public class VatCheckDao { if (response.getStatusCode() == HttpStatus.OK && response.hasBody()) { log.info("Successfully checked vat number"); Map responseMap = response.getBody(); - if (responseMap != null && responseMap.containsKey("data")) { - responseBody = (Map) responseMap.get("data"); - responseBody.remove("timestamp_creation"); - responseBody.remove("timestamp_last_update"); - responseBody.remove("data_iscrizione"); - responseBody.remove("id"); - Map data = new LinkedHashMap<>(); - data.put("data", responseBody); - vatCheckResponseBean.setValid(true); - vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.VALID_VATNUMBER_MSG)); - vatCheckResponseBean.setVatCheckResponse(data); - } + processValidResponse(responseMap, vatCheckResponseBean); } } catch (FeignException ex) { - log.error("Exception occurred while checking vat number: {0}", ex); - Utils.callException(ex.status(), ex); - } + if (ex.status() == 406) { + try { + Map errorResponse = Utils.parseErrorResponse(ex.contentUTF8()); + processValidResponse(errorResponse, vatCheckResponseBean); + } catch (Exception parseEx) { + log.error("Failed to parse 406 error response: {0}", parseEx); + } + } else { + log.error("Exception occurred while checking vat number: {0}", ex); + Utils.callException(ex.status(), ex); + } + } return vatCheckResponseBean; } + public static void processValidResponse(Map responseMap, VatCheckResponseBean vatCheckResponseBean) { + if (responseMap != null && responseMap.containsKey("data")) { + Map responseBody = (Map) responseMap.get("data"); + + if (responseBody != null) { + responseBody.remove("timestamp_creation"); + responseBody.remove("timestamp_last_update"); + responseBody.remove("data_iscrizione"); + responseBody.remove("id"); + + Map data = new LinkedHashMap<>(); + data.put("data", responseBody); + + vatCheckResponseBean.setValid(true); + vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.VALID_VATNUMBER_MSG)); + vatCheckResponseBean.setVatCheckResponse(data); + } else { + vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.INVALID_VATNUMBER)); + } + } else { + vatCheckResponseBean.setMessage(Translator.toLocale(GepafinConstant.INVALID_VATNUMBER)); + } + } public VatCheckResponseBean checkVatNumber(String vatNumber) { try { diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java index 39e26f4b..c6880af4 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEntity.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.entities; import jakarta.persistence.*; import lombok.*; +import java.math.BigDecimal; import java.time.LocalDateTime; @Entity @@ -55,4 +56,16 @@ public class ApplicationEntity extends BaseEntity { @Column(name = "APPOINTMENT_ID") private String appointmentId; - } \ No newline at end of file + @Column(name="AMOUNT_REQUESTED") + private BigDecimal amountRequested; + + @Column(name="AMOUNT_ACCEPTED") + private BigDecimal amountAccepted; + + @Column(name="DATE_ACCEPTED") + private LocalDateTime dateAccepted; + + @Column(name="DATE_REJECTED") + private LocalDateTime dateRejected; + +} \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java index 15e3d3d7..6ab7b72c 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationEvaluationEntity.java @@ -65,4 +65,7 @@ public class ApplicationEvaluationEntity extends BaseEntity{ @Column(name = "CLOSING_DATE") private LocalDateTime closingDate; + @Column(name = "ACTIVE_DAYS") + private Long activeDays; + } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java index e7aa0f40..932a8758 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/CompanyEntity.java @@ -56,4 +56,7 @@ public class CompanyEntity extends BaseEntity{ @Column(name = "NDG") private String ndg; + + @Column(name = "CODICE_ATECO") + private String codiceAteco; } diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java new file mode 100644 index 00000000..83159e3a --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/ExpirationConfigEntity.java @@ -0,0 +1,21 @@ +package net.gepafin.tendermanagement.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Data; + +@Entity +@Table(name = "expiration_config") +@Data +public class ExpirationConfigEntity extends BaseEntity { + + @Column(name="INTERVAL_DAYS") + private Long intervalDays; + + @Column(name="TYPE") + private String type; + + @Column(name="IS_DELETED") + private Boolean isDeleted; +} diff --git a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java index f7974eca..dac3a432 100644 --- a/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java +++ b/src/main/java/net/gepafin/tendermanagement/entities/SystemEmailTemplatesEntity.java @@ -47,6 +47,8 @@ public class SystemEmailTemplatesEntity extends BaseEntity { INADMISSIBILITY_NOTIFICATION_DUE_TO_FAILURE("INADMISSIBILITY_NOTIFICATION_DUE_TO_FAILURE"), ADMISSIBILITY_NOTIFICATION("ADMISSIBILITY_NOTIFICATION"), AMENDMENT_REMINDER("AMENDMENT_REMINDER"), + USER_ONBOARDING("USER_ONBOARDING"), + PASSWORD_RESET("PASSWORD_RESET"), INADMISSIBILITY_TEMPLATE("INADMISSIBILITY_NOTIFICATION"); private String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java index 493f987c..415f1dd2 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/EmailScenarioTypeEnum.java @@ -9,6 +9,8 @@ public enum EmailScenarioTypeEnum { APPLICATION_AMENDMENT_EXPIRED("APPLICATION_AMENDMENT_EXPIRED"), APPLICATION_AMENDMENT_REMINDER("APPLICATION_AMENDMENT_REMINDER"), APPLICATION_APPROVED("APPLICATION_APPROVED"), + USER_CREATION("USER_CREATION"), + PASSWORD_RESET_REQUEST("PASSWORD_RESET_REQUEST"), APPLICATION_REJECTED("APPLICATION_REJECTED"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/ExpirationTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/ExpirationTypeEnum.java new file mode 100644 index 00000000..2aaa1ae2 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/enums/ExpirationTypeEnum.java @@ -0,0 +1,20 @@ +package net.gepafin.tendermanagement.enums; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum ExpirationTypeEnum { + + AMENDMENT("AMENDMENT"), + EVALUATION("EVALUATION"); + + private String value; + + ExpirationTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java index 54a13768..96d89e91 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/NotificationTypeEnum.java @@ -11,7 +11,9 @@ public enum NotificationTypeEnum { AMENDMENT_CLOSED("AMENDMENT_CLOSED"), NDG_GENERATION("NDG_GENERATION"), EVALUATION_CREATION("EVALUATION_CREATION"), - EVALUATION_EXPIRED("EVALUATION_EXPIRED"); + EVALUATION_EXPIRED("EVALUATION_EXPIRED"), + AMENDMENT_EXPIRATION_REMINDER("AMENDMENT_EXPIRATION_REMINDER"), + EVALUATION_EXPIRATION_REMINDER("EVALUATION_EXPIRATION_REMINDER"); private final String value; diff --git a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java index 2408cb9c..0a460b99 100644 --- a/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java +++ b/src/main/java/net/gepafin/tendermanagement/enums/UserActionContextEnum.java @@ -24,7 +24,9 @@ public enum UserActionContextEnum { VALIDATE_EXISTING_USER_WITH_SPID_TOKEN("VALIDATE_EXISTING_USER_WITH_SPID_TOKEN"), GET_VALID_USER_DETAILS("GET_VALID_USER_DETAILS"), GET_ALL_USERS_BY_ROLE("GET_ALL_USERS_BY_ROLE"), - + CHANGE_USER_PASSWORD("CHANGE_USER_PASSWORD"), + INITIATE_PASSWORD_RESET_REQUEST("INITIATE_PASSWORD_RESET_REQUEST"), + RESET_USER_PASSWORD("RESET_USER_PASSWORD"), /** application action context **/ GET_APPLICATION("GET_APPLICATION"), CREATE_UPDATE_APPLICATION_FORM("CREATE_UPDATE_APPLICATION_FORM"), @@ -134,6 +136,7 @@ public enum UserActionContextEnum { /** Dashboard action context **/ GET_DASHBOARD_WIDGET_FOR_SUPER_ADMIN("GET_DASHBOARD_WIDGET_FOR_SUPER_ADMIN"), GET_DASHBOARD_WIDGET_FOR_BENEFICIARY("GET_DASHBOARD_WIDGET_FOR_BENEFICIARY"), + GET_APPLICATION_DETAILS("GET_APPLICATION_DETAILS"), /** Evaluation criteria action context **/ GET_EVALUATION_CRITERIA("GET_EVALUATION_CRITERIA"), diff --git a/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationEvaluationRequest.java b/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationEvaluationRequest.java index 6616c288..bd63054c 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationEvaluationRequest.java +++ b/src/main/java/net/gepafin/tendermanagement/model/request/ApplicationEvaluationRequest.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.model.request; import lombok.Data; import net.gepafin.tendermanagement.enums.ApplicationStatusForEvaluation; +import java.math.BigDecimal; import java.util.List; @Data public class ApplicationEvaluationRequest { @@ -15,4 +16,5 @@ public class ApplicationEvaluationRequest { private String note; private ApplicationStatusForEvaluation applicationStatus; private String motivation; + private BigDecimal amountAccepted; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java index 7da6d25f..c2fcd6cb 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java @@ -4,6 +4,7 @@ import lombok.Data; import net.gepafin.tendermanagement.enums.ApplicationEvaluationStatusTypeEnum; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -38,4 +39,8 @@ public class ApplicationEvaluationResponse { private LocalDateTime assignedAt; private String ndg; private String appointmentId; + private BigDecimal amountRequested; + private BigDecimal amountAccepted; + private LocalDateTime dateAccepted; + private LocalDateTime dateRejected; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationGetResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationGetResponseBean.java index aa686023..77048d15 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationGetResponseBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationGetResponseBean.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.model.response; import lombok.Data; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -26,6 +27,15 @@ public class ApplicationGetResponseBean { private Long protocolNumber; + private BigDecimal amountRequested; + + private BigDecimal amountAccepted; + + private LocalDateTime dateAccepted; + + private LocalDateTime dateRejected; + + private List form; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponse.java index 24ad5a34..1aae69e2 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponse.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.model.response; import lombok.Data; import net.gepafin.tendermanagement.model.response.ApplicationFormFieldResponseBean; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -37,4 +38,12 @@ public class ApplicationResponse{ private String assignedUserName; + private BigDecimal amountRequested; + + private BigDecimal amountAccepted; + + private LocalDateTime dateAccepted; + + private LocalDateTime dateRejected; + } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponseBean.java index 6b99961f..3c9016bd 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponseBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationResponseBean.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.model.response; import lombok.Data; import net.gepafin.tendermanagement.model.BaseBean; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -19,6 +20,14 @@ public class ApplicationResponseBean extends BaseBean { private Long protocolNumber; + private BigDecimal amountRequested; + + private BigDecimal amountAccepted; + + private LocalDateTime dateAccepted; + + private LocalDateTime dateRejected; + private List formFields; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java new file mode 100644 index 00000000..c119121b --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationWidgetResponseBean.java @@ -0,0 +1,23 @@ +package net.gepafin.tendermanagement.model.response; + +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; + +@Builder +@Data +public class ApplicationWidgetResponseBean { + + private Long numberOfApplication; + + private Long numberOfAssignedApplication; + + private Long numberOfAcceptedApplication; + + private Long numberOfApplicationInAmendmentState; + + private Long numberOfDueApplication; + + private BigDecimal evaluationAverageTime; +} diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/CompanyResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/CompanyResponse.java index e54b040f..ddd62f38 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/CompanyResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/CompanyResponse.java @@ -24,4 +24,5 @@ public class CompanyResponse extends BaseBean{ private Boolean isLegalRepresentant; private String contactName; private String contactEmail; + private String codiceAteco; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/NextOrPreviousFormResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/NextOrPreviousFormResponse.java index 8925e7f6..bea417a1 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/NextOrPreviousFormResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/NextOrPreviousFormResponse.java @@ -3,6 +3,9 @@ package net.gepafin.tendermanagement.model.response; import lombok.Data; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; +import java.math.BigDecimal; +import java.time.LocalDateTime; + @Data public class NextOrPreviousFormResponse { @@ -25,6 +28,14 @@ public class NextOrPreviousFormResponse { private Long protocolNumber; private ApplicationStatusTypeEnum applicationStatus; + + private BigDecimal amountRequested; + + private BigDecimal amountAccepted; + + private LocalDateTime dateAccepted; + + private LocalDateTime dateRejected; private FormApplicationResponse applicationFormResponse; diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/SuperAdminWidgetResponseBean.java b/src/main/java/net/gepafin/tendermanagement/model/response/SuperAdminWidgetResponseBean.java index 6ed30e56..02744ace 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/SuperAdminWidgetResponseBean.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/SuperAdminWidgetResponseBean.java @@ -1,14 +1,17 @@ package net.gepafin.tendermanagement.model.response; import lombok.Data; +import net.gepafin.tendermanagement.entities.UserActionEntity; +import org.springframework.data.domain.Page; import java.util.List; +import java.util.Map; @Data public class SuperAdminWidgetResponseBean { private Widget1 widget1; -// private List widgetBars; + private Map widgetBars; } diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/Widget1.java b/src/main/java/net/gepafin/tendermanagement/model/response/Widget1.java index 75c68758..a6b1ad88 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/Widget1.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/Widget1.java @@ -23,4 +23,8 @@ public class Widget1 { private BigDecimal totalActiveFinancing; + private BigDecimal totalAmountRequested; + + private BigDecimal totalAmountAccepted; + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java index 68133efb..3c89993c 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationAmendmentRequestRepository.java @@ -74,4 +74,10 @@ public interface ApplicationAmendmentRequestRepository extends JpaRepository findEvaluationsWithoutActiveAmendmentsByIds(@Param("applicationEvaluationIds") Set applicationEvaluationIds); + @Query("SELECT a FROM ApplicationAmendmentRequestEntity a " + + "WHERE a.isDeleted = false " + + "AND a.status NOT IN ('CLOSE', 'EXPIRED') " + + "AND a.endDate BETWEEN :startTime AND :endTime") + List findExpiringBetween(LocalDateTime startTime, LocalDateTime endTime); + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java index f9a7b0ac..cf0b9c66 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java @@ -1,12 +1,15 @@ package net.gepafin.tendermanagement.repositories; import net.gepafin.tendermanagement.entities.ApplicationEntity; +import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity; import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; 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; +import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -31,4 +34,33 @@ public interface ApplicationEvaluationRepository extends JpaRepository findAllByIsDeletedFalseAndEndDateBefore(@Param("currentDate") LocalDateTime currentDate); + + @Query("SELECT a FROM ApplicationEvaluationEntity a " + + "WHERE a.isDeleted = false " + + "AND a.status NOT IN ('CLOSE', 'EXPIRED') " + + "AND a.endDate BETWEEN :startTime AND :endTime") + List findExpiringBetween(LocalDateTime startTime, LocalDateTime endTime); +// @Query("SELECT AVG(DATEDIFF(DAY, e.startDate, e.endDate)) FROM ApplicationEvaluationEntity e WHERE e.applicationId IN :applicationIds AND e.startDate IS NOT NULL AND e.endDate IS NOT NULL AND e.isDeleted = false ") + @Query(""" + SELECT AVG(e.activeDays) + FROM ApplicationEvaluationEntity e + WHERE e.applicationId IN :applicationIds + AND e.activeDays IS NOT NULL + AND e.isDeleted = false + """) + BigDecimal findAverageEvaluationTimeByApplicationIds(@Param("applicationIds") List applicationIds); + @Query(""" + SELECT COUNT(e) + FROM ApplicationEvaluationEntity e + WHERE e.applicationId IN :applicationIds + AND FUNCTION('DATE', e.endDate) BETWEEN :startDate AND :endDate + AND e.isDeleted = false +""") + Long countDueApplicationsBetween( + @Param("applicationIds") List applicationIds, + @Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate + ); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java index db645f95..c46c52c7 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationRepository.java @@ -7,6 +7,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.math.BigDecimal; import java.util.List; import java.util.Optional; @@ -24,7 +25,6 @@ public interface ApplicationRepository extends JpaRepository findByIdAndUserIdAndIsDeletedFalse(Long id,Long userId); - Optional findByUserIdAndUserWithCompanyIdAndCallIdAndIsDeletedFalse(Long userId, Long userWithCompanyId, Long callId); public Optional findByIdAndUserIdAndCallIdAndIsDeletedFalse(Long applicationId, Long userId, Long callId); @@ -44,4 +44,37 @@ public interface ApplicationRepository extends JpaRepository findApplicationsPerCallWithName(@Param("hubId") Long hubId); + + // Count Applications by Status by Hub ID + @Query("SELECT a.status, COUNT(a.id) FROM ApplicationEntity a WHERE a.isDeleted = false AND a.call.hub.id = :hubId GROUP BY a.status") + List findApplicationsByStatus(@Param("hubId") Long hubId); + + @Query("SELECT SUM(a.amountRequested) " + + "FROM ApplicationEntity a " + + "WHERE a.hubId = :hubId AND a.status IN ('SUBMIT', 'SOCCORSO', 'APPROVED', 'EVALUATION', 'APPOINTMENT', 'NDG', 'ADMISSIBLE','REJECTED')") + BigDecimal findTotalAmountRequestedOfApplication(@Param("hubId") Long hubId); + + @Query("SELECT SUM(a.amountAccepted) " + + "FROM ApplicationEntity a " + + "WHERE a.hubId = :hubId AND a.status = 'APPROVED'") + BigDecimal findTotalAmountAcceptedOfApplication(@Param("hubId") Long hubId); + + @Query("SELECT COUNT(a) FROM ApplicationEntity a WHERE a.hubId = :hubId AND a.status = 'SUBMIT' AND a.isDeleted = false") + public Long countApplicationsByHubId(@Param("hubId") Long hubId); + + @Query("SELECT COUNT(a) FROM ApplicationEntity a WHERE a.hubId = :hubId AND a.status = 'EVALUATION' AND a.isDeleted = false") + Long countAssignedApplicationsByHubId(@Param("hubId") Long hubId); + + @Query("SELECT COUNT(a) FROM ApplicationEntity a WHERE a.hubId = :hubId AND a.status = 'APPROVED' AND a.isDeleted = false") + Long countApprovedApplicationsByHubId(@Param("hubId") Long hubId); + + @Query("SELECT COUNT(a) FROM ApplicationEntity a WHERE a.hubId = :hubId AND a.status = 'SOCCORSO' AND a.isDeleted = false") + Long countSoccorsoApplicationsByHubId(@Param("hubId") Long hubId); + @Query("SELECT a.id FROM ApplicationEntity a WHERE a.hubId = :hubId AND a.isDeleted = false") + List findApplicationIdsByHubId(@Param("hubId") Long hubId); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/CallRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/CallRepository.java index dc53bad7..d666de41 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/CallRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/CallRepository.java @@ -48,4 +48,10 @@ public interface CallRepository extends JpaRepository, JpaSpec BigDecimal findTotalAmountOfPublishedCallsAndHubId(@Param("hubId") Long hubId); @Query("SELECT c FROM CallEntity c WHERE c.id IN :ids AND c.status IN :status") List findByIdInAndStatusIn(@Param("ids") List ids, @Param("status") List status); + + @Query("SELECT SUM(c.amount) FROM CallEntity c WHERE c.hub.id = :hubId AND c.status = 'PUBLISH'") + BigDecimal findTotalAmountOfPublishedCalls(@Param("hubId") Long hubId); + + List findByIdIn(@Param("ids") List ids); + } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ExpirationConfigRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ExpirationConfigRepository.java new file mode 100644 index 00000000..21c7b975 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ExpirationConfigRepository.java @@ -0,0 +1,13 @@ +package net.gepafin.tendermanagement.repositories; + +import net.gepafin.tendermanagement.entities.ExpirationConfigEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ExpirationConfigRepository extends JpaRepository { + List findByTypeAndIsDeletedFalse(String type); + +} diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java index f0163c47..eb93f8dd 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/UserActionsRepository.java @@ -1,10 +1,18 @@ package net.gepafin.tendermanagement.repositories; import net.gepafin.tendermanagement.entities.UserActionEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; 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; +import java.util.List; + @Repository public interface UserActionsRepository extends JpaRepository { UserActionEntity findUserActionById(Long id); + + UserActionEntity findUserActionByIdAndIsDeletedFalse(Long id); } diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java index e8cb813a..ab00d3e0 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/UserRepository.java @@ -2,6 +2,8 @@ package net.gepafin.tendermanagement.repositories; import net.gepafin.tendermanagement.entities.UserEntity; 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; import java.util.List; @@ -12,9 +14,16 @@ public interface UserRepository extends JpaRepository { UserEntity findByBeneficiaryId(Long beneficiaryId); - Optional findByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); +// Optional findByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); - boolean existsByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); + @Query("SELECT u FROM UserEntity u WHERE LOWER(u.email) = LOWER(:email) AND u.hub.uniqueUuid = :hubUuid AND u.roleEntity.roleType <> :roleType") + Optional findUserExcludingRoleType( + @Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("roleType") String roleType + ); + +// boolean existsByEmailIgnoreCaseAndHubUniqueUuid(String email, String hubUuid); List findByRoleEntityIdInAndHubId(List roleIds, Long hubId); @@ -24,7 +33,28 @@ public interface UserRepository extends JpaRepository { Optional findByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); - boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); +// Boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); + + @Query("SELECT COUNT(u) > 0 " + + "FROM UserEntity u " + + "WHERE LOWER(u.email) = LOWER(:email) " + + "AND u.hub.uniqueUuid = :hubUuid " + + "AND u.roleEntity.roleType <> :beneficiaryRoleType") + Boolean existsByEmailIgnoreCaseForNonBeneficiaries(@Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("beneficiaryRoleType") String beneficiaryRoleType); + + @Query("SELECT COUNT(u) > 0 " + + "FROM UserEntity u " + + "WHERE LOWER(u.email) = LOWER(:email) " + + "AND u.hub.uniqueUuid = :hubUuid " + + "AND u.roleEntity.roleType = :beneficiaryRoleType") + Boolean existsByEmailIgnoreCaseForBeneficiaries(@Param("email") String email, + @Param("hubUuid") String hubUuid, + @Param("beneficiaryRoleType") String beneficiaryRoleType); + + + boolean existsByBeneficiaryCodiceFiscaleAndHubId(String codiceFiscale, Long hubId); List findByRoleEntity_RoleTypeAndHubId(String roleType, Long hubId); diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java index 91cfaf78..6a4c7b81 100644 --- a/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ApplicationAmendmentScheduler.java @@ -3,10 +3,26 @@ package net.gepafin.tendermanagement.scheduler; import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.dao.ApplicationAmendmentRequestDao; import net.gepafin.tendermanagement.dao.NotificationDao; -import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + import net.gepafin.tendermanagement.entities.ApplicationEntity; -import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; import net.gepafin.tendermanagement.entities.AssignedApplicationsEntity; +import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; +import net.gepafin.tendermanagement.repositories.ApplicationRepository; +import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity; +import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity; import net.gepafin.tendermanagement.enums.ApplicationAmendmentRequestEnum; import net.gepafin.tendermanagement.enums.ApplicationEvaluationStatusTypeEnum; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; @@ -18,25 +34,9 @@ import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; import net.gepafin.tendermanagement.model.request.UserActionRequest; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.repositories.ApplicationAmendmentRequestRepository; -import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; -import net.gepafin.tendermanagement.repositories.ApplicationRepository; -import net.gepafin.tendermanagement.repositories.AssignedApplicationsRepository; -import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.util.DateTimeUtil; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; @Component public class ApplicationAmendmentScheduler { @@ -60,9 +60,6 @@ public class ApplicationAmendmentScheduler { @Autowired private AssignedApplicationsRepository assignedApplicationsRepository; - @Autowired - private ApplicationService applicationService; - @Autowired private ApplicationRepository applicationRepository; diff --git a/src/main/java/net/gepafin/tendermanagement/scheduler/ExpirationScheduler.java b/src/main/java/net/gepafin/tendermanagement/scheduler/ExpirationScheduler.java new file mode 100644 index 00000000..60025630 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/scheduler/ExpirationScheduler.java @@ -0,0 +1,125 @@ +package net.gepafin.tendermanagement.scheduler; + +import net.gepafin.tendermanagement.dao.ApplicationAmendmentRequestDao; +import net.gepafin.tendermanagement.dao.ApplicationEvaluationDao; +import net.gepafin.tendermanagement.dao.NotificationDao; +import net.gepafin.tendermanagement.entities.*; +import net.gepafin.tendermanagement.enums.ExpirationTypeEnum; +import net.gepafin.tendermanagement.enums.NotificationTypeEnum; +import net.gepafin.tendermanagement.repositories.ApplicationAmendmentRequestRepository; +import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; +import net.gepafin.tendermanagement.repositories.ExpirationConfigRepository; +import net.gepafin.tendermanagement.service.ApplicationService; +import net.gepafin.tendermanagement.service.CompanyService; +import net.gepafin.tendermanagement.service.UserService; +import net.gepafin.tendermanagement.util.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class ExpirationScheduler { + + @Autowired + private ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository; + + @Autowired + private ApplicationEvaluationRepository applicationEvaluationRepository; + + @Autowired + private ExpirationConfigRepository expirationNotificationConfigRepository; + + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + + @Autowired + private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; + + @Autowired + private UserService userService; + + @Autowired + private ApplicationService applicationService; + + @Autowired + private NotificationDao notificationDao; + + @Autowired + private CompanyService companyService; + + private static final Logger log = LoggerFactory.getLogger(ExpirationScheduler.class); + + @Scheduled(cron = "0 0 3 * * ?") + public void processExpiration(){ + log.info("Starting the Expiration scheduler..."); + try { + Utils.setHttpServletRequestForScheduler(); + + log.info("Starting processing expiration notifications for Amendment"); + processExpiration(ExpirationTypeEnum.AMENDMENT); + + log.info("Starting processing expiration notifications for Evaluation"); + processExpiration(ExpirationTypeEnum.EVALUATION); + + log.info("Expiration scheduler completed successfully."); + + } + catch (Exception e){ + log.error("An error occurred during the Notification Expiration Scheduler: {}", e.getMessage(), e); + } + } + + private void processExpiration(ExpirationTypeEnum type) { + List configEntities = expirationNotificationConfigRepository.findByTypeAndIsDeletedFalse(type.getValue()); + + for (ExpirationConfigEntity config : configEntities) { + Long daysBefore = config.getIntervalDays(); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime startDate = now.plusDays(daysBefore).withHour(0).withMinute(0).withSecond(0).withNano(0); + LocalDateTime endDate = startDate.plusDays(1).minusNanos(1).withNano(0); + + if (ExpirationTypeEnum.AMENDMENT.equals(type)) { + processAmendmentExpiration(startDate, endDate, daysBefore); + } else if (ExpirationTypeEnum.EVALUATION.equals(type)) { + processEvaluationExpiration(startDate, endDate, daysBefore); + } + } + } + + private void processAmendmentExpiration(LocalDateTime startDate, LocalDateTime endDate, Long daysBefore) { + List amendmentEntities = applicationAmendmentRequestRepository.findExpiringBetween(startDate, endDate); + + for (ApplicationAmendmentRequestEntity entity : amendmentEntities) { + ApplicationEntity application = entity.getApplicationEvaluationEntity().getAssignedApplicationsEntity().getApplication(); + Map placeHolders = replacePlaceholders(application,daysBefore); + notificationDao.sendNotificationToInstructor(placeHolders,entity.getApplicationEvaluationEntity(), NotificationTypeEnum.AMENDMENT_EXPIRATION_REMINDER); + } + } + + private Map replacePlaceholders (ApplicationEntity application, Long daysBefore){ + Long companyId = application.getCompanyId(); + CompanyEntity company = companyService.validateCompany(companyId); + Map placeHolders = new HashMap<>(); + placeHolders.put("{{call_name}}",application.getCall().getName()); + placeHolders.put("{{company_name}}", company.getCompanyName()); + placeHolders.put("{{days_before}}", daysBefore.toString()); + return placeHolders; + } + + private void processEvaluationExpiration(LocalDateTime startDate, LocalDateTime endDate, Long daysBefore) { + List evaluationEntities = applicationEvaluationRepository.findExpiringBetween(startDate, endDate); + + for (ApplicationEvaluationEntity entity : evaluationEntities) { + ApplicationEntity application = entity.getAssignedApplicationsEntity().getApplication(); + Map placeHolders = replacePlaceholders(application,daysBefore); + notificationDao.sendNotificationToInstructor(placeHolders,entity,NotificationTypeEnum.EVALUATION_EXPIRATION_REMINDER); + } + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java b/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java index 6328ca6e..0e34c1b6 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/DashboardService.java @@ -1,6 +1,7 @@ package net.gepafin.tendermanagement.service; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; @@ -9,5 +10,5 @@ public interface DashboardService { public SuperAdminWidgetResponseBean getDashboardWidgetForSuperAdmin(HttpServletRequest request); public BeneficiaryWidgetResponseBean getDashboardWidgetForBeneficiary(HttpServletRequest request, Long companyId); - + public ApplicationWidgetResponseBean getApplicationDetails(HttpServletRequest request); } diff --git a/src/main/java/net/gepafin/tendermanagement/service/UserService.java b/src/main/java/net/gepafin/tendermanagement/service/UserService.java index 4808b00e..6a92a667 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/UserService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/UserService.java @@ -27,7 +27,7 @@ public interface UserService { UserEntity validateUser(Long userId); - String initiatePasswordReset(InitiatePasswordResetReq resetReq); + void initiatePasswordReset(InitiatePasswordResetReq resetReq); Boolean resetPassword(ResetPasswordReq resetPasswordReq); diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java b/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java index ad8182de..12ea0123 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/AuthenticationService.java @@ -12,10 +12,7 @@ import net.gepafin.tendermanagement.entities.HubEntity; import net.gepafin.tendermanagement.entities.LoginAttemptEntity; import net.gepafin.tendermanagement.entities.SamlResponseEntity; import net.gepafin.tendermanagement.entities.UserEntity; -import net.gepafin.tendermanagement.enums.LoginAttemptResultEnum; -import net.gepafin.tendermanagement.enums.LoginAttemptTypeEnum; -import net.gepafin.tendermanagement.enums.UserStatusEnum; -import net.gepafin.tendermanagement.enums.VersionActionTypeEnum; +import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.LoginReq; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.model.response.CompanyResponse; @@ -40,6 +37,7 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Service; @@ -95,15 +93,22 @@ public class AuthenticationService { LoginAttemptEntity loginAttemptEntity = prepareLoginAttemptEntity(loginReq, request); try { log.info("Attempting login for email: {}", loginReq.getEmail()); + user = userRepository.findUserExcludingRoleType( + loginReq.getEmail(), + loginReq.getHubUuid(), + RoleStatusEnum.ROLE_BENEFICIARY.getValue() + ).orElseThrow(() -> new ResourceNotFoundException( + Status.NOT_FOUND, + Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG) + )); + String emailWithHubId = loginReq.getEmail()+":"+loginReq.getHubUuid(); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( emailWithHubId, loginReq.getPassword()); Authentication authentication = this.authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); log.info("Authentication successful for email: {}", loginReq.getEmail()); - user = userRepository.findByEmailIgnoreCaseAndHubUniqueUuid(loginReq.getEmail(), loginReq.getHubUuid()) - .orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, - Translator.toLocale(GepafinConstant.USER_NOT_FOUND_MSG))); + loginAttemptEntity.setUserId(user.getId()); if (Boolean.FALSE.equals(UserStatusEnum.ACTIVE.getValue().equals(user.getStatus()))) { throw new ResourceNotFoundException(Status.NOT_FOUND, diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java index 1a6cd6fd..e578a3dc 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/DashboardServiceImpl.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.dao.DashboardDao; import net.gepafin.tendermanagement.entities.CompanyEntity; import net.gepafin.tendermanagement.entities.UserEntity; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; import net.gepafin.tendermanagement.service.DashboardService; @@ -32,4 +33,10 @@ public class DashboardServiceImpl implements DashboardService { CompanyEntity company = validator.validateUserWithCompany(request, companyId); return dashboardDao.getDashboardWidgetForBeneficiary(userEntity, company); } + + @Override + public ApplicationWidgetResponseBean getApplicationDetails(HttpServletRequest request) { + UserEntity userEntity=validator.validateUser(request); + return dashboardDao.getApplicationDetails(userEntity); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java index c38b2c8c..b959ba61 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/UserServiceImpl.java @@ -77,8 +77,8 @@ public class UserServiceImpl implements UserService { } @Override - public String initiatePasswordReset(InitiatePasswordResetReq resetReq) { - return userDao.initiatePasswordReset(resetReq); + public void initiatePasswordReset(InitiatePasswordResetReq resetReq) { + userDao.initiatePasswordReset(resetReq); } @Override diff --git a/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java b/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java index e2ab4521..90b7a052 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java +++ b/src/main/java/net/gepafin/tendermanagement/util/LoggingUtil.java @@ -41,6 +41,8 @@ public class LoggingUtil { @Autowired private TokenProvider tokenProvider; + private static final ThreadLocal userActionIdHolder = new ThreadLocal<>(); + public UserActionEntity logUserAction(UserActionRequest userActionRequest) { UserActionEntity userAction = new UserActionEntity(); try { @@ -83,12 +85,22 @@ public class LoggingUtil { userAction.setResponse(response); userActionsRepository.save(userAction); userActionRequest.getRequest().setAttribute(GepafinConstant.USER_ACTION_ID, userAction.getId()); + userActionIdHolder.set(userAction.getId()); } catch (Exception e) { log.error("Error logging user action: {}", e.getMessage(), e); } return userAction; } + public Long getUserActionId() { + return userActionIdHolder.get(); + } + + public void clearUserActionId() { + userActionIdHolder.remove(); + log.info("UserActionId cleared from ThreadLocal"); + } + private String normalizeUrl(String url) { url = url.replaceAll("(? getVersionHistoryLogById(Long id) { return versionHistoryRepository.findVersionHistoryByUserActionId(id); } + + public void updateUserActionWithError(Long userActionId, String errorDetails) { + if (userActionId == null) { + return; + } + + UserActionEntity userAction = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId); + if (userAction != null) { + userAction.setResponse(errorDetails); + userActionsRepository.save(userAction); + } + } + + public void updateUserActionWithResponse(Long userActionId, String response) { + if (userActionId == null) { + return; + } + + UserActionEntity userAction = userActionsRepository.findUserActionByIdAndIsDeletedFalse(userActionId); + if (userAction != null) { + userAction.setResponse(response); + userActionsRepository.save(userAction); + } + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java b/src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java new file mode 100644 index 00000000..0064bb82 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/util/UserActionAspect.java @@ -0,0 +1,148 @@ +package net.gepafin.tendermanagement.util; + +import com.amazonaws.services.alexaforbusiness.model.UnauthorizedException; +import jakarta.persistence.EntityNotFoundException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import net.gepafin.tendermanagement.constants.GepafinConstant; +import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; +import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; +import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.server.ResponseStatusException; + +import java.lang.reflect.InvocationTargetException; +import java.nio.file.AccessDeniedException; +import java.util.LinkedHashMap; +import java.util.Map; + +@Aspect +@Component +@Slf4j +public class UserActionAspect { + + @Autowired + private LoggingUtil loggingUtil; + + @Around("execution(public * net.gepafin.tendermanagement.web.rest.api.impl..*(..))") + public Object logApiResponse(ProceedingJoinPoint joinPoint) throws Throwable { + + Object result; + + HttpServletRequest request = getRequestFromContext(); + try { + Long userActionId = getUserActionIdFromRequest(request); + + if (userActionId != null) { + request.setAttribute(GepafinConstant.USER_ACTION_ID, userActionId); + log.info("Stored userActionId in RequestContext: {}", userActionId); + } else { + userActionId = loggingUtil.getUserActionId(); + if (userActionId != null) { + request.setAttribute(GepafinConstant.USER_ACTION_ID, userActionId); + } + } + + result = joinPoint.proceed(); + + if (result instanceof ResponseEntity) { + Long storedUserActionId = (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID); + handleSuccessResponse((ResponseEntity) result, storedUserActionId == null ? userActionId : storedUserActionId); + } + } catch (Exception ex) { + log.error("Exception occurred: ", ex); + handleError(ex, getUserActionIdFromRequest(request)); + throw ex; + } finally { + loggingUtil.clearUserActionId(); + } + + return result; + } + + private void handleSuccessResponse(ResponseEntity responseEntity, Long userActionId) { + + if (userActionId != null) { + Map responseWithUserAction = new LinkedHashMap<>(); + responseWithUserAction.put(GepafinConstant.STATUS_CODE_STRING, responseEntity.getStatusCode().value()); + + // Log and update user action + loggingUtil.updateUserActionWithResponse(userActionId, Utils.convertMapIntoJsonString(responseWithUserAction)); + log.info("Updated userActionId with response: {}", userActionId); + } + } + + private void handleError(Throwable ex, Long userActionId) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + + HttpStatus status = getStatusCodeFromException(ex); + log.info("Status Code received from exception : {}", status); + String errorMessage = ex.getMessage(); + + Map errorResponse = new LinkedHashMap<>(); + errorResponse.put(GepafinConstant.STATUS_CODE_STRING, status.value()); + errorResponse.put(GepafinConstant.GET_STATUS_CODE_STRING, status); + errorResponse.put(GepafinConstant.MESSAGE_STRING, errorMessage); + + if (userActionId != null) { + String errorDetails = Utils.convertMapIntoJsonString(errorResponse); + loggingUtil.updateUserActionWithError(userActionId, errorDetails); + log.info("Updated userActionId with error details: {}", userActionId); + } + } + + private HttpServletRequest getRequestFromContext() { + + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + return attributes != null ? attributes.getRequest() : null; + } + + private Long getUserActionIdFromRequest(HttpServletRequest request) { + + if (request != null) { + Object userActionIdAttr = request.getAttribute(GepafinConstant.USER_ACTION_ID); + return userActionIdAttr != null ? Long.valueOf(userActionIdAttr.toString()) : null; + } + return null; + } + + private HttpStatus getStatusCodeFromException(Throwable ex) { + + if (ex instanceof ResourceNotFoundException) { + return HttpStatus.NOT_FOUND; + } + + if (ex instanceof ResponseStatusException responseStatusException) { + return (HttpStatus) responseStatusException.getStatusCode(); + } + + if (ex instanceof CustomValidationException) { + return HttpStatus.BAD_REQUEST; + } + + if (ex instanceof EntityNotFoundException) { + return HttpStatus.NOT_FOUND; + } + if (ex instanceof IllegalArgumentException || ex instanceof MissingServletRequestParameterException || ex instanceof MethodArgumentNotValidException) { + return HttpStatus.BAD_REQUEST; + } + if (ex instanceof AccessDeniedException) { + return HttpStatus.FORBIDDEN; + } + if (ex instanceof UnauthorizedException) { + return HttpStatus.UNAUTHORIZED; + } + + return HttpStatus.INTERNAL_SERVER_ERROR; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index e1df59bf..18c49a6e 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -24,6 +24,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.servlet.http.HttpServletRequest; +import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.model.request.GlobalFilters; import org.apache.commons.collections4.MapUtils; @@ -154,6 +155,9 @@ public class Utils { public static String convertMapIntoJsonString(Map map) { try { ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.enable(SerializationFeature.INDENT_OUTPUT); if (MapUtils.isNotEmpty(map)) { return mapper.writeValueAsString(map); } @@ -207,7 +211,7 @@ public class Utils { return new String(decodedBytes, StandardCharsets.UTF_8); } - public static String generateSecureToken() { + public static String generateSecureSamlToken() { SecureRandom secureRandom = new SecureRandom(); byte[] tokenBytes = new byte[24]; secureRandom.nextBytes(tokenBytes); @@ -215,7 +219,14 @@ public class Utils { log.debug("Generated secure token: {}", token); return token; } - + public static String generateSecureToken() { + SecureRandom secureRandom = new SecureRandom(); + byte[] tokenBytes = new byte[5]; + secureRandom.nextBytes(tokenBytes); + String token = Base64.getUrlEncoder().withoutPadding().encodeToString(tokenBytes); + log.debug("Generated secure token: {}", token); + return token; + } public static Map> convertStringIntoMap(String jsonString) { try { return mapper.readValue(jsonString, new TypeReference>>() { @@ -703,4 +714,22 @@ public class Utils { } return globalFilters; } + + public static Map parseErrorResponse(String responseBody) { + if (StringUtils.isBlank(responseBody)) { + return defaultErrorResponse(); + } + try { + return mapper.readValue(responseBody, Map.class); + } catch (Exception e) { + log.error("Failed to parse error response: {}", e.getMessage(), e); + return defaultErrorResponse(); + } + } + + private static Map defaultErrorResponse() { + return Collections.singletonMap("message", Translator.toLocale(GepafinConstant.INVALID_VATNUMBER)); + } + + } \ No newline at end of file diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java index 753473f2..31ae1be3 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/DashboardApi.java @@ -6,6 +6,7 @@ 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.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; import net.gepafin.tendermanagement.model.util.Response; @@ -46,7 +47,19 @@ public interface DashboardApi { produces = { "application/json" }) ResponseEntity> getDashboardWidgetForBeneficiary(HttpServletRequest request, @Parameter(description = "The company id", required = true) @PathVariable(value = "companyId", required = true) Long companyId); - + @Operation(summary = "Api to get Application details", + 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", + produces = { "application/json" }) + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER')") + ResponseEntity> getApplicationDetails(HttpServletRequest request); } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java index 86122344..6dc45bec 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/UserApi.java @@ -59,6 +59,7 @@ public interface UserApi { @RequestMapping(value = "/{userId}", produces = {"application/json"}, method = RequestMethod.PUT) + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") default ResponseEntity> updateUser(HttpServletRequest request, @Parameter(description = "The user id", required = true) @PathVariable("userId") Long userId, @Parameter(description = "User request object", required = true) @Valid @RequestBody UpdateUserReq userReq) { @@ -118,7 +119,7 @@ public interface UserApi { @RequestMapping(value = "/reset-password/initiate", produces = {"application/json"}, method = RequestMethod.POST) - ResponseEntity> initiatePasswordReset( + ResponseEntity> initiatePasswordReset(HttpServletRequest request, @Parameter(description = "Initiate password reset request object", required = true) @Valid @RequestBody InitiatePasswordResetReq initiatePasswordResetReq); @Operation(summary = "Api to reset password", @@ -131,7 +132,7 @@ public interface UserApi { @RequestMapping(value = "/reset-password", produces = {"application/json"}, method = RequestMethod.POST) - ResponseEntity> resetPassword( + ResponseEntity> resetPassword(HttpServletRequest request, @Parameter(description = "Reset password request object", required = true) @Valid @RequestBody ResetPasswordReq resetPasswordReq); @Operation(summary = "Api to change user password", responses = { diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java index d26ab560..dd6ef1ae 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/CustomUserDetailsService.java @@ -2,6 +2,7 @@ package net.gepafin.tendermanagement.web.rest.api.impl; import net.gepafin.tendermanagement.entities.RoleEntity; import net.gepafin.tendermanagement.entities.UserEntity; +import net.gepafin.tendermanagement.enums.RoleStatusEnum; import net.gepafin.tendermanagement.repositories.UserRepository; import org.slf4j.Logger; @@ -35,7 +36,10 @@ public class CustomUserDetailsService implements UserDetailsService { String email = loginParts[0]; String hubId = loginParts[1]; - UserEntity user = userRepository.findByEmailIgnoreCaseAndHubUniqueUuid(email, hubId) + UserEntity user = userRepository.findUserExcludingRoleType( + email, + hubId, + RoleStatusEnum.ROLE_BENEFICIARY.getValue()) .orElseThrow( () -> new UsernameNotFoundException("User " + email + " was not found in the database")); return createSpringSecurityUser(user); diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java index 7ff7be61..ed1f3d68 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/DashboardApiController.java @@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.enums.UserActionContextEnum; import net.gepafin.tendermanagement.enums.UserActionLogsEnum; import net.gepafin.tendermanagement.model.request.UserActionRequest; +import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean; import net.gepafin.tendermanagement.model.response.BeneficiaryWidgetResponseBean; import net.gepafin.tendermanagement.model.response.SuperAdminWidgetResponseBean; import net.gepafin.tendermanagement.model.util.Response; @@ -49,5 +50,14 @@ public class DashboardApiController implements DashboardApi { return ResponseEntity.status(HttpStatus.CREATED) .body(new Response<>(widgetResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.DASHBOARD_WIDGET_FETCHED_SUCCESSFULLY))); } + @Override + public ResponseEntity> getApplicationDetails(HttpServletRequest request) { + /** This code is responsible for creating user action logs for the "Get complete application page" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.VIEW).actionContext(UserActionContextEnum.GET_APPLICATION_DETAILS).build()); + + ApplicationWidgetResponseBean widgetResponseBean= dashboardService.getApplicationDetails(request); + return ResponseEntity.status(HttpStatus.CREATED) + .body(new Response<>(widgetResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.DASHBOARD_WIDGET_FETCHED_SUCCESSFULLY))); + } } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java index 22232b57..0d649943 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/UserApiController.java @@ -125,20 +125,35 @@ public class UserApiController implements UserApi { @Override public ResponseEntity> changePassword(HttpServletRequest httpServletRequest, @Valid @RequestBody ChangePasswordRequest request) { log.info("Change Password attempt for email: {}", request.getEmail()); + + /** This code is responsible for "Change user password" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) + .actionContext(UserActionContextEnum.CHANGE_USER_PASSWORD).build()); + userService.changePassword(httpServletRequest, request); return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.SUCCESS_PASSWORD_CHANGED))); } @Override - public ResponseEntity> initiatePasswordReset(InitiatePasswordResetReq request) { + public ResponseEntity> initiatePasswordReset(HttpServletRequest httpServletRequest,InitiatePasswordResetReq request) { log.info("Initiating password reset for email: {}", request.getEmail()); - String resetToken = userService.initiatePasswordReset(request); + + /** This code is responsible for "Initiating Password Reset Request" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) + .actionContext(UserActionContextEnum.INITIATE_PASSWORD_RESET_REQUEST).build()); + + userService.initiatePasswordReset(request); log.info("Password reset token generated for email: {}", request.getEmail()); - return ResponseEntity.ok(new Response<>(resetToken, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED))); + return ResponseEntity.ok(new Response<>(null, Status.SUCCESS, Translator.toLocale(GepafinConstant.RESET_PASSWORD_INITIATED))); } @Override - public ResponseEntity> resetPassword(ResetPasswordReq request) { + public ResponseEntity> resetPassword(HttpServletRequest httpServletRequest,ResetPasswordReq request) { log.info("Resetting password for username: {}", request.getEmail()); + + /** This code is responsible for "Resest user password" operation. **/ + loggingUtil.logUserAction(UserActionRequest.builder().request(httpServletRequest).actionType(UserActionLogsEnum.UPDATE) + .actionContext(UserActionContextEnum.RESET_USER_PASSWORD).build()); + Boolean success = userService.resetPassword(request); if (success) { log.info("Password reset successfully for username: {}", request.getEmail()); diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index df295088..c6a9cce8 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -24,7 +24,7 @@ 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.base.url=https://prd.galileonetwork.it/gateway/rest appointment.portal.user=UtenzaAPIPortal@621 appointment.portal.password=u13nzaAP1P0rtal appointment.portal.source=GEPAFINPORTAL @@ -32,7 +32,8 @@ appointment.portal.context=GEPAFINPORTAL flagDaFirmare=true # RabbitMQ properties for STOMP broker relay for Notification -spring.rabbitmq.host=rabbitmq.bflows.ai +#spring.rabbitmq.host=rabbitmq.bflows.ai +spring.rabbitmq.host=172.18.0.5 spring.rabbitmq.port=61613 spring.rabbitmq.username=guest spring.rabbitmq.password=guest 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 cf3c3987..25ff1bf2 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 @@ -1961,6 +1961,17 @@ + + select + setval('gepafin_schema.system_email_template_id_seq', (select + max(id)+1 + from gepafin_schema.system_email_template), false) + + + + @@ -1970,19 +1981,20 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -2042,7 +2054,7 @@ - + @@ -2081,6 +2093,11 @@ + + + + + @@ -2139,5 +2156,51 @@ + + + TRUNCATE TABLE FORM_FIELD RESTART IDENTITY; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/dump/insert_expiration_scheduler_data_07_01_2025.sql b/src/main/resources/db/dump/insert_expiration_scheduler_data_07_01_2025.sql new file mode 100644 index 00000000..dca8f3a1 --- /dev/null +++ b/src/main/resources/db/dump/insert_expiration_scheduler_data_07_01_2025.sql @@ -0,0 +1,9 @@ + +INSERT INTO expiration_config (interval_days, type, created_date, updated_date) +VALUES +(5, 'AMENDMENT', '2024-12-03T11:00:51', '2024-12-03T11:00:51'), +(2, 'AMENDMENT', '2024-12-03T11:00:51', '2024-12-03T11:00:51'), +(0, 'AMENDMENT', '2024-12-03T11:00:51', '2024-12-03T11:00:51'), +(5, 'EVALUATION', '2024-12-03T11:00:51', '2024-12-03T11:00:51'), +(2, 'EVALUATION', '2024-12-03T11:00:51', '2024-12-03T11:00:51'), +(0, 'EVALUATION', '2024-12-03T11:00:51', '2024-12-03T11:00:51'); diff --git a/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql b/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql new file mode 100644 index 00000000..927345b0 --- /dev/null +++ b/src/main/resources/db/dump/insert_system_email_template_for_reset_password_28_11_2024.sql @@ -0,0 +1,60 @@ +INSERT INTO gepafin_schema.system_email_template +(template_name, "type", html_content, subject, "json", "system", is_deleted, created_date, updated_date,email_scenario) +VALUES +( + 'Password Reset Link Email (Italian)', + 'PASSWORD_RESET', + ' + + + + + +
+ + + + + + + + + + + + + + + + + +
+

Richiesta di Reimpostazione Password

+
+

Gentile {{user_name}},

+

Hai richiesto di reimpostare la tua password.

+
+

Cordiali saluti,

+

{{email_signature}}

+
+ + Reimposta la Password +
+
', + 'Richiesta di Reimpostazione Password', + NULL, + true, + false, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + 'PASSWORD_RESET_REQUEST'); diff --git a/src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql b/src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql new file mode 100644 index 00000000..cfe54849 --- /dev/null +++ b/src/main/resources/db/dump/insert_system_email_template_for_user_28_11_2024.sql @@ -0,0 +1,29 @@ +INSERT INTO gepafin_schema.system_email_template +(template_name, "type", html_content, subject, "json", "system", is_deleted, created_date, updated_date, email_scenario) +VALUES +( + 'Welcome Email for New User', + 'USER_ONBOARDING', + ' + +
+

Benvenuto!

+

Ciao {{user_name}},

+

Siamo lieti di averti con noi. Di seguito trovi alcune informazioni utili:

+
    +
  • Nome Utente: {{user_email}}
  • +
+

Per accedere, utilizza il tuo indirizzo email registrato. Se hai bisogno di supporto, non esitare a contattarci.

+

Distinti saluti,

+

{{email_signature}}

+
+ + ', + 'Welcome - {{user_name}}', + NULL, + true, + false, + CURRENT_TIMESTAMP, + CURRENT_TIMESTAMP, + 'USER_CREATION' +); diff --git a/src/main/resources/db/dump/update_form_field_data_03_01_2025.sql b/src/main/resources/db/dump/update_form_field_data_03_01_2025.sql new file mode 100644 index 00000000..c3c08ca7 --- /dev/null +++ b/src/main/resources/db/dump/update_form_field_data_03_01_2025.sql @@ -0,0 +1,101 @@ +INSERT INTO FORM_FIELD (SORT_ORDER, NAME, LABEL, DESCRIPTION, SETTINGS, VALIDATORS, CREATED_DATE, UPDATED_DATE) +VALUES +(1, 'textinput', 'Testo Breve', 'Per risposte concise (nomi, titoli, brevi descrizioni)', + '[{"name": "label", "value": "Testo Breve"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "minLength": null, "maxLength": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(2, 'textarea', 'Testo Lungo', 'Campo di testo esteso per paragrafi, descrizioni, proposte', + '[{"name": "label", "value": "Testo Lungo"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "minLength": null, "maxLength": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(3, 'wysiwyg', 'Campo di Testo Formattato', 'Editor avanzato per testo con formattazione', + '[{"name": "label", "value": "Testo Formattato"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "minLength": null, "maxLength": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(4, 'numberinput', 'Campo Numerico', 'Per l''inserimento di valori numerici (quantità, importi, percentuali)', + '[{"name": "label", "value": "Numero"}, {"name": "placeholder", "value": "0"}, {"name": "step", "value": "0"},{"name": "isRequestedAmount","value": false}]', + '{"isRequired": false, "min": null, "max": null, "pattern": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(5, 'radio', 'Scelta Singola', 'Gruppo di opzioni per selezione singola', + '[{"name": "label", "value": "Scelta Singola"}, {"name": "options", "value": []}]', + '{"isRequired": false, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(6, 'select', 'Menu a Tendina', 'Selezione da opzioni predefinite', + '[{"name": "label", "value": "Menu a Tendina"}, {"name": "options", "value": []}]', + '{"isRequired": false, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(7, 'checkboxes', 'Scelta Multipla', 'Gruppo di opzioni per selezione singola o multipla', + '[{"name": "label", "value": "Scelta Multipla"}, {"name": "options", "value": []}]', + '{"isRequired": false, "min": null, "max": null, "custom": null}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(8, 'switch', 'Casella di Spunta', 'Per selezioni binarie, accettazioni, conferme', + '[{"name": "label", "value": "Casella di Spunta"}]', + '{"isRequired": false}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(9, 'datepicker', 'Data', 'Selezione di data', + '[{"name": "label", "value": "Data"}]', + '{"isRequired": false}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(10, 'fileupload', 'Caricamento File', 'Per l''upload di documenti o immagini', + '[{"name": "label", "value": "Caricamento File"}, {"name": "mime", "value": []}]', + '{"isRequired": false, "maxSize": 100000}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(11, 'textinput', 'Campo Partita IVA', 'Specifico per l''inserimento del numero di Partita IVA', + '[{"name": "label", "value": "Partita IVA"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isPIVA"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(12, 'textinput', 'Campo Codice Fiscale','Specifico per l''inserimento del Codice Fiscale italiano per persone fisiche e giuridiche', + '[{"name": "label", "value": "Codice Fiscale"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isCodiceFiscale"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(13, 'textinput', 'Campo CAP','Per l''inserimento del Codice di Avviamento Postale', + '[{"name": "label", "value": "CAP"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isCAP"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(14, 'textinput', 'Campo IBAN', 'Per l''inserimento del codice IBAN', + '[{"name": "label", "value": "IBAN"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": true, "custom": "isIBAN"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(15, 'textinput', 'Campo Email', 'Per l''inserimento di indirizzi email standard (non PEC)', + '[{"name": "label", "value": "Campo Email"}, {"name": "placeholder", "value": "nome@esempio.it"}]', + '{"isRequired": false, "custom": "isEmail"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(16, 'textinput', 'Campo PEC', 'Specifico per l''inserimento di un indirizzo di Posta Elettronica Certificata', + '[{"name": "label", "value": "Campo PEC"}, {"name": "placeholder", "value": "nome@pec.it"}]', + '{"isRequired": false, "custom": "isEmailPEC"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(17, 'textinput', 'Campo URL', 'Per l''inserimento di indirizzi web', + '[{"name": "label", "value": "Indirizzo URL"}, {"name": "placeholder", "value": ""}]', + '{"isRequired": false, "custom": "isUrl"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(18, 'textinput', 'Marca da bollo', 'Per inserire codice di marca da bollo', + '[{"name": "label", "value": "Marca da bollo"}, {"name": "placeholder", "value": "Numero identificativo"}]', + '{"isRequired": false, "custom": "isMarcaDaBollo"}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(19, 'paragraph', 'Paragrafo', 'Semplice testo formattato', + '[{"name": "text", "value": ""}]', + '{}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + +(20, 'table', 'Tabella', 'Tabella', + '[{"name": "label", "value": "Tabella"}, {"name": "table_columns", "value": []}]', + '{}', + CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); diff --git a/src/main/resources/db/dump/update_json_template_for_notification_03_01_2025.sql b/src/main/resources/db/dump/update_json_template_for_notification_03_01_2025.sql new file mode 100644 index 00000000..06bf6aa0 --- /dev/null +++ b/src/main/resources/db/dump/update_json_template_for_notification_03_01_2025.sql @@ -0,0 +1,3 @@ +INSERT INTO notification_type (notification_name,title, json_template,created_date,updated_date,is_deleted) VALUES +('AMENDMENT_EXPIRATION_REMINDER','Lemendamento sta per scadere','Lemendamento per {{call_name}} - {{company_name}} scadrà tra {{days_before}} giorni. Assicurati che tutte le azioni necessarie siano completate prima della scadenza.','2025-01-03T10:16:26.472Z','2025-01-03T10:16:26.472Z','false'), +('EVALUATION_EXPIRATION_REMINDER','La valutazione sta per scadere','Lemendamento per {{call_name}} - {{company_name}} scadrà tra {{days_before}} giorni. Assicurati che tutte le azioni necessarie siano completate prima della scadenza.','2025-01-03T10:16:26.472Z','2025-01-03T10:16:26.472Z','false'); \ No newline at end of file diff --git a/src/main/resources/message_en.properties b/src/main/resources/message_en.properties index 44a27275..c743b3fa 100644 --- a/src/main/resources/message_en.properties +++ b/src/main/resources/message_en.properties @@ -346,3 +346,4 @@ notification.sent.successfully=Notification sent successfully. notification.deleted.successfully=Notification deleted successfully. notification.updated.successfully=Notification updated successfully. user.with.company.not.found = User with company not found for user or company. +amount.accepted.required=Amount accepted is required while approving the application. diff --git a/src/main/resources/message_it.properties b/src/main/resources/message_it.properties index 0b22e9e3..14a5dbb5 100644 --- a/src/main/resources/message_it.properties +++ b/src/main/resources/message_it.properties @@ -329,7 +329,6 @@ appointment.created.successfully = Appuntamento creato con successo. error.try.again = Errore di chiamata di servizio durante l'esecuzione dell'operazione. Riprovare. document.uploading.is.in.progress = Il documento ? in fase di caricamento. all.document.checked.and.one.checklist.checked=Tutti i documenti devono essere controllati e almeno una checklist deve essere controllata. -<<<<<<< HEAD #notification messsages notification.already.in.state=La notifica � gi� nello stato fornito. @@ -339,5 +338,4 @@ notification.sent.successfully=Notifica inviata con successo. notification.deleted.successfully=Notifica eliminata con successo. notification.updated.successfully=Notifica aggiornata con successo. user.with.company.not.found = Utente con azienda non trovato per utente o azienda. -======= ->>>>>>> 832666a4d412c2c81f5c1dfb5b1866aba2c40bdd +amount.accepted.required=L'importo accettato è obbligatorio durante l'approvazione della domanda.