diff --git a/core-services/egov-hrms/pom.xml b/core-services/egov-hrms/pom.xml index 0a0507aa5a4..ff717d747cc 100644 --- a/core-services/egov-hrms/pom.xml +++ b/core-services/egov-hrms/pom.xml @@ -39,11 +39,11 @@ org.flywaydb flyway-core - 9.22.3 org.postgresql postgresql + 42.7.1 org.egov.services diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/config/PropertiesManager.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/config/PropertiesManager.java index 1f0522f0d36..14e911985c0 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/config/PropertiesManager.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/config/PropertiesManager.java @@ -82,7 +82,13 @@ public class PropertiesManager { @Value("${egov.idgen.path}") public String idGenEndpoint; - + + // Email + @Value("${kafka.topics.notification.email}") + private String emailNotifTopic; + + @Value("${notification.email.enabled}") + private Boolean isEmailNotificationEnabled; //Kafka Topics @Value("${kafka.topics.save.service}") @@ -96,7 +102,9 @@ public class PropertiesManager { @Value("${kafka.topics.hrms.updateData}") public String updateTopic; - + + @Value("${kafka.topics.hrms.email.notification}") + public String hrmsEmailNotifTopic; //Variables @Value("${egov.idgen.ack.name}") @@ -140,4 +148,10 @@ public class PropertiesManager { @Value("${egov.boundary.search.url}") private String boundarySearchUrl; + + @Value("${hrms.email.notification.implementation.partner}") + public String emailNotificationImplementationPartner; + + @Value("${hrms.email.notification.website.link}") + public String emailNotificationWebsiteLink; } \ No newline at end of file diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/consumer/HrmsConsumer.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/consumer/HrmsConsumer.java index a4c33b0c096..74b2360cd73 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/consumer/HrmsConsumer.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/consumer/HrmsConsumer.java @@ -32,12 +32,17 @@ public class HrmsConsumer { @Autowired private PropertiesManager propertiesManager; - @KafkaListener(topics = {"${kafka.topics.hrms.updateData}"}) + @KafkaListener(topics = {"${kafka.topics.hrms.updateData}", "${kafka.topics.hrms.email.notification}"}) public void listenUpdateEmployeeData(final HashMap record,@Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { try { EmployeeRequest employeeRequest = mapper.convertValue(record, EmployeeRequest.class); - hrmsProducer.push(propertiesManager.getUpdateEmployeeTopic(), employeeRequest); - notificationService.sendReactivationNotification(employeeRequest); + + if(topic.equals(propertiesManager.getHrmsEmailNotifTopic())) { + notificationService.processEmailNotification(employeeRequest); + } else { + hrmsProducer.push(propertiesManager.getUpdateEmployeeTopic(), employeeRequest); + notificationService.sendReactivationNotification(employeeRequest); + } } catch (final Exception e) { log.error("Error while listening to value: " + record + " on topic: " + topic + ": ", e); diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/service/EmployeeService.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/service/EmployeeService.java index 2cecd5f5f71..914982bbad3 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/service/EmployeeService.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/service/EmployeeService.java @@ -103,6 +103,9 @@ public class EmployeeService { @Autowired private ObjectMapper objectMapper; + @Autowired + private IndividualService individualService; + /** * Service method for create employee. Does following: * 1. Sets ids to all the objects using idgen service. @@ -121,8 +124,12 @@ public EmployeeResponse create(EmployeeRequest employeeRequest) { enrichCreateRequest(employee, requestInfo); createUser(employee, requestInfo); pwdMap.put(employee.getUuid(), employee.getUser().getPassword()); - employee.getUser().setPassword(null); }); + hrmsProducer.push(propertiesManager.getHrmsEmailNotifTopic(), employeeRequest); + + // Setting password as null after sending employeeRequest to email notification topic to send email. + employeeRequest.getEmployees().forEach(employee -> employee.getUser().setPassword(null)); + hrmsProducer.push(propertiesManager.getSaveEmployeeTopic(), employeeRequest); notificationService.sendNotification(employeeRequest, pwdMap); return generateResponse(employeeRequest); @@ -137,6 +144,7 @@ public EmployeeResponse create(EmployeeRequest employeeRequest) { */ public EmployeeResponse search(EmployeeSearchCriteria criteria, RequestInfo requestInfo) { boolean userChecked = false; + Long totalCount = 0L; /*if(null == criteria.getIsActive() || criteria.getIsActive()) criteria.setIsActive(true); else @@ -156,6 +164,7 @@ public EmployeeResponse search(EmployeeSearchCriteria criteria, RequestInfo requ userSearchCriteria.put(HRMSConstants.HRMS_USER_SEARCH_CRITERA_USERNAME, criteria.getCodes().get(0)); } UserResponse userResponse = userService.getUser(requestInfo, userSearchCriteria); + totalCount = userResponse.getTotalCount(); userChecked =true; if(!CollectionUtils.isEmpty(userResponse.getUser())) { mapOfUsers.putAll(userResponse.getUser().stream() @@ -178,6 +187,7 @@ public EmployeeResponse search(EmployeeSearchCriteria criteria, RequestInfo requ userSearchCriteria.put(HRMSConstants.HRMS_USER_SEARCH_CRITERA_TENANTID,criteria.getTenantId()); userSearchCriteria.put(HRMSConstants.HRMS_USER_SEARCH_CRITERA_NAME,name); UserResponse userResponse = userService.getUser(requestInfo, userSearchCriteria); + totalCount = userResponse.getTotalCount(); userChecked =true; if(!CollectionUtils.isEmpty(userResponse.getUser())) { mapOfUsers.putAll(userResponse.getUser().stream() @@ -191,6 +201,32 @@ public EmployeeResponse search(EmployeeSearchCriteria criteria, RequestInfo requ else criteria.setUuids(userUUIDs); } + + if(!CollectionUtils.isEmpty(criteria.getUserServiceUuids())) { + List userUUIDs = new ArrayList<>(); + Map userSearchCriteria = new HashMap<>(); + + userSearchCriteria.put(HRMSConstants.HRMS_USER_SERACH_CRITERIA_USERTYPE_CODE, HRMSConstants.HRMS_USER_SERACH_CRITERIA_USERTYPE); + userSearchCriteria.put(HRMSConstants.HRMS_USER_SEARCH_CRITERA_TENANTID, criteria.getTenantId()); + userSearchCriteria.put(HRMSConstants.HRMS_USER_SEARCH_CRITERA_USER_SERVICE_UUIDS, criteria.getUserServiceUuids()); + if(!CollectionUtils.isEmpty(criteria.getNames())) + userSearchCriteria.put(HRMSConstants.HRMS_USER_SEARCH_CRITERA_NAME, criteria.getNames().get(0)); + UserResponse userResponse = userService.getUser(requestInfo, userSearchCriteria); + totalCount = userResponse.getTotalCount(); + userChecked =true; + if(!CollectionUtils.isEmpty(userResponse.getUser())) { + mapOfUsers.putAll(userResponse.getUser().stream() + .collect(Collectors.toMap(User::getUuid, Function.identity()))); + } + + List uuids = userResponse.getUser().stream().map(User :: getUuid).collect(Collectors.toList()); + userUUIDs.addAll(uuids); + + if(!CollectionUtils.isEmpty(criteria.getUuids())) + criteria.setUuids(criteria.getUuids().stream().filter(userUUIDs::contains).collect(Collectors.toList())); + else + criteria.setUuids(userUUIDs); + } } if(userChecked) criteria.setTenantId(null); @@ -207,6 +243,7 @@ public EmployeeResponse search(EmployeeSearchCriteria criteria, RequestInfo requ if(mapOfUsers.isEmpty()){ log.info("searching in user service"); UserResponse userResponse = userService.getUser(requestInfo, userSearchCriteria); + totalCount = userResponse.getTotalCount(); if(!CollectionUtils.isEmpty(userResponse.getUser())) { mapOfUsers = userResponse.getUser().stream() .collect(Collectors.toMap(User :: getUuid, Function.identity())); @@ -217,7 +254,8 @@ public EmployeeResponse search(EmployeeSearchCriteria criteria, RequestInfo requ } } return EmployeeResponse.builder().responseInfo(factory.createResponseInfoFromRequestInfo(requestInfo, true)) - .employees(employees).build(); + .employees(employees) + .totalCount(totalCount).build(); } @@ -231,7 +269,14 @@ private void createUser(Employee employee, RequestInfo requestInfo) { enrichUser(employee); UserRequest request = UserRequest.builder().requestInfo(requestInfo).user(employee.getUser()).build(); try { - UserResponse response = userService.createUser(request); + UserResponse response; + if(userService instanceof IndividualService) { + String localityCode = (employee.getJurisdictions()!=null && !employee.getJurisdictions().isEmpty())? employee.getJurisdictions().get(0).getBoundary() : null; + response = individualService.createUserByLocality(request, localityCode); + } + else{ + response = userService.createUser(request); + } User user = response.getUser().get(0); employee.setId(UUID.fromString(user.getUuid()).getMostSignificantBits()); employee.setUuid(user.getUuid()); diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/service/IndividualService.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/service/IndividualService.java index 6a967bf8cfb..17302f58b1f 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/service/IndividualService.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/service/IndividualService.java @@ -17,26 +17,19 @@ import org.apache.commons.lang3.StringUtils; import org.egov.common.contract.request.RequestInfo; import org.egov.common.models.core.Role; -import org.egov.common.models.individual.Address; -import org.egov.common.models.individual.AddressType; -import org.egov.common.models.individual.Gender; -import org.egov.common.models.individual.Identifier; -import org.egov.common.models.individual.Individual; -import org.egov.common.models.individual.IndividualBulkResponse; -import org.egov.common.models.individual.IndividualRequest; -import org.egov.common.models.individual.IndividualResponse; -import org.egov.common.models.individual.Name; -import org.egov.common.models.individual.UserDetails; +import org.egov.common.models.individual.*; import org.egov.hrms.config.PropertiesManager; import org.egov.hrms.repository.RestCallRepository; import org.egov.hrms.utils.HRMSConstants; import org.egov.hrms.web.contract.User; import org.egov.hrms.web.contract.UserRequest; import org.egov.hrms.web.contract.UserResponse; +import org.egov.hrms.web.models.IndividualBulkResponse; import org.egov.hrms.web.models.IndividualSearch; import org.egov.hrms.web.models.IndividualSearchRequest; import org.springframework.beans.factory.annotation.Autowired; +import static org.egov.hrms.utils.HRMSConstants.HRMS_USER_SEARCH_CRITERA_USER_SERVICE_UUIDS; import static org.egov.hrms.utils.HRMSConstants.SYSTEM_GENERATED; @Slf4j @@ -56,7 +49,7 @@ public IndividualService(PropertiesManager propertiesManager, @Override public UserResponse createUser(UserRequest userRequest) { - IndividualRequest request = mapToIndividualRequest(userRequest); + IndividualRequest request = mapToIndividualRequest(userRequest, null); StringBuilder uri = new StringBuilder(); uri.append(propertiesManager.getIndividualHost()); uri.append(propertiesManager.getIndividualCreateEndpoint()); @@ -70,6 +63,21 @@ public UserResponse createUser(UserRequest userRequest) { return userResponse; } + public UserResponse createUserByLocality(UserRequest userRequest, String localityCode) { + IndividualRequest request = mapToIndividualRequest(userRequest,localityCode); + StringBuilder uri = new StringBuilder(); + uri.append(propertiesManager.getIndividualHost()); + uri.append(propertiesManager.getIndividualCreateEndpoint()); + IndividualResponse response = restCallRepository + .fetchResult(uri, request, IndividualResponse.class); + UserResponse userResponse = null; + if (response != null && response.getIndividual() != null) { + log.info("response received from individual service"); + userResponse = mapToUserResponse(response); + } + return userResponse; + } + /** * Updates a user by searching for the corresponding individual and updating their details. * @@ -210,6 +218,7 @@ public UserResponse getUser(RequestInfo requestInfo, Map userSea mobileNumberList ) .id((List) userSearchCriteria.get("uuid")) + .userUuid((List) userSearchCriteria.get(HRMS_USER_SEARCH_CRITERA_USER_SERVICE_UUIDS)) .roleCodes((List) userSearchCriteria.get("roleCodes")) .username(usernameList) // given name @@ -256,7 +265,7 @@ private static Date convertMillisecondsToDate(long milliseconds) { } } - private static IndividualRequest mapToIndividualRequest(UserRequest userRequest) { + private static IndividualRequest mapToIndividualRequest(UserRequest userRequest, String localityCode) { Individual individual = Individual.builder() .id(userRequest.getUser().getUuid()) .userId(userRequest.getUser().getId() != null ? @@ -276,6 +285,7 @@ private static IndividualRequest mapToIndividualRequest(UserRequest userRequest) .type(AddressType.CORRESPONDENCE) .addressLine1(userRequest.getUser().getCorrespondenceAddress()) .clientReferenceId(String.valueOf(UUID.randomUUID())) + .locality((localityCode!=null) ? Boundary.builder().code(localityCode).build() : null) .isDeleted(Boolean.FALSE) .build())) /* @@ -306,6 +316,9 @@ private static IndividualRequest mapToIndividualRequest(UserRequest userRequest) .build()).collect(Collectors.toList())) .userType(UserType.fromValue(userRequest.getUser().getType())) .build()) + .skills(userRequest.getUser().getRoles().stream().map(role -> Skill.builder() + .type(role.getCode()).level(role.getCode()) + .build()).collect(Collectors.toList())) .isDeleted(Boolean.FALSE) .clientAuditDetails(AuditDetails.builder().createdBy(userRequest.getRequestInfo().getUserInfo().getUuid()).lastModifiedBy(userRequest.getRequestInfo().getUserInfo().getUuid()).build()) .rowVersion(userRequest.getUser().getRowVersion()) @@ -331,6 +344,7 @@ private static UserResponse mapToUserResponse(IndividualBulkResponse response) { .responseInfo(response.getResponseInfo()) .user(response.getIndividual().stream() .map(IndividualService::getUser).collect(Collectors.toList())) + .totalCount(response.getTotalCount()) .build(); return userResponse; } diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/service/NotificationService.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/service/NotificationService.java index acb427e4ce7..a8915e3983c 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/service/NotificationService.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/service/NotificationService.java @@ -11,30 +11,40 @@ import org.egov.hrms.producer.HRMSProducer; import org.egov.hrms.repository.RestCallRepository; import org.egov.hrms.utils.HRMSConstants; +import org.egov.hrms.utils.NotificationUtil; +import org.egov.hrms.web.contract.EmailRequest; import org.egov.hrms.web.contract.EmployeeRequest; import org.egov.hrms.web.contract.RequestInfoWrapper; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.jayway.jsonpath.JsonPath; import lombok.extern.slf4j.Slf4j; +import org.springframework.util.CollectionUtils; import org.springframework.web.client.RestTemplate; +import static org.egov.hrms.utils.HRMSConstants.HEALTH_HRMS_EMAIL_LOCALIZATION_CODE; + @Service @Slf4j public class NotificationService { - - @Autowired + private HRMSProducer producer; - - @Autowired + private RestCallRepository repository; - @Autowired private RestTemplate restTemplate; + private NotificationUtil notificationUtil; + + public NotificationService(HRMSProducer producer, RestCallRepository repository, RestTemplate restTemplate, NotificationUtil notificationUtil) { + this.producer = producer; + this.repository = repository; + this.restTemplate = restTemplate; + this.notificationUtil = notificationUtil; + } + @Value("${kafka.topics.notification.sms}") private String smsTopic; @@ -193,4 +203,26 @@ public Map> getLocalisedMessages(RequestInfo request return localizedMessageMap; } + /** + * Creates and sends email notification to the employees whose details are provided in the employeeRequest. + * + * @param employeeRequest The employee request with employee details. + */ + public void processEmailNotification(EmployeeRequest employeeRequest) { + if (employeeRequest == null || CollectionUtils.isEmpty(employeeRequest.getEmployees())) { + log.error("Invalid employee request received for email notification"); + return; + } + try { + // Fetch localization messages and get email message template for HEALTH_HRMS_EMAIL_LOCALIZATION_CODE template code. + String localizationMessages = notificationUtil.getLocalizationMessages(employeeRequest); + String messageTemplate = notificationUtil.getMessageTemplate(HEALTH_HRMS_EMAIL_LOCALIZATION_CODE, localizationMessages); + + // Create email requests from the employee details provided in the employeeRequest. + List emailRequests = notificationUtil.createEmailRequest(employeeRequest, messageTemplate); + notificationUtil.sendEmail(emailRequests); + } catch (Exception e) { + log.error("Error processing email notification for given employees"); + } + } } diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/HRMSConstants.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/HRMSConstants.java index 0d01b642d97..34ef1076fdb 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/HRMSConstants.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/HRMSConstants.java @@ -30,6 +30,8 @@ public class HRMSConstants { public static final String HRMS_EMP_CREATE_LOCLZN_CODE = "hrms.employee.create.notification"; public static final String HRMS_EMP_REACTIVATE_LOCLZN_CODE = "hrms.employee.reactivation.notification"; public static final String HRMS_LOCALIZATION_MODULE_CODE = "egov-hrms"; + public static final String HEALTH_HRMS_LOCALIZATION_MODULE_CODE = "rainmaker-hr"; + public static final String HEALTH_HRMS_EMAIL_LOCALIZATION_CODE = "HEALTH_HRMS_EMAIL_CODE"; public static final String HRMS_LOCALIZATION_ENG_LOCALE_CODE = "en_IN"; public static final String HRMS_TENANTBOUNDARY_HIERARCHY_JSONPATH = "$.TenantBoundary[?(@.boundary.code ==\"%s\")].hierarchyType.code"; public static final String HRMS_TENANTBOUNDARY_BOUNDARY_TYPE_JSONPATH ="$.TenantBoundary[?(@.hierarchyType.name==\"%1$s\" && @.boundary.code ==\"%2$s\")]..label"; @@ -39,6 +41,7 @@ public class HRMSConstants { public static final String HRMS_MDMS_CODE_FLITER = "[?(@.active == true)].code"; public static final String HRMS_USER_SEARCH_CRITERA_UUID = "uuid"; + public static final String HRMS_USER_SEARCH_CRITERA_USER_SERVICE_UUIDS = "userServiceUuids"; public static final String HRMS_USER_SEARCH_CRITERA_ROLECODES = "roleCodes"; public static final String HRMS_USER_SEARCH_CRITERA_TENANTID = "tenantId"; public static final String HRMS_USER_SEARCH_CRITERA_MOBILENO = "mobileNumber"; diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/NotificationUtil.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/NotificationUtil.java new file mode 100644 index 00000000000..22b8ea41493 --- /dev/null +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/utils/NotificationUtil.java @@ -0,0 +1,154 @@ +package org.egov.hrms.utils; + +import com.jayway.jsonpath.JsonPath; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.request.RequestInfo; +import org.egov.hrms.config.PropertiesManager; +import org.egov.hrms.model.Employee; +import org.egov.hrms.producer.HRMSProducer; +import org.egov.hrms.repository.RestCallRepository; +import org.egov.hrms.web.contract.Email; +import org.egov.hrms.web.contract.EmailRequest; +import org.egov.hrms.web.contract.EmployeeRequest; +import org.egov.hrms.web.contract.RequestInfoWrapper; +import org.json.JSONObject; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; + +import static org.egov.hrms.utils.HRMSConstants.*; + +@Slf4j +@Component +public class NotificationUtil { + + private RestCallRepository restCallRepository; + + private PropertiesManager propertiesManager; + + private HRMSProducer producer; + + public NotificationUtil(RestCallRepository restCallRepository, PropertiesManager propertiesManager, HRMSProducer producer) { + this.restCallRepository = restCallRepository; + this.propertiesManager = propertiesManager; + this.producer = producer; + } + + /** + * Extracts message for the specific code from the localization messages. + * + * @param notificationCode The code for which message is required. + * @param localizationMessage The localization messages. + * @return message for the specific code. + */ + public String getMessageTemplate(String notificationCode, String localizationMessage) { + // Create the path to get the message for the provided notification code from localization messages. + String path = "$..messages[?(@.code==\"{}\")].message"; + path = path.replace("{}", notificationCode); + String message = null; + try { + // Tries to get the message for the provided notificationCode. + List data = JsonPath.parse(localizationMessage).read(path); + if (!CollectionUtils.isEmpty(data)) + message = data.get(0).toString(); + else + log.error("Fetching from localization failed with code " + notificationCode); + } catch (Exception e) { + log.warn("Fetching from localization failed", e); + } + return message; + } + + /** + * Fetches all the localization messages from localization service. + * + * @param employeeRequest The employee request + * @return Localization messages for the module + */ + public String getLocalizationMessages(EmployeeRequest employeeRequest) { + + RequestInfoWrapper requestInfoWrapper = new RequestInfoWrapper(); + requestInfoWrapper.setRequestInfo(employeeRequest.getRequestInfo()); + + LinkedHashMap responseMap = (LinkedHashMap) restCallRepository.fetchResult(getUri(employeeRequest), requestInfoWrapper); + return new JSONObject(responseMap).toString(); + } + + /** + * Returns the search uri for the localization search to get localization messages from "rainmaker-hr" module. + * + * @param employeeRequest The employee request with locale code. + * @return The uri for localization search call. + */ + public StringBuilder getUri(EmployeeRequest employeeRequest) { + String tenantId = employeeRequest.getEmployees().get(0).getTenantId().split("\\.")[0]; + + String locale = HRMS_LOCALIZATION_ENG_LOCALE_CODE; + if (!StringUtils.isEmpty(employeeRequest.getRequestInfo().getMsgId()) && employeeRequest.getRequestInfo().getMsgId().split("|").length >= 2) + locale = employeeRequest.getRequestInfo().getMsgId().split("\\|")[1]; + + StringBuilder uri = new StringBuilder(); + uri.append(propertiesManager.getLocalizationHost()).append(propertiesManager.getLocalizationSearchEndpoint()) + .append("?").append("locale=").append(locale).append("&tenantId=").append(tenantId).append("&module=").append(HEALTH_HRMS_LOCALIZATION_MODULE_CODE); + + return uri; + } + + /** + * Replaces the placeholders from the email template and creates an email request from the given employee request. + * + * + * @param employeeRequest The employee request + * @param emailTemplate the email template + * @return The list of email requests + */ + public List createEmailRequest(EmployeeRequest employeeRequest, String emailTemplate) { + RequestInfo requestInfo = employeeRequest.getRequestInfo(); + + List emailRequest = new LinkedList<>(); + + // Iterate over each employee details and create email request for each employee. + for (Employee employee : employeeRequest.getEmployees()) { + String customizedMsg = emailTemplate.replace("{User's name}", employee.getUser().getName()); + customizedMsg = customizedMsg.replace("{Username}", employee.getCode()); + customizedMsg = customizedMsg.replace("{Password}", employee.getUser().getPassword()); + customizedMsg = customizedMsg.replace("{website URL}", propertiesManager.getEmailNotificationWebsiteLink()); + customizedMsg = customizedMsg.replace("{Implementation partner}", propertiesManager.getEmailNotificationImplementationPartner()); + + // Get email subject and email body from the provided email template. + String subject = customizedMsg.substring(customizedMsg.indexOf("

")+4, customizedMsg.indexOf("

")); + String body = customizedMsg.substring(customizedMsg.indexOf("")+5); + + // Create the email object with the employee's email id, subject and customized email body created. + Email emailObj = Email.builder().emailTo(Collections.singleton(employee.getUser().getEmailId())).isHTML(true).body(body).subject(subject).build(); + EmailRequest email = new EmailRequest(requestInfo, emailObj); + emailRequest.add(email); + } + return emailRequest; + } + + /** + * Pushes email request list into email notification topic if email notification is enabled. + * + * @param emailRequestList The list of emailRequests. + */ + public void sendEmail(List emailRequestList) { + if (propertiesManager.getIsEmailNotificationEnabled()) { + if (CollectionUtils.isEmpty(emailRequestList)) { + log.error("No Emails Found!"); + } else { + // Iterate over each email and push them into emailNotifTopic to send emails. + for (EmailRequest emailRequest : emailRequestList) { + producer.push(propertiesManager.getEmailNotifTopic(), emailRequest); + log.info("Email Request -> " + emailRequest.toString()); + log.info("EMAIL notification sent!"); + } + } + } + } +} diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/Email.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/Email.java new file mode 100644 index 00000000000..68c6011e4e4 --- /dev/null +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/Email.java @@ -0,0 +1,31 @@ +package org.egov.hrms.web.contract; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Set; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class Email { + + @NotNull + @Size(min = 1, message = "At least one recipient is required") + private Set emailTo; + + @NotBlank(message = "Subject is required") + private String subject; + + @NotBlank(message = "Body is required") + private String body; + @JsonProperty("isHTML") + private boolean isHTML; + +} diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmailRequest.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmailRequest.java new file mode 100644 index 00000000000..015b31217b1 --- /dev/null +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmailRequest.java @@ -0,0 +1,16 @@ +package org.egov.hrms.web.contract; + +import lombok.*; +import org.egov.common.contract.request.RequestInfo; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Setter +@Getter +@ToString +public class EmailRequest { + + private RequestInfo requestInfo; + private Email email; +} diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeResponse.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeResponse.java index 6fb5141aeed..df8fb1538e5 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeResponse.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeResponse.java @@ -54,6 +54,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; + @Builder @AllArgsConstructor @EqualsAndHashCode @@ -69,4 +70,8 @@ public class EmployeeResponse { @JsonProperty("Employees") private List employees; + @JsonProperty("TotalCount") + @Builder.Default + private Long totalCount = 0L; + } \ No newline at end of file diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeSearchCriteria.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeSearchCriteria.java index f5a3f9dfca6..052f98a7072 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeSearchCriteria.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/EmployeeSearchCriteria.java @@ -23,38 +23,40 @@ @Builder public class EmployeeSearchCriteria { - public List codes; + private List codes; - public List names; + private List names; - public List departments; + private List departments; - public List designations; + private List designations; - public Long asOnDate; + private Long asOnDate; - public List roles; + private List roles; - public List ids; + private List ids; - public List employeestatuses; + private List employeestatuses; - public List employeetypes; + private List employeetypes; - public List uuids; + private List uuids; + + private List userServiceUuids; - public List positions; + private List positions; - public Boolean isActive; + private Boolean isActive; @Size(max = 250) - public String tenantId; + private String tenantId; - public String phone; + private String phone; - public Integer offset; + private Integer offset; - public Integer limit; + private Integer limit; private Boolean includeUnassigned = false; diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/UserResponse.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/UserResponse.java index 285964a0a87..ea727df4cbd 100644 --- a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/UserResponse.java +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/contract/UserResponse.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Builder; import org.egov.common.contract.response.ResponseInfo; @@ -66,4 +67,8 @@ public class UserResponse { private List user = new ArrayList(); + @JsonIgnore + @Builder.Default + private Long totalCount = 0L; + } \ No newline at end of file diff --git a/core-services/egov-hrms/src/main/java/org/egov/hrms/web/models/IndividualBulkResponse.java b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/models/IndividualBulkResponse.java new file mode 100644 index 00000000000..845d6cb72d0 --- /dev/null +++ b/core-services/egov-hrms/src/main/java/org/egov/hrms/web/models/IndividualBulkResponse.java @@ -0,0 +1,54 @@ +package org.egov.hrms.web.models; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.egov.common.models.individual.Individual; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * IndividualBulkResponse + */ +@Validated + + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class IndividualBulkResponse { + + @JsonProperty("ResponseInfo") + @NotNull + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("TotalCount") + @Valid + @Builder.Default + private Long totalCount = 0L; + + @JsonProperty("Individual") + @Valid + private List individual = null; + + public IndividualBulkResponse addIndividualItem(Individual individualItem) { + if (this.individual == null) { + this.individual = new ArrayList<>(); + } + this.individual.add(individualItem); + return this; + } + +} diff --git a/core-services/egov-hrms/src/main/resources/application.properties b/core-services/egov-hrms/src/main/resources/application.properties index dcece925352..6333f704295 100644 --- a/core-services/egov-hrms/src/main/resources/application.properties +++ b/core-services/egov-hrms/src/main/resources/application.properties @@ -65,6 +65,7 @@ egov.individual.host=https://health-dev.digit.org egov.individual.create.endpoint=/individual/v1/_create egov.individual.update.endpoint=/individual/v1/_update egov.individual.search.endpoint=/individual/v1/_search +egov.individual.delete.endpoint=/individual/v1/_delete # use qualifier as "defaultUserService" to integrate with egov-user module # use qualifier as "individualService" to integrate with individual module @@ -101,7 +102,9 @@ spring.kafka.producer.value-serializer=org.springframework.kafka.support.seriali kafka.topics.save.service=save-hrms-employee kafka.topics.update.service=update-hrms-employee kafka.topics.notification.sms=egov.core.notification.sms +kafka.topics.notification.email=egov.core.notification.email kafka.topics.hrms.updateData= egov-hrms-update +kafka.topics.hrms.email.notification=hrms-send-email-notification spring.kafka.listener.missing-topics-fatal=false @@ -117,6 +120,12 @@ state.level.tenant.id=default egov.hrms.auto.generate.password=true +# EMAIL NOTIFICATION CONFIG +notification.email.enabled=true + +hrms.email.notification.implementation.partner=NMCP Mozambique +hrms.email.notification.website.link=https://unified-qa.digit.org/microplan-ui + # BOUNDARY SERVICE egov.boundary.host=http://localhost:8081 egov.boundary.search.url=/boundary-service/boundary/_search diff --git a/health-services/household/CHANGELOG.md b/health-services/household/CHANGELOG.md index 3b9cbec0526..d8d6e6d5019 100644 --- a/health-services/household/CHANGELOG.md +++ b/health-services/household/CHANGELOG.md @@ -1,5 +1,10 @@ All notable changes to this module will be documented in this file. +## 1.1.5 - 2025-01-28 + +- Added householdType column in household table +- Upgraded to heath models 1.0.25 + ## 1.1.4 - 2024-08-29 - Added `ExistentEntityValidator` fixes diff --git a/health-services/household/pom.xml b/health-services/household/pom.xml index 798eba22c92..0ad08feb096 100644 --- a/health-services/household/pom.xml +++ b/health-services/household/pom.xml @@ -5,7 +5,7 @@ household jar household - 1.1.4 + 1.1.5 17 ${java.version} @@ -45,12 +45,12 @@ org.egov.common health-services-common - 1.0.18-SNAPSHOT + 1.0.19-SNAPSHOT org.egov.common health-services-models - 1.0.20-SNAPSHOT + 1.0.25-dev-SNAPSHOT compile diff --git a/health-services/household/src/main/java/org/egov/household/config/HouseholdConfiguration.java b/health-services/household/src/main/java/org/egov/household/config/HouseholdConfiguration.java index c906d52786f..6baa26b172d 100644 --- a/health-services/household/src/main/java/org/egov/household/config/HouseholdConfiguration.java +++ b/health-services/household/src/main/java/org/egov/household/config/HouseholdConfiguration.java @@ -42,4 +42,10 @@ public class HouseholdConfiguration { @Value("${egov.boundary.search.url}") private String boundarySearchUrl; + + @Value("${household.type.same.validation}") + private boolean householdTypeSameValidation; + + @Value("${household.type.community.creator.role}") + private String communityHouseholdCreatorRoleCode; } diff --git a/health-services/household/src/main/java/org/egov/household/repository/HouseholdRepository.java b/health-services/household/src/main/java/org/egov/household/repository/HouseholdRepository.java index 55a0cee69e1..e61717fce37 100644 --- a/health-services/household/src/main/java/org/egov/household/repository/HouseholdRepository.java +++ b/health-services/household/src/main/java/org/egov/household/repository/HouseholdRepository.java @@ -82,6 +82,10 @@ public SearchResponse find(HouseholdSearch searchObject, Integer limi query = GenericQueryBuilder.generateQuery(query, whereFields).toString(); query = query.replace("id IN (:id)", "h.id IN (:id)"); query = query.replace("clientReferenceId IN (:clientReferenceId)", "h.clientReferenceId IN (:clientReferenceId)"); + // To consider null values present in db as family if family parameter is passed + if (searchObject.getHouseholdType() != null && searchObject.getHouseholdType().equalsIgnoreCase("FAMILY")) { + query = query.replace("householdType=:householdType", "(householdType!='COMMUNITY' OR householdType IS NULL)"); + } if(CollectionUtils.isEmpty(whereFields)) { query = query + " where h.tenantId=:tenantId "; @@ -129,6 +133,10 @@ public SearchResponse findByRadius(HouseholdSearch searchObject, Inte query = GenericQueryBuilder.generateQuery(query, whereFields).toString(); query = query.replace("id IN (:id)", "h.id IN (:id)"); query = query.replace("clientReferenceId IN (:clientReferenceId)", "h.clientReferenceId IN (:clientReferenceId)"); + // To consider null values present in db as family if family parameter is passed + if (searchObject.getHouseholdType() != null && searchObject.getHouseholdType().equalsIgnoreCase("FAMILY")) { + query = query.replace("householdType=:householdType", "(householdType!='COMMUNITY' OR householdType IS NULL)"); + } if(CollectionUtils.isEmpty(whereFields)) { query = query + " where h.tenantId=:tenantId "; diff --git a/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdMemberRowMapper.java b/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdMemberRowMapper.java index 79a762807ea..547312ec86c 100644 --- a/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdMemberRowMapper.java +++ b/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdMemberRowMapper.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import digit.models.coremodels.AuditDetails; +import org.egov.common.contract.models.AuditDetails; import org.egov.common.models.core.AdditionalFields; import org.egov.common.models.household.HouseholdMember; import org.springframework.jdbc.core.RowMapper; diff --git a/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdRowMapper.java b/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdRowMapper.java index be08e68e1d1..1cd6814ea73 100644 --- a/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdRowMapper.java +++ b/health-services/household/src/main/java/org/egov/household/repository/rowmapper/HouseholdRowMapper.java @@ -5,11 +5,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import digit.models.coremodels.AuditDetails; +import org.egov.common.contract.models.AuditDetails; import org.egov.common.models.core.AdditionalFields; import org.egov.common.models.household.Address; import org.egov.common.models.household.AddressType; import org.egov.common.models.core.Boundary; +import org.egov.common.models.household.HouseHoldType; import org.egov.common.models.household.Household; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; @@ -36,6 +37,7 @@ public Household mapRow(ResultSet resultSet, int i) throws SQLException { Household household = Household.builder() .id(resultSet.getString("id")) .rowVersion(resultSet.getInt("rowVersion")) + .householdType(HouseHoldType.fromValue(resultSet.getString("householdType"))) .isDeleted(resultSet.getBoolean("isDeleted")) .tenantId(resultSet.getString("tenantId")) .memberCount(resultSet.getInt("numberOfMembers")) diff --git a/health-services/household/src/main/java/org/egov/household/service/HouseholdEnrichmentService.java b/health-services/household/src/main/java/org/egov/household/service/HouseholdEnrichmentService.java index a2ce8dc5e9d..b31ec077888 100644 --- a/health-services/household/src/main/java/org/egov/household/service/HouseholdEnrichmentService.java +++ b/health-services/household/src/main/java/org/egov/household/service/HouseholdEnrichmentService.java @@ -1,6 +1,6 @@ package org.egov.household.service; -import digit.models.coremodels.AuditDetails; +import org.egov.common.contract.models.AuditDetails; import lombok.extern.slf4j.Slf4j; import org.egov.common.models.household.Address; import org.egov.common.models.household.Household; diff --git a/health-services/household/src/main/java/org/egov/household/service/HouseholdService.java b/health-services/household/src/main/java/org/egov/household/service/HouseholdService.java index c6f701b6eb0..9b7f35b8f93 100644 --- a/health-services/household/src/main/java/org/egov/household/service/HouseholdService.java +++ b/health-services/household/src/main/java/org/egov/household/service/HouseholdService.java @@ -14,13 +14,7 @@ import org.egov.common.validator.Validator; import org.egov.household.config.HouseholdConfiguration; import org.egov.household.repository.HouseholdRepository; -import org.egov.household.validators.household.HExistentEntityValidator; -import org.egov.household.validators.household.HBoundaryValidator; -import org.egov.household.validators.household.HIsDeletedValidator; -import org.egov.household.validators.household.HNonExistentEntityValidator; -import org.egov.household.validators.household.HNullIdValidator; -import org.egov.household.validators.household.HRowVersionValidator; -import org.egov.household.validators.household.HUniqueEntityValidator; +import org.egov.household.validators.household.*; import org.egov.common.models.household.HouseholdSearch; import org.egov.tracer.model.CustomException; import org.springframework.beans.factory.annotation.Autowired; @@ -28,7 +22,6 @@ import org.springframework.util.ReflectionUtils; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Predicate; @@ -62,7 +55,9 @@ public class HouseholdService { private final Predicate> isApplicableForCreate = validator -> validator.getClass().equals(HBoundaryValidator.class) - || validator.getClass().equals(HExistentEntityValidator.class); + || validator.getClass().equals(HExistentEntityValidator.class) + || validator.getClass().equals(HCommunityValidator.class) + || validator.getClass().equals(HCommunityTypeValidator.class); private final Predicate> isApplicableForUpdate = validator -> validator.getClass().equals(HNullIdValidator.class) @@ -70,7 +65,10 @@ public class HouseholdService { || validator.getClass().equals(HIsDeletedValidator.class) || validator.getClass().equals(HUniqueEntityValidator.class) || validator.getClass().equals(HNonExistentEntityValidator.class) - || validator.getClass().equals(HRowVersionValidator.class); + || validator.getClass().equals(HRowVersionValidator.class) + || validator.getClass().equals(HCommunityValidator.class) + || validator.getClass().equals(HCommunityTypeValidator.class) + || validator.getClass().equals(HHouseholdTypeChangeValidator.class); private final Predicate> isApplicableForDelete = validator -> validator.getClass().equals(HNullIdValidator.class) diff --git a/health-services/household/src/main/java/org/egov/household/validators/household/HCommunityTypeValidator.java b/health-services/household/src/main/java/org/egov/household/validators/household/HCommunityTypeValidator.java new file mode 100644 index 00000000000..9e76039121b --- /dev/null +++ b/health-services/household/src/main/java/org/egov/household/validators/household/HCommunityTypeValidator.java @@ -0,0 +1,53 @@ +package org.egov.household.validators.household; + +import org.egov.common.models.Error; +import org.egov.common.models.household.HouseHoldType; +import org.egov.common.models.household.Household; +import org.egov.common.models.household.HouseholdBulkRequest; +import org.egov.common.validator.Validator; +import org.egov.household.config.HouseholdConfiguration; +import org.egov.tracer.model.CustomException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.common.utils.CommonUtils.populateErrorDetails; + +public class HCommunityTypeValidator implements Validator { + private final HouseholdConfiguration configuration; + + @Autowired + public HCommunityTypeValidator(HouseholdConfiguration configuration) { + this.configuration = configuration; + } + @Override + public Map> validate(HouseholdBulkRequest request) { + HashMap> errorDetailsMap = new HashMap<>(); + if (configuration.isHouseholdTypeSameValidation()) { + // validate if request contains households of different householdTypes + List communityHouseholds = request.getHouseholds() + .stream() + .filter(household -> household.getHouseholdType() != null && + household.getHouseholdType().equals(HouseHoldType.COMMUNITY)) + .toList(); + + if (!CollectionUtils.isEmpty(communityHouseholds) && + request.getHouseholds().size() != communityHouseholds.size()) { + communityHouseholds.forEach(household -> { + Error error = Error.builder() + .errorMessage("Community and Family household cannot be in same request") + .errorCode("COMMUNITY_AND_FAMILY_HOUSEHOLD_IN_SAME_REQUEST") + .type(Error.ErrorType.NON_RECOVERABLE) + .exception(new CustomException("COMMUNITY_AND_FAMILY_HOUSEHOLD_IN_SAME_REQUEST", "Community and Family household cannot be in same request")) + .build(); + // Populate error details for the household + populateErrorDetails(household, error, errorDetailsMap); + }); + } + } + return errorDetailsMap; + } +} diff --git a/health-services/household/src/main/java/org/egov/household/validators/household/HCommunityValidator.java b/health-services/household/src/main/java/org/egov/household/validators/household/HCommunityValidator.java new file mode 100644 index 00000000000..005afb42e3c --- /dev/null +++ b/health-services/household/src/main/java/org/egov/household/validators/household/HCommunityValidator.java @@ -0,0 +1,62 @@ +package org.egov.household.validators.household; + +import lombok.extern.slf4j.Slf4j; +import org.egov.common.models.Error; +import org.egov.common.models.household.HouseHoldType; +import org.egov.common.models.household.Household; +import org.egov.common.models.household.HouseholdBulkRequest; +import org.egov.common.validator.Validator; +import org.egov.household.config.HouseholdConfiguration; +import org.egov.tracer.model.CustomException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.common.utils.CommonUtils.populateErrorDetails; + +@Component +@Order(value = 1) +@Slf4j +public class HCommunityValidator implements Validator { + + private final HouseholdConfiguration configuration; + + @Autowired + public HCommunityValidator(HouseholdConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public Map> validate(HouseholdBulkRequest request) { + HashMap> errorDetailsMap = new HashMap<>(); + List communityHouseholds = request.getHouseholds() + .stream() + .filter(household -> household.getHouseholdType() != null && + household.getHouseholdType().equals(HouseHoldType.COMMUNITY)) + .toList(); + + if (!CollectionUtils.isEmpty(communityHouseholds) && + request.getRequestInfo().getUserInfo().getRoles() + .stream() + .noneMatch(role -> role.getCode().equals(configuration.getCommunityHouseholdCreatorRoleCode()))) { + communityHouseholds.forEach(household -> { + Error error = Error.builder() + .errorMessage("User doesn't have permission to create/update community household") + .errorCode("COMMUNITY_USER_ACCESS_DENIED") + .type(Error.ErrorType.NON_RECOVERABLE) + .exception(new CustomException("COMMUNITY_USER_ACCESS_DENIED", "User doesn't have permission to create/update community household")) + .build(); + // Populate error details for the household + populateErrorDetails(household, error, errorDetailsMap); + }); + } + return errorDetailsMap; + } + + +} diff --git a/health-services/household/src/main/java/org/egov/household/validators/household/HHouseholdTypeChangeValidator.java b/health-services/household/src/main/java/org/egov/household/validators/household/HHouseholdTypeChangeValidator.java new file mode 100644 index 00000000000..483a11abba3 --- /dev/null +++ b/health-services/household/src/main/java/org/egov/household/validators/household/HHouseholdTypeChangeValidator.java @@ -0,0 +1,97 @@ +package org.egov.household.validators.household; + +import lombok.extern.slf4j.Slf4j; +import org.egov.common.models.Error; +import org.egov.common.models.household.Household; +import org.egov.common.models.household.HouseholdBulkRequest; +import org.egov.common.models.household.HouseholdSearch; +import org.egov.common.validator.Validator; +import org.egov.household.repository.HouseholdRepository; +import org.egov.tracer.model.CustomException; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.egov.common.utils.CommonUtils.*; +import static org.egov.common.utils.CommonUtils.notHavingErrors; + +@Slf4j +@Component +@Order(value = 3) +public class HHouseholdTypeChangeValidator implements Validator { + + private final HouseholdRepository householdRepository; + + public HHouseholdTypeChangeValidator(HouseholdRepository householdRepository) { + this.householdRepository = householdRepository; + } + + @Override + public Map> validate(HouseholdBulkRequest request) { + // Map to hold household entities and their error details + Map> errorDetailsMap = new HashMap<>(); + // Get the list of household entities from the request + List entities = request.getHouseholds(); + // Map to store entities by their IDs + Map eMap = entities.stream().filter(notHavingErrors()).collect(Collectors.toMap(Household::getId, household -> household)); + // Lists to store IDs and client reference IDs + List idList = new ArrayList<>(); + List clientReferenceIdList = new ArrayList<>(); + // Extract IDs and client reference IDs from household entities + entities.forEach(household -> { + idList.add(household.getId()); + clientReferenceIdList.add(household.getClientReferenceId()); + }); + // Check if the entity map is not empty + if (!eMap.isEmpty()) { + // Create a search object for querying existing entities + HouseholdSearch householdSearch = HouseholdSearch.builder() + .clientReferenceId(clientReferenceIdList) + .id(idList) + .build(); + + List existingEntities; + try { + // Query the repository to find existing entities + existingEntities = householdRepository.find(householdSearch, entities.size(), 0, + entities.get(0).getTenantId(), null, false).getResponse(); + } catch (Exception e) { + // Handle query builder exception + log.error("Search failed for Household with error: {}", e.getMessage(), e); + throw new CustomException("HOUSEHOLD_SEARCH_FAILED", "Search Failed for Household, " + e.getMessage()); + } + // Check for non-existent entities + List entitiesWithHouseholdTypeChange = changeInHouseholdType(eMap, + existingEntities); + // Populate error details for non-existent entities + entitiesWithHouseholdTypeChange.forEach(entity -> { + Error error = Error.builder().errorMessage("Household Type change").errorCode("HOUSEHOLD_TYPE_CHANGE") + .type(Error.ErrorType.NON_RECOVERABLE) + .exception(new CustomException("HOUSEHOLD_TYPE_CHANGE", "Household Type change")).build(); + populateErrorDetails(entity, error, errorDetailsMap); + }); + } + + return errorDetailsMap; + } + + + private List changeInHouseholdType(Map eMap, + List existingEntities) { + List entitiesWithHouseholdTypeChange = new ArrayList<>(); + + for (Household existingEntity : existingEntities) { + if (eMap.containsKey(existingEntity.getId())) { + if (!existingEntity.getHouseholdType().equals(eMap.get(existingEntity.getId()).getHouseholdType())) { + entitiesWithHouseholdTypeChange.add(eMap.get(existingEntity.getId())); + } + } + } + return entitiesWithHouseholdTypeChange; + } +} diff --git a/health-services/household/src/main/resources/application.properties b/health-services/household/src/main/resources/application.properties index dfd2b4f6937..c5fc6bdeaee 100644 --- a/health-services/household/src/main/resources/application.properties +++ b/health-services/household/src/main/resources/application.properties @@ -103,3 +103,7 @@ egov.individual.search.url=/individual/v1/_search egov.boundary.host=http://localhost:8081 egov.boundary.search.url=/boundary-service/boundary/_search egov.boundary.hierarchy=HCM-Moz-Hierarchy + +#Community Household Type +household.type.same.validation=true +household.type.community.creator.role=COMMUNITY_CREATOR diff --git a/health-services/household/src/main/resources/db/migration/main/V20241114114225__household_type_ddl.sql b/health-services/household/src/main/resources/db/migration/main/V20241114114225__household_type_ddl.sql new file mode 100644 index 00000000000..4f98c9fad80 --- /dev/null +++ b/health-services/household/src/main/resources/db/migration/main/V20241114114225__household_type_ddl.sql @@ -0,0 +1 @@ +ALTER TABLE HOUSEHOLD ADD COLUMN IF NOT EXISTS householdType character varying(64) DEFAULT 'FAMILY' NOT NULL; \ No newline at end of file diff --git a/health-services/household/src/main/resources/household-persister.yml b/health-services/household/src/main/resources/household-persister.yml index f86a803b8ce..54e066b3e94 100644 --- a/health-services/household/src/main/resources/household-persister.yml +++ b/health-services/household/src/main/resources/household-persister.yml @@ -6,12 +6,13 @@ serviceMaps: fromTopic: save-household-topic isTransaction: true queryMaps: - - query: INSERT INTO HOUSEHOLD(id, tenantId, clientReferenceId, numberOfMembers, addressId, additionalDetails, createdBy, lastModifiedBy, createdTime, lastModifiedTime, rowVersion, isDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + - query: INSERT INTO HOUSEHOLD(id, tenantId, clientReferenceId, householdType, numberOfMembers, addressId, additionalDetails, createdBy, lastModifiedBy, createdTime, lastModifiedTime, rowVersion, isDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); basePath: $.* jsonMaps: - jsonPath: $.*.id - jsonPath: $.*.tenantId - jsonPath: $.*.clientReferenceId + - jsonPath: $.*.householdType - jsonPath: $.*.memberCount - jsonPath: $.*.address.id - jsonPath: $.*.additionalFields diff --git a/health-services/individual/src/main/java/org/egov/individual/repository/IndividualRepository.java b/health-services/individual/src/main/java/org/egov/individual/repository/IndividualRepository.java index 703b5521652..8b9ef49d763 100644 --- a/health-services/individual/src/main/java/org/egov/individual/repository/IndividualRepository.java +++ b/health-services/individual/src/main/java/org/egov/individual/repository/IndividualRepository.java @@ -98,7 +98,7 @@ public SearchResponse find(IndividualSearch searchObject, Integer li return findByRadius(query, searchObject, includeDeleted, paramsMap); } if (searchObject.getIdentifier() == null) { - String queryWithoutLimit = query.replace("ORDER BY id ASC LIMIT :limit OFFSET :offset", ""); + String queryWithoutLimit = query.replace("ORDER BY createdtime DESC LIMIT :limit OFFSET :offset", ""); Long totalCount = constructTotalCountCTEAndReturnResult(queryWithoutLimit, paramsMap, this.namedParameterJdbcTemplate); List individuals = this.namedParameterJdbcTemplate.query(query, paramsMap, this.rowMapper); if (!individuals.isEmpty()) { @@ -226,7 +226,7 @@ private String getQueryForIndividual(IndividualSearch searchObject, Integer limi query = query.replace(tableName + " AND", tableName + " WHERE "); } if (searchObject.getIndividualName() != null) { - query = query + "AND givenname LIKE :individualName "; + query = query + "AND givenname ILIKE :individualName "; paramsMap.put("individualName", "%"+searchObject.getIndividualName()+"%"); } if (searchObject.getGender() != null) { diff --git a/health-services/libraries/health-services-models/CHANGELOG.md b/health-services/libraries/health-services-models/CHANGELOG.md index c7476cbe79b..e08569ca889 100644 --- a/health-services/libraries/health-services-models/CHANGELOG.md +++ b/health-services/libraries/health-services-models/CHANGELOG.md @@ -1,5 +1,11 @@ All notable changes to this module will be documented in this file. +## 1.0.25 - 2025-01-03 +- Added BeneficiaryType Enum and update in ProjectType, Target models +- Added HouseHoldType Enum and added in Household, HouseholdSearch models +- Added DownsyncCLFHousehold, DownsyncCLFHouseholdResponse, HouseholdMemberMap +- Updated DownsyncCriteria to include householdId + ## 1.0.21 - 2024-08-07 - Added UserActionEnum, UserAction Entities, TaskStatus enum - Added isCascadingProjectDateUpdate in ProjectRequest model diff --git a/health-services/libraries/health-services-models/pom.xml b/health-services/libraries/health-services-models/pom.xml index dc4e4c878ca..4a110e3b9a2 100644 --- a/health-services/libraries/health-services-models/pom.xml +++ b/health-services/libraries/health-services-models/pom.xml @@ -5,7 +5,7 @@ 4.0.0 org.egov.common health-services-models - 1.0.23-SNAPSHOT + 1.0.25-SNAPSHOT 17 ${java.version} @@ -84,4 +84,4 @@ https://nexus-repo.digit.org/nexus/content/repositories/snapshots/ - \ No newline at end of file + diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseHoldType.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseHoldType.java new file mode 100644 index 00000000000..77948226c61 --- /dev/null +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseHoldType.java @@ -0,0 +1,31 @@ +package org.egov.common.models.household; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public enum HouseHoldType { + + FAMILY("FAMILY"), + + COMMUNITY("COMMUNITY"), + + OTHER("OTHER"); + + private String value; + + HouseHoldType(String value) { + this.value = value; + } + + @JsonCreator + public static HouseHoldType fromValue(String text) { + for (HouseHoldType b : HouseHoldType.values()) { + if (String.valueOf(b.value).equalsIgnoreCase(text)) { + return b; + } + } + return null; + } + +} \ No newline at end of file diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/Household.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/Household.java index 63c982ff07f..f43bc53e2f2 100644 --- a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/Household.java +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/Household.java @@ -27,13 +27,16 @@ public class Household extends EgovOfflineModel { @JsonProperty("memberCount") @NotNull - @Range(min = 0, max = 1000) +// @Range(min = 0, max = 1000) private Integer memberCount = null; @JsonProperty("address") @Valid private Address address = null; + @JsonProperty("householdType") + private HouseHoldType householdType = null; + //TODO remove @JsonProperty("isDeleted") private Boolean isDeleted = Boolean.FALSE; diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseholdSearch.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseholdSearch.java index 4ff5ccec51e..a6d754e3b99 100644 --- a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseholdSearch.java +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/household/HouseholdSearch.java @@ -37,6 +37,9 @@ public class HouseholdSearch extends EgovOfflineSearchModel { @DecimalMax("180") private Double longitude = null; + @JsonProperty("householdType") + private String householdType = null; + /* * @value unit of measurement in Kilometer * */ diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/BeneficiaryType.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/BeneficiaryType.java new file mode 100644 index 00000000000..3836a8c5168 --- /dev/null +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/BeneficiaryType.java @@ -0,0 +1,35 @@ +package org.egov.common.models.project; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + + +public enum BeneficiaryType { + + HOUSEHOLD("HOUSEHOLD"), + + INDIVIDUAL("INDIVIDUAL"); + + private String value; + + BeneficiaryType(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static BeneficiaryType fromValue(String text) { + for(BeneficiaryType a:BeneficiaryType.values()){ + if(String.valueOf(a.value).equalsIgnoreCase(text)){ + return a; + } + } + + return null; + } +} \ No newline at end of file diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/ProjectType.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/ProjectType.java index 78445bed4a9..6f781df0f5a 100644 --- a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/ProjectType.java +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/ProjectType.java @@ -48,7 +48,7 @@ public class ProjectType { private String group = null; @JsonProperty("beneficiaryType") - private String beneficiaryType = null; + private BeneficiaryType beneficiaryType = null; @JsonProperty("eligibilityCriteria") @Size(max=10) diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/Target.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/Target.java index e7152211048..187648724a6 100644 --- a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/Target.java +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/project/Target.java @@ -31,7 +31,7 @@ public class Target { private String projectid = null; @JsonProperty("beneficiaryType") - private String beneficiaryType = null; + private BeneficiaryType beneficiaryType = null; @JsonProperty("totalNo") private Integer totalNo = null; diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCLFHousehold.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCLFHousehold.java new file mode 100644 index 00000000000..c0ccef312fe --- /dev/null +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCLFHousehold.java @@ -0,0 +1,24 @@ +package org.egov.common.models.referralmanagement.beneficiarydownsync; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.models.household.Household; + +import java.util.List; +import java.util.Map; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder + +public class DownsyncCLFHousehold { + @JsonProperty("HouseholdCountMap") + List householdMemberCountMap; + + @JsonProperty("DownsyncCriteria") + DownsyncCriteria downsyncCriteria; +} diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCLFHouseholdResponse.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCLFHouseholdResponse.java new file mode 100644 index 00000000000..97a3023163e --- /dev/null +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCLFHouseholdResponse.java @@ -0,0 +1,24 @@ +package org.egov.common.models.referralmanagement.beneficiarydownsync; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder + +public class DownsyncCLFHouseholdResponse { + + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo; + + @JsonProperty("Households") + private DownsyncCLFHousehold Households; +} diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCriteria.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCriteria.java index 71816f7849f..400de52948b 100644 --- a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCriteria.java +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/DownsyncCriteria.java @@ -35,5 +35,7 @@ public class DownsyncCriteria { private Integer limit = 50; private Long totalCount; + + private String householdId; } diff --git a/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/HouseholdMemberMap.java b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/HouseholdMemberMap.java new file mode 100644 index 00000000000..ee6819f59ec --- /dev/null +++ b/health-services/libraries/health-services-models/src/main/java/org/egov/common/models/referralmanagement/beneficiarydownsync/HouseholdMemberMap.java @@ -0,0 +1,20 @@ +package org.egov.common.models.referralmanagement.beneficiarydownsync; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.models.household.Household; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class HouseholdMemberMap { + + private Household household; + + private Integer numberOfMembers; + + +} \ No newline at end of file diff --git a/health-services/project/CHANGELOG.md b/health-services/project/CHANGELOG.md index 95369041416..c995b81daaf 100644 --- a/health-services/project/CHANGELOG.md +++ b/health-services/project/CHANGELOG.md @@ -1,5 +1,8 @@ All notable changes to this module will be documented in this file. +## 1.1.6 - 2025-01-27 +- Added isAncestorProjectId param for search projects API to support search projects with ancestor project id as well + ## 1.1.5 - 2024-08-07 - Added UserAction functionality with support for Location capture. diff --git a/health-services/project/pom.xml b/health-services/project/pom.xml index 9cb321d57d0..3333388355c 100644 --- a/health-services/project/pom.xml +++ b/health-services/project/pom.xml @@ -5,7 +5,7 @@ project jar project - 1.1.5 + 1.1.6 17 ${java.version} @@ -45,12 +45,12 @@ org.egov.common health-services-common - 1.0.20-dev-SNAPSHOT + 1.0.20-SNAPSHOT org.egov.common health-services-models - 1.0.23-dev-SNAPSHOT + 1.0.25-SNAPSHOT compile diff --git a/health-services/project/src/main/java/org/egov/project/repository/ProjectRepository.java b/health-services/project/src/main/java/org/egov/project/repository/ProjectRepository.java index ae7b4c9994b..31bd571cb77 100644 --- a/health-services/project/src/main/java/org/egov/project/repository/ProjectRepository.java +++ b/health-services/project/src/main/java/org/egov/project/repository/ProjectRepository.java @@ -69,37 +69,44 @@ public ProjectRepository(Producer producer, NamedParameterJdbcTemplate namedPara } - public List getProjects(ProjectRequest project, Integer limit, Integer offset, String tenantId, Long lastChangedSince, Boolean includeDeleted, Boolean includeAncestors, Boolean includeDescendants, Long createdFrom, Long createdTo) { + /** + * @param isAncestorProjectId When true, treats the project IDs in the ProjectRequest as ancestor project IDs + */ + public List getProjects(ProjectRequest project, Integer limit, Integer offset, String tenantId, Long lastChangedSince, Boolean includeDeleted, Boolean includeAncestors, Boolean includeDescendants, Long createdFrom, Long createdTo, boolean isAncestorProjectId) { //Fetch Projects based on search criteria - List projects = getProjectsBasedOnSearchCriteria(project.getProjects(), limit, offset, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo); + List projects = getProjectsBasedOnSearchCriteria(project.getProjects(), limit, offset, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, isAncestorProjectId); Set projectIds = projects.stream().map(Project :: getId).collect(Collectors.toSet()); List ancestors = null; List descendants = null; - //Get Project ancestors if includeAncestors flag is true - if (includeAncestors) { - ancestors = getProjectAncestors(projects); - if (ancestors != null && !ancestors.isEmpty()) { - List ancestorProjectIds = ancestors.stream().map(Project :: getId).collect(Collectors.toList()); - projectIds.addAll(ancestorProjectIds); + List targets = new ArrayList<>(); + List documents = new ArrayList<>(); + if(!projectIds.isEmpty()) { + //Get Project ancestors if includeAncestors flag is true + if (includeAncestors) { + ancestors = getProjectAncestors(projects); + if (ancestors != null && !ancestors.isEmpty()) { + List ancestorProjectIds = ancestors.stream().map(Project :: getId).collect(Collectors.toList()); + projectIds.addAll(ancestorProjectIds); + } } - } - //Get Project descendants if includeDescendants flag is true - if (includeDescendants) { - descendants = getProjectDescendants(projects); - if (descendants != null && !descendants.isEmpty()) { - List descendantsProjectIds = descendants.stream().map(Project :: getId).collect(Collectors.toList()); - projectIds.addAll(descendantsProjectIds); + //Get Project descendants if includeDescendants flag is true + if (includeDescendants) { + descendants = getProjectDescendants(projects); + if (descendants != null && !descendants.isEmpty()) { + List descendantsProjectIds = descendants.stream().map(Project :: getId).collect(Collectors.toList()); + projectIds.addAll(descendantsProjectIds); + } } - } - //Fetch targets based on Project Ids - List targets = getTargetsBasedOnProjectIds(projectIds); + //Fetch targets based on Project Ids + targets = getTargetsBasedOnProjectIds(projectIds); - //Fetch documents based on Project Ids - List documents = getDocumentsBasedOnProjectIds(projectIds); + //Fetch documents based on Project Ids + documents = getDocumentsBasedOnProjectIds(projectIds); + } //Construct Project Objects with fetched projects, targets and documents using Project id return buildProjectSearchResult(projects, targets, documents, ancestors, descendants); @@ -114,28 +121,32 @@ public List getProjects(@NotNull @Valid ProjectSearch projectSearch, @V List ancestors = null; List descendants = null; - //Get Project ancestors if includeAncestors flag is true - if (urlParams.getIncludeAncestors()) { - ancestors = getProjectAncestors(projects); - if (ancestors != null && !ancestors.isEmpty()) { - List ancestorProjectIds = ancestors.stream().map(Project :: getId).collect(Collectors.toList()); - projectIds.addAll(ancestorProjectIds); + List targets = new ArrayList<>(); + List documents = new ArrayList<>(); + if(!projectIds.isEmpty()) { + //Get Project ancestors if includeAncestors flag is true + if (urlParams.getIncludeAncestors()) { + ancestors = getProjectAncestors(projects); + if (ancestors != null && !ancestors.isEmpty()) { + List ancestorProjectIds = ancestors.stream().map(Project :: getId).toList(); + projectIds.addAll(ancestorProjectIds); + } } - } - //Get Project descendants if includeDescendants flag is true - if (urlParams.getIncludeDescendants()) { - descendants = getProjectDescendants(projects); - if (descendants != null && !descendants.isEmpty()) { - List descendantsProjectIds = descendants.stream().map(Project :: getId).collect(Collectors.toList()); - projectIds.addAll(descendantsProjectIds); + //Get Project descendants if includeDescendants flag is true + if (urlParams.getIncludeDescendants()) { + descendants = getProjectDescendants(projects); + if (descendants != null && !descendants.isEmpty()) { + List descendantsProjectIds = descendants.stream().map(Project :: getId).toList(); + projectIds.addAll(descendantsProjectIds); + } } - } - //Fetch targets based on Project Ids - List targets = getTargetsBasedOnProjectIds(projectIds); + //Fetch targets based on Project Ids + targets = getTargetsBasedOnProjectIds(projectIds); - //Fetch documents based on Project Ids - List documents = getDocumentsBasedOnProjectIds(projectIds); + //Fetch documents based on Project Ids + documents = getDocumentsBasedOnProjectIds(projectIds); + } //Construct Project Objects with fetched projects, targets and documents using Project id return buildProjectSearchResult(projects, targets, documents, ancestors, descendants); @@ -151,9 +162,9 @@ private List getProjectsBasedOnV2SearchCriteria(@NotNull @Valid Project } /* Fetch Projects based on search criteria */ - private List getProjectsBasedOnSearchCriteria(List projectsRequest, Integer limit, Integer offset, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo) { + private List getProjectsBasedOnSearchCriteria(List projectsRequest, Integer limit, Integer offset, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, boolean isAncestorProjectId) { List preparedStmtList = new ArrayList<>(); - String query = queryBuilder.getProjectSearchQuery(projectsRequest, limit, offset, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, preparedStmtList, false); + String query = queryBuilder.getProjectSearchQuery(projectsRequest, limit, offset, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, isAncestorProjectId, preparedStmtList, false); List projects = jdbcTemplate.query(query, addressRowMapper, preparedStmtList.toArray()); log.info("Fetched project list based on given search criteria"); @@ -339,9 +350,9 @@ private void addDescendantsToProjectSearchResult(Project project, List * query build at the run time) * @return */ - public Integer getProjectCount(ProjectRequest project, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo) { + public Integer getProjectCount(ProjectRequest project, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, boolean isAncestorProjectId) { List preparedStatement = new ArrayList<>(); - String query = queryBuilder.getSearchCountQueryString(project.getProjects(), tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, preparedStatement); + String query = queryBuilder.getSearchCountQueryString(project.getProjects(), tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, isAncestorProjectId, preparedStatement); if (query == null) return 0; diff --git a/health-services/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java b/health-services/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java index 4b3aeda7bac..bce83d7cc5d 100644 --- a/health-services/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java +++ b/health-services/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java @@ -46,8 +46,11 @@ public class ProjectAddressQueryBuilder { "left join project_address addr " + "on prj.id = addr.projectId ";; - /* Constructs project search query based on conditions */ - public String getProjectSearchQuery(List projects, Integer limit, Integer offset, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, List preparedStmtList, boolean isCountQuery) { + /** + * Constructs project search query based on conditions + * @param isAncestorProjectId if set to true, project id in the projects would be considered as ancestor project id. + */ + public String getProjectSearchQuery(List projects, Integer limit, Integer offset, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, boolean isAncestorProjectId, List preparedStmtList, boolean isCountQuery) { //This uses a ternary operator to choose between PROJECTS_COUNT_QUERY or FETCH_PROJECT_ADDRESS_QUERY based on the value of isCountQuery. String query = isCountQuery ? PROJECTS_COUNT_QUERY : FETCH_PROJECT_ADDRESS_QUERY; StringBuilder queryBuilder = new StringBuilder(query); @@ -69,7 +72,16 @@ public String getProjectSearchQuery(List projects, Integer limit, Integ } } - if (StringUtils.isNotBlank(project.getId())) { + /* + * If isAncestorProjectId is set to true, Then either id equals to project id or projectHierarchy + * should have id of the project + */ + if (isAncestorProjectId && StringUtils.isNotBlank(project.getId())) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" ( prj.projectHierarchy LIKE ? OR prj.id =? ) "); + preparedStmtList.add('%' + project.getId() + '%'); + preparedStmtList.add(project.getId()); + } else if (StringUtils.isNotBlank(project.getId())) { addClauseIfRequired(preparedStmtList, queryBuilder); queryBuilder.append(" prj.id =? "); preparedStmtList.add(project.getId()); @@ -379,8 +391,8 @@ public String getProjectDescendantsSearchQueryBasedOnIds(List projectIds } /* Returns query to get total projects count based on project search params */ - public String getSearchCountQueryString(List projects, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, List preparedStatement) { - String query = getProjectSearchQuery(projects, config.getMaxLimit(), config.getDefaultOffset(), tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, preparedStatement, true); + public String getSearchCountQueryString(List projects, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, boolean isAncestorProjectId, List preparedStatement) { + String query = getProjectSearchQuery(projects, config.getMaxLimit(), config.getDefaultOffset(), tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, isAncestorProjectId, preparedStatement, true); return query; } diff --git a/health-services/project/src/main/java/org/egov/project/repository/rowmapper/TargetRowMapper.java b/health-services/project/src/main/java/org/egov/project/repository/rowmapper/TargetRowMapper.java index 59477283fec..f8a3b22fbc5 100644 --- a/health-services/project/src/main/java/org/egov/project/repository/rowmapper/TargetRowMapper.java +++ b/health-services/project/src/main/java/org/egov/project/repository/rowmapper/TargetRowMapper.java @@ -1,6 +1,7 @@ package org.egov.project.repository.rowmapper; import org.egov.common.contract.models.AuditDetails; +import org.egov.common.models.project.BeneficiaryType; import org.egov.common.models.project.Target; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; @@ -40,7 +41,7 @@ public List extractData(ResultSet rs) throws SQLException, DataAccessExc Target target = Target.builder() .id(target_id) .projectid(target_projectId) - .beneficiaryType(target_beneficiaryType) + .beneficiaryType(BeneficiaryType.fromValue(target_beneficiaryType)) .totalNo(target_totalNo) .targetNo(target_targetNo) .isDeleted(target_isDeleted) diff --git a/health-services/project/src/main/java/org/egov/project/service/ProjectService.java b/health-services/project/src/main/java/org/egov/project/service/ProjectService.java index e27c827ce29..349248e1ac6 100644 --- a/health-services/project/src/main/java/org/egov/project/service/ProjectService.java +++ b/health-services/project/src/main/java/org/egov/project/service/ProjectService.java @@ -79,6 +79,11 @@ public ProjectRequest createProject(ProjectRequest projectRequest) { return projectRequest; } + /** + * Search for projects based on various criteria + * @param isAncestorProjectId When true, treats the project IDs in the search criteria as ancestor project IDs + * and returns all projects (including children) under these ancestors + */ public List searchProject( ProjectRequest project, Integer limit, @@ -89,7 +94,8 @@ public List searchProject( Boolean includeAncestors, Boolean includeDescendants, Long createdFrom, - Long createdTo + Long createdTo, + boolean isAncestorProjectId ) { projectValidator.validateSearchProjectRequest(project, limit, offset, tenantId, createdFrom, createdTo); List projects = projectRepository.getProjects( @@ -102,7 +108,8 @@ public List searchProject( includeAncestors, includeDescendants, createdFrom, - createdTo + createdTo, + isAncestorProjectId ); return projects; } @@ -125,7 +132,7 @@ public ProjectRequest updateProject(ProjectRequest request) { List projectsFromDB = searchProject( getSearchProjectRequest(request.getProjects(), request.getRequestInfo(), false), projectConfiguration.getMaxLimit(), projectConfiguration.getDefaultOffset(), - request.getProjects().get(0).getTenantId(), null, false, false, false, null, null + request.getProjects().get(0).getTenantId(), null, false, false, false, null, null, false ); log.info("Fetched projects for update request"); @@ -280,7 +287,8 @@ private void checkAndEnrichCascadingProjectDates(ProjectRequest request, Project true, true, null, - null + null, + false ); /* @@ -301,7 +309,7 @@ private List getParentProjects(ProjectRequest projectRequest) { List parentProjects = null; List projectsForSearchRequest = projectRequest.getProjects().stream().filter(p -> StringUtils.isNotBlank(p.getParent())).collect(Collectors.toList()); if (projectsForSearchRequest.size() > 0) { - parentProjects = searchProject(getSearchProjectRequest(projectsForSearchRequest, projectRequest.getRequestInfo(), true), projectConfiguration.getMaxLimit(), projectConfiguration.getDefaultOffset(), projectRequest.getProjects().get(0).getTenantId(), null, false, false, false, null, null); + parentProjects = searchProject(getSearchProjectRequest(projectsForSearchRequest, projectRequest.getRequestInfo(), true), projectConfiguration.getMaxLimit(), projectConfiguration.getDefaultOffset(), projectRequest.getProjects().get(0).getTenantId(), null, false, false, false, null, null, false); } log.info("Fetched parent projects from DB"); return parentProjects; @@ -329,8 +337,8 @@ private ProjectRequest getSearchProjectRequest(List projects, RequestIn /** * @return Count of List of matching projects */ - public Integer countAllProjects(ProjectRequest project, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo) { - return projectRepository.getProjectCount(project, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo); + public Integer countAllProjects(ProjectRequest project, String tenantId, Long lastChangedSince, Boolean includeDeleted, Long createdFrom, Long createdTo, boolean isAncestorProjectId) { + return projectRepository.getProjectCount(project, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, isAncestorProjectId); } diff --git a/health-services/project/src/main/java/org/egov/project/validator/beneficiary/BeneficiaryValidator.java b/health-services/project/src/main/java/org/egov/project/validator/beneficiary/BeneficiaryValidator.java index c0be5cfcb64..a8206f51245 100644 --- a/health-services/project/src/main/java/org/egov/project/validator/beneficiary/BeneficiaryValidator.java +++ b/health-services/project/src/main/java/org/egov/project/validator/beneficiary/BeneficiaryValidator.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.models.project.*; import org.egov.mdms.model.MasterDetail; import org.egov.mdms.model.MdmsCriteria; import org.egov.mdms.model.MdmsCriteriaReq; @@ -20,10 +21,6 @@ import org.egov.common.models.individual.IndividualBulkResponse; import org.egov.common.models.individual.IndividualSearch; import org.egov.common.models.individual.IndividualSearchRequest; -import org.egov.common.models.project.BeneficiaryBulkRequest; -import org.egov.common.models.project.Project; -import org.egov.common.models.project.ProjectBeneficiary; -import org.egov.common.models.project.ProjectType; import org.egov.common.service.MdmsService; import org.egov.common.validator.Validator; import org.egov.project.config.ProjectConfiguration; @@ -103,11 +100,11 @@ public Map> validate(BeneficiaryBulkRequest bene Map projectMap = getIdToObjMap(existingProjects); log.info("creating beneficiaryType map"); - Map> beneficiaryTypeMap = validProjectBeneficiaries.stream() + Map> beneficiaryTypeMap = validProjectBeneficiaries.stream() .collect(Collectors.groupingBy(b -> projectTypeMap.get(projectMap.get(b .getProjectId()).getProjectTypeId()).getBeneficiaryType())); - for (Map.Entry> entry : beneficiaryTypeMap.entrySet()) { + for (Map.Entry> entry : beneficiaryTypeMap.entrySet()) { log.info("fetch the beneficiaries for type {}", entry.getKey()); searchBeneficiary(entry.getKey(), entry.getValue(), beneficiaryBulkRequest.getRequestInfo(), tenantId, errorDetailsMap); @@ -116,18 +113,18 @@ public Map> validate(BeneficiaryBulkRequest bene return errorDetailsMap; } - private void searchBeneficiary(String beneficiaryType, List beneficiaryList, + private void searchBeneficiary(BeneficiaryType beneficiaryType, List beneficiaryList, RequestInfo requestInfo, String tenantId, Map> errorDetailsMap) { switch (beneficiaryType) { - case "HOUSEHOLD": + case HOUSEHOLD: searchHouseholdBeneficiary(beneficiaryList, requestInfo, tenantId, errorDetailsMap); break; - case "INDIVIDUAL": + case INDIVIDUAL: searchIndividualBeneficiary(beneficiaryList, requestInfo, tenantId, errorDetailsMap); break; default: - throw new CustomException("INVALID_BENEFICIARY_TYPE", beneficiaryType); + throw new CustomException("INVALID_BENEFICIARY_TYPE", beneficiaryType.name()); } } diff --git a/health-services/project/src/main/java/org/egov/project/validator/staff/PsUserIdValidator.java b/health-services/project/src/main/java/org/egov/project/validator/staff/PsUserIdValidator.java index a33408b813c..5aecbe1a6c9 100644 --- a/health-services/project/src/main/java/org/egov/project/validator/staff/PsUserIdValidator.java +++ b/health-services/project/src/main/java/org/egov/project/validator/staff/PsUserIdValidator.java @@ -112,4 +112,4 @@ public Map> validate(ProjectStaffBulkRequest request) } return errorDetailsMap; } -} +} \ No newline at end of file diff --git a/health-services/project/src/main/java/org/egov/project/validator/task/PtResourceQuantityValidator.java b/health-services/project/src/main/java/org/egov/project/validator/task/PtResourceQuantityValidator.java index 0a4a68bd917..44169bf63e3 100644 --- a/health-services/project/src/main/java/org/egov/project/validator/task/PtResourceQuantityValidator.java +++ b/health-services/project/src/main/java/org/egov/project/validator/task/PtResourceQuantityValidator.java @@ -234,4 +234,4 @@ private MdmsCriteriaReq getMdmsRequest(RequestInfo requestInfo, String tenantId, mdmsCriteriaReq.setRequestInfo(requestInfo); return mdmsCriteriaReq; } -} +} \ No newline at end of file diff --git a/health-services/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java b/health-services/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java index 2ec0adc34d3..49017853794 100644 --- a/health-services/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java +++ b/health-services/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java @@ -486,7 +486,8 @@ public ResponseEntity searchProject( @ApiParam(value = "Used in project search API to specify if response should include project elements that are in the preceding hierarchy of matched projects.", defaultValue = "false") @Valid @RequestParam(value = "includeAncestors", required = false, defaultValue = "false") Boolean includeAncestors, @ApiParam(value = "Used in project search API to specify if response should include project elements that are in the following hierarchy of matched projects.", defaultValue = "false") @Valid @RequestParam(value = "includeDescendants", required = false, defaultValue = "false") Boolean includeDescendants, @ApiParam(value = "Used in project search API to limit the search results to only those projects whose creation date is after the specified 'createdFrom' date", defaultValue = "false") @Valid @RequestParam(value = "createdFrom", required = false) Long createdFrom, - @ApiParam(value = "Used in project search API to limit the search results to only those projects whose creation date is before the specified 'createdTo' date", defaultValue = "false") @Valid @RequestParam(value = "createdTo", required = false) Long createdTo + @ApiParam(value = "Used in project search API to limit the search results to only those projects whose creation date is before the specified 'createdTo' date", defaultValue = "false") @Valid @RequestParam(value = "createdTo", required = false) Long createdTo, + @ApiParam(value = "Used in project search API to specify if response should be one which is in the preceding hierarchy of matched projects.") @Valid @RequestParam(value = "isAncestorProjectId", required = false, defaultValue = "false") boolean isAncestorProjectId ) { List projects = projectService.searchProject( project, @@ -498,10 +499,11 @@ public ResponseEntity searchProject( includeAncestors, includeDescendants, createdFrom, - createdTo + createdTo, + isAncestorProjectId ); ResponseInfo responseInfo = ResponseInfoFactory.createResponseInfo(project.getRequestInfo(), true); - Integer count = projectService.countAllProjects(project, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo); + Integer count = projectService.countAllProjects(project, tenantId, lastChangedSince, includeDeleted, createdFrom, createdTo, isAncestorProjectId); ProjectResponse projectResponse = ProjectResponse.builder().responseInfo(responseInfo).project(projects).totalCount(count).build(); return new ResponseEntity(projectResponse, HttpStatus.OK); }