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/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/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 daaae5a8..cf0b9c66 100644 --- a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationEvaluationRepository.java @@ -1,6 +1,7 @@ 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; @@ -33,6 +34,12 @@ 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) 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/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/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml index 994c3941..4b73b739 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 @@ -2164,4 +2164,29 @@ path="db/dump/update_form_field_data_03_01_2025.sql"/> + + + + + + + + + + + + + + + + + + + + + + + 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/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