diff --git a/build/build-config.yml b/build/build-config.yml index dec24965782..2002805a366 100644 --- a/build/build-config.yml +++ b/build/build-config.yml @@ -250,15 +250,22 @@ config: dockerfile: "build/17/maven/Dockerfile" - work-dir: "health-services/plan-service/src/main/resources/db" image-name: "plan-service-db" - - name: "builds/health-campaign-services/health-services/resource-estimation-service" + - name: "builds/health-campaign-services/health-services/resource-generator" build: - - work-dir: "health-services/resource-estimation-service" - image-name: "resource-estimation-service" + - work-dir: "health-services/resource-generator" + image-name: "resource-generator" dockerfile: "build/17/maven/Dockerfile" - name: "builds/health-campaign-services/analytics/auth-proxy" build: - work-dir: "analytics/auth-proxy" image-name: "auth-proxy" + - name: "builds/health-campaign-services/health-services/census-service" + build: + - work-dir: "health-services/census-service" + image-name: "census-service" + dockerfile: "build/17/maven/Dockerfile" + - work-dir: "health-services/census-service/src/main/resources/db" + image-name: "census-service-db" # frontend - name: builds/health-campaign-services/frontend/workbench-ui diff --git a/health-services/census-service/CHANGELOG.md b/health-services/census-service/CHANGELOG.md new file mode 100644 index 00000000000..38562489c90 --- /dev/null +++ b/health-services/census-service/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog +All notable changes to this module will be documented in this file. + +## 1.0.0 - 2024-11-28 +#### Census Service +The Census Service introduces core functionalities for managing census data: + +1. Validation of Census: Ensures data integrity by validating all census requests before processing. +2. Census Create: Creates new census records after validation and enrichment, publishing request to the designated Kafka topic to handle the creation process asynchronously. +3. Census Update: Updates existing records post-validation and enrichment by sending request to the designated Kafka update topic. +4. Census Bulk Update: Updates multiple census records in one operation after successful validation. +5. Census Search: Enables searching for census records with the provided search criteria. +6. Plan Facility Consumer: Listens to Plan Facility Update topic to assign facility to a boundary in census. \ No newline at end of file diff --git a/health-services/census-service/README.md b/health-services/census-service/README.md new file mode 100644 index 00000000000..a2e8a9f7b84 --- /dev/null +++ b/health-services/census-service/README.md @@ -0,0 +1,18 @@ +# Swagger generated server + +Spring Boot Server + + +## Overview +This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. +By using the [OpenAPI-Spec](https://github.com/swagger-api/swagger-core), you can easily generate a server stub. +This is an example of building a swagger-enabled server in Java using the SpringBoot framework. + +The underlying library integrating swagger to SpringBoot is [springfox](https://github.com/springfox/springfox) + +Start your server as an simple java application + +You can view the api documentation in swagger-ui by pointing to +http://localhost:8080/ + +Change default port value in application.properties \ No newline at end of file diff --git a/health-services/census-service/pom.xml b/health-services/census-service/pom.xml new file mode 100644 index 00000000000..7e6f7adc8fb --- /dev/null +++ b/health-services/census-service/pom.xml @@ -0,0 +1,141 @@ + + 4.0.0 + org.egov + census-service + jar + census-service + 1.0.0 + + 17 + ${java.version} + ${java.version} + + + org.springframework.boot + spring-boot-starter-parent + 3.2.2 + + + src/main/java + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.egov.common + health-services-models + 1.0.21-SNAPSHOT + compile + + + junit + junit + 4.13.2 + test + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.flywaydb + flyway-core + 9.22.3 + + + org.postgresql + postgresql + 42.7.1 + + + org.springframework.boot + spring-boot-starter-test + test + + + + io.swagger + swagger-core + 1.5.18 + + + io.swagger.core.v3 + swagger-annotations + 2.2.8 + + + net.minidev + json-smart + 2.5.0 + + + + org.egov.services + tracer + 2.9.0-SNAPSHOT + + + + + + + + org.egov + mdms-client + 2.9.0-SNAPSHOT + compile + + + org.projectlombok + lombok + true + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.springframework.boot + spring-boot-starter-validation + + + + + repo.egovernments.org + eGov ERP Releases Repository + https://nexus-repo.egovernments.org/nexus/content/repositories/releases/ + + + repo.egovernments.org.snapshots + eGov ERP Releases Repository + https://nexus-repo.egovernments.org/nexus/content/repositories/snapshots/ + + + repo.egovernments.org.public + eGov Public Repository Group + https://nexus-repo.egovernments.org/nexus/content/groups/public/ + + + repo.digit.org + eGov DIGIT Releases Repository + https://nexus-repo.digit.org/nexus/content/repositories/snapshots/ + + + diff --git a/health-services/census-service/src/main/java/digit/Main.java b/health-services/census-service/src/main/java/digit/Main.java new file mode 100644 index 00000000000..6e3d79db11c --- /dev/null +++ b/health-services/census-service/src/main/java/digit/Main.java @@ -0,0 +1,20 @@ +package digit; + + +import org.egov.tracer.config.TracerConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Import; + +@Import({ TracerConfiguration.class }) +@SpringBootApplication +@ComponentScan(basePackages = { "digit", "digit.web.controllers" , "digit.config"}) +public class Main { + + + public static void main(String[] args) throws Exception { + SpringApplication.run(Main.class, args); + } + +} diff --git a/health-services/census-service/src/main/java/digit/config/Configuration.java b/health-services/census-service/src/main/java/digit/config/Configuration.java new file mode 100644 index 00000000000..8459885357e --- /dev/null +++ b/health-services/census-service/src/main/java/digit/config/Configuration.java @@ -0,0 +1,86 @@ +package digit.config; + +import lombok.*; +import org.egov.tracer.config.TracerConfiguration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@Data +@Import({TracerConfiguration.class}) +@NoArgsConstructor +@AllArgsConstructor +@Setter +@Getter +public class Configuration { + + // Allowed roles for census + @Value("#{${allowed.census.roles}}") + private List allowedCensusRoles; + + @Value("#{${workflow.restricted.roles}}") + private List workflowRestrictedRoles; + + // Persister Topic + @Value("${census.create.topic}") + private String censusCreateTopic; + + @Value("${census.update.topic}") + private String censusUpdateTopic; + + @Value("${census.bulk.update.topic}") + private String censusBulkUpdateTopic; + + @Value("${plan.facility.update.topic}") + private String planFcailityUpdateTopic; + + // Boundary Service + @Value("${egov.boundary.service.host}") + private String boundaryServiceHost; + + @Value("${egov.boundary.relationship.search.endpoint}") + private String boundaryRelationshipSearchEndpoint; + + @Value("${egov.boundary.hierarchy.search.endpoint}") + private String boundaryHierarchySearchEndpoint; + + // Plan Service + @Value("${egov.plan.service.host}") + private String planServiceHost; + + @Value("${egov.plan.employee.assignment.search.endpoint}") + private String planEmployeeAssignmentSearchEndpoint; + + //Workflow + @Value("${egov.workflow.host}") + private String wfHost; + + @Value("${egov.workflow.transition.path}") + private String wfTransitionPath; + + @Value("${egov.business.service.search.endpoint}") + private String businessServiceSearchEndpoint; + + @Value("${workflow.initiate.action}") + private List wfInitiateActions; + + @Value("${workflow.intermediate.action}") + private List wfIntermediateActions; + + @Value("${workflow.send.back.actions}") + private List wfSendBackActions; + + //SMSNotification + @Value("${egov.sms.notification.topic}") + private String smsNotificationTopic; + + //Pagination + @Value("${census.default.offset}") + private Integer defaultOffset; + + @Value("${census.default.limit}") + private Integer defaultLimit; +} diff --git a/health-services/census-service/src/main/java/digit/config/MainConfiguration.java b/health-services/census-service/src/main/java/digit/config/MainConfiguration.java new file mode 100644 index 00000000000..239331c9cd8 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/config/MainConfiguration.java @@ -0,0 +1,41 @@ +package digit.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +import java.util.TimeZone; + +import jakarta.annotation.PostConstruct; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.tracer.config.TracerConfiguration; + + +@Import({TracerConfiguration.class}) +public class MainConfiguration { + + @Value("${app.timezone}") + private String timeZone; + + @PostConstruct + public void initialize() { + TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); + } + + @Bean + public ObjectMapper objectMapper() { + return new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).setTimeZone(TimeZone.getTimeZone(timeZone)); + } + + @Bean + @Autowired + public MappingJackson2HttpMessageConverter jacksonConverter(ObjectMapper objectMapper) { + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + converter.setObjectMapper(objectMapper); + return converter; + } +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/config/ServiceConstants.java b/health-services/census-service/src/main/java/digit/config/ServiceConstants.java new file mode 100644 index 00000000000..5d944060848 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/config/ServiceConstants.java @@ -0,0 +1,114 @@ +package digit.config; + + +import org.springframework.stereotype.Component; + + +@Component +public class ServiceConstants { + + public static final String EXTERNAL_SERVICE_EXCEPTION = "External Service threw an Exception: "; + public static final String SEARCHER_SERVICE_EXCEPTION = "Exception while fetching from searcher: "; + + public static final String IDGEN_ERROR = "IDGEN ERROR"; + public static final String NO_IDS_FOUND_ERROR = "No ids returned from idgen Service"; + + public static final String ERROR_WHILE_FETCHING_FROM_MDMS = "Exception occurred while fetching category lists from mdms: "; + + public static final String ERROR_WHILE_FETCHING_BOUNDARY_DETAILS = "Exception occurred while fetching boundary relationship from boundary service: "; + + public static final String ERROR_WHILE_FETCHING_BOUNDARY_HIERARCHY_DETAILS = "Exception occurred while fetching boundary hierarchy details from boundary service: "; + + public static final String ERROR_WHILE_FETCHING_EMPLOYEE_ASSIGNMENT_DETAILS = "Exception occurred while fetching plan employee assignment details from plan service: "; + + public static final String ERROR_WHILE_FETCHING_BUSINESS_SERVICE_DETAILS = "Exception occurred while fetching business service details: "; + + public static final String RES_MSG_ID = "uief87324"; + public static final String SUCCESSFUL = "successful"; + public static final String FAILED = "failed"; + + public static final String URL = "url"; + public static final String URL_SHORTENING_ERROR_CODE = "URL_SHORTENING_ERROR"; + public static final String URL_SHORTENING_ERROR_MESSAGE = "Unable to shorten url: "; + + public static final String DOB_FORMAT_Y_M_D = "yyyy-MM-dd"; + public static final String DOB_FORMAT_D_M_Y = "dd/MM/yyyy"; + public static final String ILLEGAL_ARGUMENT_EXCEPTION_CODE = "IllegalArgumentException"; + public static final String OBJECTMAPPER_UNABLE_TO_CONVERT = "ObjectMapper not able to convertValue in userCall"; + public static final String DOB_FORMAT_D_M_Y_H_M_S = "dd-MM-yyyy HH:mm:ss"; + public static final String CREATED_DATE = "createdDate"; + public static final String LAST_MODIFIED_DATE = "lastModifiedDate"; + public static final String DOB = "dob"; + public static final String PWD_EXPIRY_DATE = "pwdExpiryDate"; + public static final String INVALID_DATE_FORMAT_CODE = "INVALID_DATE_FORMAT"; + public static final String INVALID_DATE_FORMAT_MESSAGE = "Failed to parse date format in user"; + public static final String CITIZEN_UPPER = "CITIZEN"; + public static final String CITIZEN_LOWER = "Citizen"; + public static final String USER = "user"; + public static final String PIPE_REGEX = "\\|"; + public static final String FACILITY_ID_FIELD = "facilityId"; + public static final String FACILITY_NAME_FIELD = "facilityName"; + + public static final String PARSING_ERROR_CODE = "PARSING ERROR"; + public static final String PARSING_ERROR_MESSAGE = "Failed to parse JSON data from PGobject"; + + public static final String FAILED_TO_PARSE_BUSINESS_SERVICE_SEARCH = "Failed to parse response of workflow business service search"; + public static final String BUSINESS_SERVICE_NOT_FOUND = "BUSINESSSERVICE_NOT_FOUND"; + public static final String THE_BUSINESS_SERVICE = "The businessService "; + public static final String NOT_FOUND = " is not found"; + public static final String TENANTID = "?tenantId="; + public static final String BUSINESS_SERVICES = "&businessServices="; + + public static final String NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_CODE = "NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE"; + public static final String NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_MESSAGE = "Invalid or incorrect boundaryCode. No boundary data found."; + + public static final String NO_BUSINESS_SERVICE_DATA_FOUND_CODE = "NO_BUSINESS_SERVICE_DATA_FOUND"; + public static final String NO_BUSINESS_SERVICE_DATA_FOUND_MESSAGE = "Invalid or incorrect businessService. No business service data found."; + + public static final String USERINFO_MISSING_CODE = "USERINFO_MISSING"; + public static final String USERINFO_MISSING_MESSAGE = "UserInfo is missing in Request Info "; + + public static final String ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE = "ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS"; + public static final String ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE = "Exception occurred while updating additional details : "; + + public static final String WORKFLOW_INTEGRATION_ERROR_CODE = "WORKFLOW_INTEGRATION_ERROR"; + public static final String WORKFLOW_INTEGRATION_ERROR_MESSAGE = "Exception occured while integrating with workflow : "; + + public static final String INVALID_PARTNER_CODE = "INVALID_PARTNER"; + public static final String INVALID_PARTNER_MESSAGE = "Invalid partner assignment or invalid jurisdiction of the assigned partner"; + + public static final String INVALID_CENSUS_CODE = "INVALID_CENSUS"; + public static final String INVALID_CENSUS_MESSAGE = "Provided census does not exist"; + + public static final String DUPLICATE_CENSUS_ID_IN_BULK_UPDATE_CODE = "DUPLICATE_CENSUS_ID_IN_BULK_UPDATE"; + public static final String DUPLICATE_CENSUS_ID_IN_BULK_UPDATE_MESSAGE = "Census provided in the bulk update request are not unique."; + + public static final String INVALID_SOURCE_OR_TENANT_ID_FOR_BULK_UPDATE_CODE = "INVALID_SOURCE_OR_TENANT_ID_FOR_BULK_UPDATE"; + public static final String INVALID_SOURCE_OR_TENANT_ID_FOR_BULK_UPDATE_MESSAGE = "Tenant id and source should be same across all entries for bulk update."; + + public static final String WORKFLOW_NOT_FOUND_FOR_BULK_UPDATE_CODE = "WORKFLOW_NOT_FOUND_FOR_BULK_UPDATE"; + public static final String WORKFLOW_NOT_FOUND_FOR_BULK_UPDATE_MESSAGE = "Workflow information is mandatory for each entry for bulk update"; + + public static final String DUPLICATE_KEY_IN_ADDITIONAL_FIELD_CODE = "DUPLICATE_KEY_IN_ADDITIONAL_FIELD"; + public static final String DUPLICATE_KEY_IN_ADDITIONAL_FIELD_MESSGAE = "Duplicate key found in additional field : "; + + public static final String CENSUS_ALREADY_EXISTS_CODE = "CENSUS_ALREADY_EXISTS"; + public static final String CENSUS_ALREADY_EXISTS_MESSAGE = "Census with the given boundary and source already exists."; + + public static final String DIFFERENT_WORKFLOW_FOR_BULK_UPDATE_CODE = "DIFFERENT_WORKFLOW_FOR_BULK_UPDATE"; + public static final String DIFFERENT_WORKFLOW_FOR_BULK_UPDATE_MESSAGE = "All entries should be in the same state for bulk transitioning census records."; + + public static final String UNAUTHORIZED_WORKFLOW_ACCESS_CODE = "UNAUTHORIZED_WORKFLOW_ACCESS"; + public static final String UNAUTHORIZED_WORKFLOW_ACCESS_MESSAGE = "User with provided roles cannot have an active workflow. Please remove the workflow or update user roles."; + + public static final String SEARCH_CRITERIA_EMPTY_CODE = "SEARCH_CRITERIA_EMPTY"; + public static final String SEARCH_CRITERIA_EMPTY_MESSAGE = "Search criteria cannot be empty"; + + public static final String TENANT_ID_EMPTY_CODE = "TENANT_ID_EMPTY"; + public static final String TENANT_ID_EMPTY_MESSAGE = "Tenant Id cannot be empty, TenantId should be present"; + + //Workflow constants + public static final String MODULE_NAME_VALUE = "census-service"; + + public static final String CENSUS_BUSINESS_SERVICE = "CENSUS"; +} diff --git a/health-services/census-service/src/main/java/digit/kafka/FacilityCatchmentConsumer.java b/health-services/census-service/src/main/java/digit/kafka/FacilityCatchmentConsumer.java new file mode 100644 index 00000000000..d5be4b7458c --- /dev/null +++ b/health-services/census-service/src/main/java/digit/kafka/FacilityCatchmentConsumer.java @@ -0,0 +1,99 @@ +package digit.kafka; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.repository.CensusRepository; +import digit.service.CensusService; +import digit.service.enrichment.CensusEnrichment; +import digit.util.BoundaryUtil; +import digit.util.CommonUtil; +import digit.web.models.BulkCensusRequest; +import digit.web.models.Census; +import digit.web.models.CensusResponse; +import digit.web.models.boundary.BoundaryTypeHierarchyResponse; +import digit.web.models.plan.PlanFacilityDTO; +import digit.web.models.plan.PlanFacilityRequestDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static digit.config.ServiceConstants.FACILITY_ID_FIELD; +import static digit.config.ServiceConstants.FACILITY_NAME_FIELD; + +@Component +@Slf4j +public class FacilityCatchmentConsumer { + + private ObjectMapper objectMapper; + + private CensusService service; + + private CensusRepository repository; + + private CommonUtil commonUtil; + + private BoundaryUtil boundaryUtil; + + private CensusEnrichment enrichment; + + public FacilityCatchmentConsumer(ObjectMapper objectMapper, CensusService service, CommonUtil commonUtil, CensusRepository repository, BoundaryUtil boundaryUtil, CensusEnrichment enrichment) { + this.objectMapper = objectMapper; + this.service = service; + this.commonUtil = commonUtil; + this.repository = repository; + this.boundaryUtil = boundaryUtil; + this.enrichment = enrichment; + } + + @KafkaListener(topics = {"${plan.facility.update.topic}"}) + public void listen(Map consumerRecord, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { + try { + PlanFacilityRequestDTO planFacilityRequestDTO = objectMapper.convertValue(consumerRecord, PlanFacilityRequestDTO.class); + PlanFacilityDTO planFacilityDTO = planFacilityRequestDTO.getPlanFacilityDTO(); + + CensusResponse censusResponse = service.search(commonUtil.getCensusSearchRequest(planFacilityDTO.getTenantId(), planFacilityDTO.getPlanConfigurationId(), planFacilityDTO.getServiceBoundaries(), planFacilityDTO.getInitiallySetServiceBoundaries(), planFacilityRequestDTO.getRequestInfo())); + List censusFromSearch = censusResponse.getCensus(); + + BoundaryTypeHierarchyResponse boundaryTypeHierarchyResponse = boundaryUtil.fetchBoundaryHierarchy(planFacilityRequestDTO.getRequestInfo(), censusFromSearch.get(0).getTenantId(), censusFromSearch.get(0).getHierarchyType()); + String facilityId = planFacilityRequestDTO.getPlanFacilityDTO().getFacilityId(); + String facilityName = planFacilityRequestDTO.getPlanFacilityDTO().getFacilityName(); + + Set boundariesWithFacility = new HashSet<>(List.of(planFacilityDTO.getServiceBoundaries().split(","))); + Set boundariesWithNoFacility = new HashSet<>(planFacilityDTO.getInitiallySetServiceBoundaries()); + + censusFromSearch.forEach(census -> { + String boundaryCode = census.getBoundaryCode(); + + if (!boundariesWithFacility.contains(boundaryCode)) { + + // Unassigning facilities to the boundaries which were initially assigned that facility + census.setAdditionalDetails(commonUtil.removeFieldFromAdditionalDetails(census.getAdditionalDetails(), FACILITY_ID_FIELD)); + census.setAdditionalDetails(commonUtil.removeFieldFromAdditionalDetails(census.getAdditionalDetails(), FACILITY_NAME_FIELD)); + census.setFacilityAssigned(Boolean.FALSE); + census.setPartnerAssignmentValidationEnabled(Boolean.FALSE); + + } else if (!boundariesWithNoFacility.contains(boundaryCode)) { + + // Assigning facilities to the newly added boundaries in the update request. + census.setAdditionalDetails(commonUtil.updateFieldInAdditionalDetails(census.getAdditionalDetails(), FACILITY_ID_FIELD, facilityId)); + census.setAdditionalDetails(commonUtil.updateFieldInAdditionalDetails(census.getAdditionalDetails(), FACILITY_NAME_FIELD, facilityName)); + census.setFacilityAssigned(Boolean.TRUE); + census.setPartnerAssignmentValidationEnabled(Boolean.FALSE); + } + }); + + // Enrich jurisdiction mapping in census for indexer + enrichment.enrichJurisdictionMapping(censusFromSearch, boundaryTypeHierarchyResponse.getBoundaryHierarchy().get(0)); + repository.bulkUpdate(BulkCensusRequest.builder().requestInfo(planFacilityRequestDTO.getRequestInfo()).census(censusFromSearch).build()); + + } catch (Exception exception) { + log.error("Error in census consumer", exception); + } + } +} diff --git a/health-services/census-service/src/main/java/digit/kafka/Producer.java b/health-services/census-service/src/main/java/digit/kafka/Producer.java new file mode 100644 index 00000000000..542f4f686c0 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/kafka/Producer.java @@ -0,0 +1,20 @@ +package digit.kafka; + +import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.kafka.CustomKafkaTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +// NOTE: If tracer is disabled change CustomKafkaTemplate to KafkaTemplate in autowiring + +@Service +@Slf4j +public class Producer { + + @Autowired + private CustomKafkaTemplate kafkaTemplate; + + public void push(String topic, Object value) { + kafkaTemplate.send(topic, value); + } +} diff --git a/health-services/census-service/src/main/java/digit/kafka/ResourceCensusConsumer.java b/health-services/census-service/src/main/java/digit/kafka/ResourceCensusConsumer.java new file mode 100644 index 00000000000..e161225b23a --- /dev/null +++ b/health-services/census-service/src/main/java/digit/kafka/ResourceCensusConsumer.java @@ -0,0 +1,37 @@ +package digit.kafka; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.service.CensusService; +import digit.web.models.CensusRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +@Slf4j +public class ResourceCensusConsumer { + + private CensusService censusService; + + private ObjectMapper mapper; + + public ResourceCensusConsumer(CensusService censusService, ObjectMapper mapper) { + this.censusService = censusService; + this.mapper = mapper; + } + + @KafkaListener(topics = {"${resource.config.consumer.census.create.topic}"}) + public void listen(Map consumerRecord, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { + try { + CensusRequest censusRequest = mapper.convertValue(consumerRecord, CensusRequest.class); + censusRequest.getCensus().setPartnerAssignmentValidationEnabled(Boolean.FALSE); + censusService.create(censusRequest); + } catch (Exception exception) { + log.error("Error in resource census consumer", exception); + } + } +} diff --git a/health-services/census-service/src/main/java/digit/repository/CensusRepository.java b/health-services/census-service/src/main/java/digit/repository/CensusRepository.java new file mode 100644 index 00000000000..4a9f159e7e9 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/repository/CensusRepository.java @@ -0,0 +1,21 @@ +package digit.repository; + +import digit.web.models.*; + +import java.util.List; +import java.util.Map; + +public interface CensusRepository { + + public void create(CensusRequest censusRequest); + + public List search(CensusSearchCriteria censusSearchCriteria); + + public void update(CensusRequest censusRequest); + + public void bulkUpdate(BulkCensusRequest request); + + public Integer count(CensusSearchCriteria censusSearchCriteria); + + public Map statusCount(CensusSearchRequest censusSearchRequest); +} diff --git a/health-services/census-service/src/main/java/digit/repository/ServiceRequestRepository.java b/health-services/census-service/src/main/java/digit/repository/ServiceRequestRepository.java new file mode 100644 index 00000000000..d09d230e4fa --- /dev/null +++ b/health-services/census-service/src/main/java/digit/repository/ServiceRequestRepository.java @@ -0,0 +1,45 @@ +package digit.repository; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.model.ServiceCallException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; + +import static digit.config.ServiceConstants.*; + +@Repository +@Slf4j +public class ServiceRequestRepository { + + private ObjectMapper mapper; + + private RestTemplate restTemplate; + + public ServiceRequestRepository(ObjectMapper mapper, RestTemplate restTemplate) { + this.mapper = mapper; + this.restTemplate = restTemplate; + } + + + public Object fetchResult(StringBuilder uri, Object request) { + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + Object response = null; + try { + response = restTemplate.postForObject(uri.toString(), request, Map.class); + } catch (HttpClientErrorException e) { + log.error(EXTERNAL_SERVICE_EXCEPTION, e); + throw new ServiceCallException(e.getResponseBodyAsString()); + } catch (Exception e) { + log.error(SEARCHER_SERVICE_EXCEPTION, e); + } + + return response; + } +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/repository/impl/CensusRepositoryImpl.java b/health-services/census-service/src/main/java/digit/repository/impl/CensusRepositoryImpl.java new file mode 100644 index 00000000000..c6dec4b4d11 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/repository/impl/CensusRepositoryImpl.java @@ -0,0 +1,219 @@ +package digit.repository.impl; + +import digit.config.Configuration; +import digit.kafka.Producer; +import digit.repository.CensusRepository; +import digit.repository.querybuilder.CensusQueryBuilder; +import digit.repository.rowmapper.CensusRowMapper; +import digit.repository.rowmapper.StatusCountRowMapper; +import digit.util.CommonUtil; +import digit.web.models.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.SingleColumnRowMapper; +import org.springframework.stereotype.Repository; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static digit.config.ServiceConstants.CENSUS_BUSINESS_SERVICE; + +@Slf4j +@Repository +public class CensusRepositoryImpl implements CensusRepository { + + private Producer producer; + + private Configuration config; + + private CensusQueryBuilder queryBuilder; + + private CensusRowMapper censusRowMapper; + + private JdbcTemplate jdbcTemplate; + + private StatusCountRowMapper statusCountRowMapper; + + private CommonUtil commonUtil; + + public CensusRepositoryImpl(Producer producer, Configuration config, CensusQueryBuilder queryBuilder, CensusRowMapper censusRowMapper, JdbcTemplate jdbcTemplate, StatusCountRowMapper statusCountRowMapper,CommonUtil commonUtil) { + this.producer = producer; + this.config = config; + this.queryBuilder = queryBuilder; + this.censusRowMapper = censusRowMapper; + this.jdbcTemplate = jdbcTemplate; + this.statusCountRowMapper = statusCountRowMapper; + this.commonUtil = commonUtil; + } + + /** + * Pushes a new census record to persister kafka topic. + * + * @param censusRequest The request containing the census details + */ + @Override + public void create(CensusRequest censusRequest) { + CensusRequestDTO requestDTO = convertToReqDTO(censusRequest); + producer.push(config.getCensusCreateTopic(), requestDTO); + } + + /** + * Searches for census records based on the provided search criteria. + * + * @param censusSearchCriteria The criteria to use for searching census records. + * @return A list of census records that match the search criteria. + */ + @Override + public List search(CensusSearchCriteria censusSearchCriteria) { + + if(censusSearchCriteria.getAreaCodes() != null && censusSearchCriteria.getAreaCodes().isEmpty()) + return new ArrayList<>(); + + // Fetch census ids from database + List censusIds = queryDatabaseForCensusIds(censusSearchCriteria); + + // Return empty list back as response if no census ids are found + if(CollectionUtils.isEmpty(censusIds)) { + log.info("No census ids found for provided census search criteria."); + return new ArrayList<>(); + } + + // Fetch census from database based on the acquired ids + return searchCensusByIds(censusIds); + } + + private List searchCensusByIds(List censusIds) { + + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getCensusQuery(censusIds, preparedStmtList); + log.info("Census query: " + query); + return jdbcTemplate.query(query, censusRowMapper, preparedStmtList.toArray()); + + } + + private List queryDatabaseForCensusIds(CensusSearchCriteria censusSearchCriteria) { + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getCensusSearchQuery(censusSearchCriteria, preparedStmtList); + log.info("Census search query: " + query); + return jdbcTemplate.query(query, new SingleColumnRowMapper<>(String.class), preparedStmtList.toArray()); + } + + /** + * Counts the number of census records based on the provided search criteria. + * + * @param censusSearchCriteria The search criteria for filtering census records. + * @return The total count of census matching the search criteria. + */ + @Override + public Integer count(CensusSearchCriteria censusSearchCriteria) { + + if(censusSearchCriteria.getAreaCodes() != null && censusSearchCriteria.getAreaCodes().isEmpty()) + return 0; + + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getCensusCountQuery(censusSearchCriteria, preparedStmtList); + + return jdbcTemplate.queryForObject(query, preparedStmtList.toArray(), Integer.class); + } + + /** + * Counts the census record based on their current status for the provided search criteria. + * + * @param censusSearchRequest The request with search criteria for filtering census records. + * @return The status count of census records for the given search criteria. + */ + @Override + public Map statusCount(CensusSearchRequest censusSearchRequest) { + List preparedStmtList = new ArrayList<>(); + List statusList = commonUtil.getStatusFromBusinessService(censusSearchRequest.getRequestInfo(), CENSUS_BUSINESS_SERVICE, censusSearchRequest.getCensusSearchCriteria().getTenantId()); + + String query = queryBuilder.getCensusStatusCountQuery(censusSearchRequest.getCensusSearchCriteria(), preparedStmtList); + Map statusCountMap = jdbcTemplate.query(query, statusCountRowMapper, preparedStmtList.toArray()); + + statusList.forEach(status -> { + if(ObjectUtils.isEmpty(statusCountMap.get(status))) + statusCountMap.put(status, 0); + }); + + return statusCountMap; + } + + /** + * Pushes an updated existing census record to persister kafka topic. + * + * @param censusRequest The request containing the updated census details + */ + @Override + public void update(CensusRequest censusRequest) { + CensusRequestDTO requestDTO = convertToReqDTO(censusRequest); + producer.push(config.getCensusUpdateTopic(), requestDTO); + } + + /** + * Updates workflow status of a list of census records. + * + * @param request The bulk request containing the census records. + */ + @Override + public void bulkUpdate(BulkCensusRequest request) { + // Get bulk census update query + String bulkCensusUpdateQuery = queryBuilder.getBulkCensusQuery(); + + // Prepare rows for bulk update + List rows = request.getCensus().stream().map(census -> new Object[] { + census.getStatus(), + !CollectionUtils.isEmpty(census.getAssignee()) ? String.join(",", census.getAssignee()) : census.getAssignee(), + census.getAuditDetails().getLastModifiedBy(), + census.getAuditDetails().getLastModifiedTime(), + commonUtil.convertToPgObject(census.getAdditionalDetails()), + census.getFacilityAssigned(), + census.getId() + }).toList(); + + // Perform bulk update + jdbcTemplate.batchUpdate(bulkCensusUpdateQuery, rows); + producer.push(config.getCensusBulkUpdateTopic(), request); + } + + /** + * Converts the CensusRequest to a data transfer object (DTO) + * + * @param censusRequest The request to be converted to DTO + * @return a DTO for CensusRequest + */ + private CensusRequestDTO convertToReqDTO(CensusRequest censusRequest) { + Census census = censusRequest.getCensus(); + + String assignee = !CollectionUtils.isEmpty(census.getAssignee()) ? String.join(",", census.getAssignee()) : null; + + // Creating a new data transfer object (DTO) for Census + CensusDTO censusDTO = CensusDTO.builder() + .id(census.getId()) + .tenantId(census.getTenantId()) + .hierarchyType(census.getHierarchyType()) + .boundaryCode(census.getBoundaryCode()) + .assignee(assignee) + .status(census.getStatus()) + .type(census.getType().toString()) + .totalPopulation(census.getTotalPopulation()) + .populationByDemographics(census.getPopulationByDemographics()) + .jurisdictionMapping(census.getJurisdictionMapping()) + .additionalFields(census.getAdditionalFields()) + .effectiveFrom(census.getEffectiveFrom()) + .effectiveTo(census.getEffectiveTo()) + .source(census.getSource()) + .boundaryAncestralPath(census.getBoundaryAncestralPath().get(0)) + .facilityAssigned(census.getFacilityAssigned()) + .additionalDetails(census.getAdditionalDetails()) + .auditDetails(census.getAuditDetails()) + .build(); + + return CensusRequestDTO.builder() + .requestInfo(censusRequest.getRequestInfo()) + .censusDTO(censusDTO) + .build(); + } +} diff --git a/health-services/census-service/src/main/java/digit/repository/querybuilder/CensusQueryBuilder.java b/health-services/census-service/src/main/java/digit/repository/querybuilder/CensusQueryBuilder.java new file mode 100644 index 00000000000..7eec3f639ca --- /dev/null +++ b/health-services/census-service/src/main/java/digit/repository/querybuilder/CensusQueryBuilder.java @@ -0,0 +1,219 @@ +package digit.repository.querybuilder; + +import digit.config.Configuration; +import digit.util.QueryUtil; +import digit.web.models.CensusSearchCriteria; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; + +@Component +public class CensusQueryBuilder { + + private QueryUtil queryUtil; + + private Configuration config; + + public CensusQueryBuilder(QueryUtil queryUtil, Configuration config) { + this.config = config; + this.queryUtil = queryUtil; + } + + private static final String CENSUS_SEARCH_BASE_QUERY = "SELECT id FROM census cen"; + + private static final String CENSUS_QUERY = "SELECT cen.id as census_id, cen.tenant_id as census_tenant_id, cen.hierarchy_type as census_hierarchy_type, cen.boundary_code as census_boundary_code, cen.type as census_type, cen.total_population as census_total_population, cen.effective_from as census_effective_from, cen.effective_to as census_effective_to, cen.source as census_source, cen.status as census_status, cen.assignee as census_assignee, cen.boundary_ancestral_path as census_boundary_ancestral_path, cen.facility_assigned as census_facility_assigned, cen.additional_details as census_additional_details, cen.created_by as census_created_by, cen.created_time as census_created_time, cen.last_modified_by as census_last_modified_by, cen.last_modified_time as census_last_modified_time, \n" + + "\t pbd.id as population_by_demographics_id, pbd.census_id as population_by_demographics_census_id, pbd.demographic_variable as population_by_demographics_demographic_variable, pbd.population_distribution as population_by_demographics_population_distribution, pbd.created_by as population_by_demographics_created_by, pbd.created_time as population_by_demographics_created_time, pbd.last_modified_by as population_by_demographics_last_modified_by, pbd.last_modified_time as population_by_demographics_last_modified_time, \n" + + "\t adf.id as additional_field_id, adf.census_id as additional_field_census_id, adf.key as additional_field_key, adf.value as additional_field_value, adf.show_on_ui as additional_field_show_on_ui, adf.editable as additional_field_editable, adf.order as additional_field_order \n" + + "\t FROM census cen \n" + + "\t LEFT JOIN population_by_demographics pbd ON cen.id = pbd.census_id \n" + + "\t LEFT JOIN additional_field adf ON cen.id = adf.census_id"; + + private static final String CENSUS_SEARCH_QUERY_ORDER_BY_CLAUSE = " ORDER BY cen.last_modified_time DESC"; + + private static final String CENSUS_SEARCH_QUERY_COUNT_WRAPPER = "SELECT COUNT(id) AS total_count FROM ( "; + + private static final String CENSUS_STATUS_COUNT_QUERY = "SELECT COUNT(id) as census_status_count, status as census_status FROM (SELECT id, status FROM census {INTERNAL_QUERY}) as census_status_map GROUP BY census_status"; + + private static final String BULK_CENSUS_UPDATE_QUERY = "UPDATE census SET status = ?, assignee = ?, last_modified_by = ?, last_modified_time = ?, additional_details = ?, facility_assigned = ? WHERE id = ?"; + + /** + * Constructs a SQL query string for searching Census records based on the provided search criteria. + * Also adds an ORDER BY clause and handles pagination. + * + * @param ids The census ids used for filtering Census records. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A complete SQL query string for searching Census records. + */ + public String getCensusQuery(List ids, List preparedStmtList) { + String query = buildCensusQuery(ids, preparedStmtList); + query = queryUtil.addOrderByClause(query, CENSUS_SEARCH_QUERY_ORDER_BY_CLAUSE); + return query; + } + + private String buildCensusQuery(List ids, List preparedStmtList) { + StringBuilder builder = new StringBuilder(CENSUS_QUERY); + + if (!CollectionUtils.isEmpty(ids)) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" cen.id IN ( ").append(queryUtil.createQuery(ids.size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, ids); + } + + return builder.toString(); + } + + public String getCensusSearchQuery(CensusSearchCriteria censusSearchCriteria, List preparedStmtList) { + String query = buildCensusSearchQuery(censusSearchCriteria, preparedStmtList, Boolean.FALSE, Boolean.FALSE); + query = queryUtil.addOrderByClause(query, CENSUS_SEARCH_QUERY_ORDER_BY_CLAUSE); + query = getPaginatedQuery(query, preparedStmtList, censusSearchCriteria); + return query; + } + + /** + * Constructs the count query to get the total count of census based on search criteria. + * + * @param searchCriteria The criteria used for filtering Census records. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A SQL query string to get the total count of Census records for a given search criteria. + */ + public String getCensusCountQuery(CensusSearchCriteria searchCriteria, List preparedStmtList) { + return buildCensusSearchQuery(searchCriteria, preparedStmtList, Boolean.TRUE, Boolean.FALSE); + } + + /** + * Constructs the status count query to get the count of census based on their current status for the given search criteria + * + * @param searchCriteria The criteria used for filtering Census records. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A SQL query string to get the status count of Census records for a given search criteria. + */ + public String getCensusStatusCountQuery(CensusSearchCriteria searchCriteria, List preparedStmtList) { + CensusSearchCriteria censusSearchCriteria = CensusSearchCriteria.builder().tenantId(searchCriteria.getTenantId()).source(searchCriteria.getSource()).jurisdiction(searchCriteria.getJurisdiction()).build(); + return buildCensusSearchQuery(censusSearchCriteria, preparedStmtList, Boolean.FALSE, Boolean.TRUE); + } + + /** + * Constructs query based on the provided search criteria + * + * @param criteria The criteria used for filtering Census ids. + * @param preparedStmtList A list to store prepared statement parameters. + * @return SQL query string for searching Census ids based on search criteria + */ + private String buildCensusSearchQuery(CensusSearchCriteria criteria, List preparedStmtList, Boolean isCount, Boolean isStatusCount) { + StringBuilder builder = new StringBuilder(CENSUS_SEARCH_BASE_QUERY); + + if(isStatusCount) { + builder = new StringBuilder(); + } + + if (!ObjectUtils.isEmpty(criteria.getId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" id = ?"); + preparedStmtList.add(criteria.getId()); + } + + if (!CollectionUtils.isEmpty(criteria.getIds())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" id IN ( ").append(queryUtil.createQuery(criteria.getIds().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, criteria.getIds()); + } + + if (!ObjectUtils.isEmpty(criteria.getTenantId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" tenant_id = ?"); + preparedStmtList.add(criteria.getTenantId()); + } + + if (!ObjectUtils.isEmpty(criteria.getStatus())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" status = ?"); + preparedStmtList.add(criteria.getStatus()); + } + + if (!ObjectUtils.isEmpty(criteria.getSource())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" source = ?"); + preparedStmtList.add(criteria.getSource()); + } + + if (!ObjectUtils.isEmpty(criteria.getFacilityAssigned())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" facility_assigned = ?"); + preparedStmtList.add(criteria.getFacilityAssigned()); + } + + if (!ObjectUtils.isEmpty(criteria.getEffectiveTo())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + if (criteria.getEffectiveTo() == 0) { + builder.append(" effective_to IS NULL "); + } else { + builder.append(" effective_to = ?"); + preparedStmtList.add(criteria.getEffectiveTo()); + } + } + + if (!CollectionUtils.isEmpty(criteria.getAreaCodes())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" boundary_code IN ( ").append(queryUtil.createQuery(criteria.getAreaCodes().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, criteria.getAreaCodes()); + } + + if (!ObjectUtils.isEmpty(criteria.getAssignee())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" ARRAY [ ").append(queryUtil.createQuery(Collections.singleton(criteria.getAssignee()).size())).append(" ]").append("::text[] "); + builder.append(" && string_to_array(assignee, ',') "); + queryUtil.addToPreparedStatement(preparedStmtList, Collections.singleton(criteria.getAssignee())); + } + + if (!CollectionUtils.isEmpty(criteria.getJurisdiction())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" ARRAY [ ").append(queryUtil.createQuery(criteria.getJurisdiction().size())).append(" ]").append("::text[] "); + builder.append(" && string_to_array(boundary_ancestral_path, '|') "); + queryUtil.addToPreparedStatement(preparedStmtList, criteria.getJurisdiction()); + } + + StringBuilder countQuery = new StringBuilder(); + if (isCount) { + countQuery.append(CENSUS_SEARCH_QUERY_COUNT_WRAPPER).append(builder); + countQuery.append(") AS subquery"); + + return countQuery.toString(); + } + + if (isStatusCount) { + return CENSUS_STATUS_COUNT_QUERY.replace("{INTERNAL_QUERY}", builder); + } + + return builder.toString(); + } + + public String getBulkCensusQuery() { + return BULK_CENSUS_UPDATE_QUERY; + } + + /** + * This method appends pagination i.e. limit and offset to the query. + * + * @param query the query to which pagination is to be added. + * @param preparedStmtList prepared statement list to add limit and offset. + * @return a query with pagination + */ + public String getPaginatedQuery(String query, List preparedStmtList, CensusSearchCriteria searchCriteria) { + StringBuilder paginatedQuery = new StringBuilder(query); + + // Append offset + paginatedQuery.append(" OFFSET ? "); + preparedStmtList.add(!ObjectUtils.isEmpty(searchCriteria.getOffset()) ? searchCriteria.getOffset() : config.getDefaultOffset()); + + // Append limit + paginatedQuery.append(" LIMIT ? "); + preparedStmtList.add(!ObjectUtils.isEmpty(searchCriteria.getLimit()) ? searchCriteria.getLimit() : config.getDefaultLimit()); + + return paginatedQuery.toString(); + } +} diff --git a/health-services/census-service/src/main/java/digit/repository/rowmapper/CensusRowMapper.java b/health-services/census-service/src/main/java/digit/repository/rowmapper/CensusRowMapper.java new file mode 100644 index 00000000000..7edac5005dd --- /dev/null +++ b/health-services/census-service/src/main/java/digit/repository/rowmapper/CensusRowMapper.java @@ -0,0 +1,148 @@ +package digit.repository.rowmapper; + +import digit.util.QueryUtil; +import digit.web.models.AdditionalField; +import digit.web.models.Census; +import digit.web.models.PopulationByDemographic; +import org.egov.common.contract.models.AuditDetails; +import org.postgresql.util.PGobject; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +@Component +public class CensusRowMapper implements ResultSetExtractor> { + + private QueryUtil queryUtil; + + public CensusRowMapper(QueryUtil queryUtil) { + this.queryUtil = queryUtil; + } + + /** + * Creates a list of Census record based on the ResultSet. + * + * @param rs the ResultSet containing data. + * @return a list of Census record + * @throws SQLException + * @throws DataAccessException + */ + @Override + public List extractData(ResultSet rs) throws SQLException, DataAccessException { + Map censusMap = new LinkedHashMap<>(); + Set populationByDemographicSet = new HashSet<>(); + Set additionalFieldSet = new HashSet<>(); + + while (rs.next()) { + String censusId = rs.getString("census_id"); + Census censusEntry = censusMap.get(censusId); + + if (ObjectUtils.isEmpty(censusEntry)) { + censusEntry = new Census(); + populationByDemographicSet.clear(); + additionalFieldSet.clear(); + + // Prepare audit details + AuditDetails auditDetails = AuditDetails.builder().createdBy(rs.getString("census_created_by")).createdTime(rs.getLong("census_created_time")).lastModifiedBy(rs.getString("census_last_modified_by")).lastModifiedTime(rs.getLong("census_last_modified_time")).build(); + + String commaSeparatedAssignee = rs.getString("census_assignee"); + List assignee = !ObjectUtils.isEmpty(commaSeparatedAssignee) ? Arrays.asList(commaSeparatedAssignee.split(",")) : null; + + // Prepare census entry + censusEntry.setId(rs.getString("census_id")); + censusEntry.setTenantId(rs.getString("census_tenant_id")); + censusEntry.setHierarchyType(rs.getString("census_hierarchy_type")); + censusEntry.setBoundaryCode(rs.getString("census_boundary_code")); + censusEntry.setType(Census.TypeEnum.fromValue(rs.getString("census_type"))); + censusEntry.setTotalPopulation(rs.getLong("census_total_population")); + censusEntry.setEffectiveFrom(rs.getLong("census_effective_from")); + censusEntry.setEffectiveTo(rs.getLong("census_effective_to")); + censusEntry.setSource(rs.getString("census_source")); + censusEntry.setStatus(rs.getString("census_status")); + censusEntry.setAssignee(assignee); + censusEntry.setBoundaryAncestralPath(Collections.singletonList(rs.getString("census_boundary_ancestral_path"))); + censusEntry.setFacilityAssigned(rs.getBoolean("census_facility_assigned")); + censusEntry.setAdditionalDetails(queryUtil.parseJson((PGobject) rs.getObject("census_additional_details"))); + censusEntry.setAuditDetails(auditDetails); + } + addPopulationByDemographic(rs, populationByDemographicSet, censusEntry); + addAdditionalField(rs, additionalFieldSet, censusEntry); + + censusMap.put(censusId, censusEntry); + } + + return new ArrayList<>(censusMap.values()); + } + + /** + * Adds a AdditionalField object to the census entry based on the result set. + * + * @param rs The ResultSet containing the data. + * @param additionalFieldSet A set to keep track of added AdditionalField objects. + * @param censusEntry The Census entry to which the AdditionalField object will be added. + * @throws SQLException If an SQL error occurs. + */ + private void addAdditionalField(ResultSet rs, Set additionalFieldSet, Census censusEntry) throws SQLException { + String additionalFieldId = rs.getString("additional_field_id"); + + if (ObjectUtils.isEmpty(additionalFieldId) || additionalFieldSet.contains(additionalFieldId)) { + return; + } + + AdditionalField additionalField = new AdditionalField(); + additionalField.setId(rs.getString("additional_field_id")); + additionalField.setKey(rs.getString("additional_field_key")); + additionalField.setValue(rs.getBigDecimal("additional_field_value")); + additionalField.setShowOnUi(rs.getBoolean("additional_field_show_on_ui")); + additionalField.setEditable(rs.getBoolean("additional_field_editable")); + additionalField.setOrder(rs.getInt("additional_field_order")); + + if (CollectionUtils.isEmpty(censusEntry.getAdditionalFields())) { + List additionalFields = new ArrayList<>(); + additionalFields.add(additionalField); + censusEntry.setAdditionalFields(additionalFields); + } else { + censusEntry.getAdditionalFields().add(additionalField); + } + + additionalFieldSet.add(additionalFieldId); + } + + + /** + * Adds a PopulationByDemographics object to the census entry based on the result set. + * + * @param rs The ResultSet containing the data. + * @param populationByDemographicSet A set to keep track of added PopulationByDemographics objects. + * @param censusEntry The Census entry to which the PopulationByDemographics object will be added. + * @throws SQLException If an SQL error occurs. + */ + private void addPopulationByDemographic(ResultSet rs, Set populationByDemographicSet, Census censusEntry) throws SQLException { + String populationByDemographicId = rs.getString("population_by_demographics_id"); + + if (ObjectUtils.isEmpty(populationByDemographicId) || populationByDemographicSet.contains(populationByDemographicId)) { + return; + } + + PopulationByDemographic populationByDemographic = new PopulationByDemographic(); + populationByDemographic.setId(rs.getString("population_by_demographics_id")); + populationByDemographic.setDemographicVariable(PopulationByDemographic.DemographicVariableEnum.fromValue(rs.getString("population_by_demographics_demographic_variable"))); + populationByDemographic.setPopulationDistribution(queryUtil.parseJson((PGobject) rs.getObject("population_by_demographics_population_distribution"))); + + if (CollectionUtils.isEmpty(censusEntry.getPopulationByDemographics())) { + List populationByDemographicList = new ArrayList<>(); + populationByDemographicList.add(populationByDemographic); + censusEntry.setPopulationByDemographics(populationByDemographicList); + } else { + censusEntry.getPopulationByDemographics().add(populationByDemographic); + } + + populationByDemographicSet.add(populationByDemographicId); + } +} diff --git a/health-services/census-service/src/main/java/digit/repository/rowmapper/StatusCountRowMapper.java b/health-services/census-service/src/main/java/digit/repository/rowmapper/StatusCountRowMapper.java new file mode 100644 index 00000000000..b997f2f933f --- /dev/null +++ b/health-services/census-service/src/main/java/digit/repository/rowmapper/StatusCountRowMapper.java @@ -0,0 +1,30 @@ +package digit.repository.rowmapper; + +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class StatusCountRowMapper implements ResultSetExtractor> { + + @Override + public Map extractData(ResultSet rs) throws SQLException, DataAccessException { + Map statusCountMap = new HashMap<>(); + + while (rs.next()) { + String status = rs.getString("census_status"); + Integer statusCount = rs.getInt("census_status_count"); + + if(!ObjectUtils.isEmpty(status)) + statusCountMap.put(status, statusCount); + } + + return statusCountMap; + } +} diff --git a/health-services/census-service/src/main/java/digit/service/CensusService.java b/health-services/census-service/src/main/java/digit/service/CensusService.java new file mode 100644 index 00000000000..ec37824bfc1 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/service/CensusService.java @@ -0,0 +1,127 @@ +package digit.service; + +import digit.repository.CensusRepository; +import digit.service.enrichment.CensusEnrichment; +import digit.service.enrichment.CensusTimeframeEnrichment; +import digit.service.validator.CensusValidator; +import digit.service.workflow.WorkflowService; +import digit.util.ResponseInfoFactory; +import digit.web.models.BulkCensusRequest; +import digit.web.models.CensusRequest; +import digit.web.models.CensusResponse; +import digit.web.models.CensusSearchRequest; +import org.springframework.stereotype.Service; + +import java.util.Collections; + +@Service +public class CensusService { + + private ResponseInfoFactory responseInfoFactory; + + private CensusRepository repository; + + private CensusValidator validator; + + private CensusEnrichment enrichment; + + private CensusTimeframeEnrichment timeframeEnrichment; + + private WorkflowService workflow; + + public CensusService(ResponseInfoFactory responseInfoFactory, CensusRepository repository, CensusValidator validator, CensusEnrichment enrichment, CensusTimeframeEnrichment timeframeEnrichment, WorkflowService workflow) { + this.responseInfoFactory = responseInfoFactory; + this.repository = repository; + this.validator = validator; + this.enrichment = enrichment; + this.timeframeEnrichment = timeframeEnrichment; + this.workflow = workflow; + } + + /** + * Creates a new census record based on the provided request. + * + * @param request The request containing the census data. + * @return The created census reponse. + */ + public CensusResponse create(CensusRequest request) { + + // Validate census create request + validator.validateCreate(request); + + // Enrich census create request + enrichment.enrichCreate(request); + + // Enrich timeframe for previous census + timeframeEnrichment.enrichPreviousTimeframe(request); + + // Call workflow transition API for status update + workflow.invokeWorkflowForStatusUpdate(request); + + // Delegate creation request to repository + repository.create(request); + + return CensusResponse.builder() + .census(Collections.singletonList(request.getCensus())) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) + .build(); + } + + /** + * Searches for census record based on the provided search criteria. + * + * @param request The search request containing the criteria. + * @return A list of census record that matches the search criteria. + */ + public CensusResponse search(CensusSearchRequest request) { + + return CensusResponse.builder() + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) + .census(repository.search(request.getCensusSearchCriteria())) + .totalCount(repository.count(request.getCensusSearchCriteria())) + .statusCount(repository.statusCount(request)) + .build(); + } + + /** + * Updates an existing census record based on the provided request. + * + * @param request The request containing the updated census data. + * @return The updated census response. + */ + public CensusResponse update(CensusRequest request) { + // Validate census update request + validator.validateUpdate(request); + + // Enrich census update request + enrichment.enrichUpdate(request); + + // Call workflow transition API for status update + workflow.invokeWorkflowForStatusUpdate(request); + + // Delegate update request to repository + repository.update(request); + + return CensusResponse.builder() + .census(Collections.singletonList(request.getCensus())) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) + .build(); + } + + public CensusResponse bulkUpdate(BulkCensusRequest request) { + + // Validate census bulk update request + validator.validateBulkUpdate(request); + + // Call workflow transition for updating status and assignee + workflow.invokeWorkflowForStatusUpdate(request); + + // Delegate bulk update request to repository + repository.bulkUpdate(request); + + return CensusResponse.builder() + .census(request.getCensus()) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) + .build(); + } +} diff --git a/health-services/census-service/src/main/java/digit/service/enrichment/CensusEnrichment.java b/health-services/census-service/src/main/java/digit/service/enrichment/CensusEnrichment.java new file mode 100644 index 00000000000..4754de26c4e --- /dev/null +++ b/health-services/census-service/src/main/java/digit/service/enrichment/CensusEnrichment.java @@ -0,0 +1,218 @@ +package digit.service.enrichment; + +import digit.util.CommonUtil; +import digit.web.models.AdditionalField; +import digit.web.models.Census; +import digit.web.models.CensusRequest; +import digit.web.models.boundary.BoundaryTypeHierarchy; +import digit.web.models.boundary.BoundaryTypeHierarchyDefinition; +import digit.web.models.boundary.EnrichedBoundary; +import digit.web.models.boundary.HierarchyRelation; +import org.egov.common.utils.UUIDEnrichmentUtil; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.egov.common.utils.AuditDetailsEnrichmentUtil.prepareAuditDetails; + +@Component +public class CensusEnrichment { + + private CommonUtil commonUtil; + + public CensusEnrichment(CommonUtil commonUtil) { + this.commonUtil = commonUtil; + } + + /** + * Enriches the CensusRequest for creating a new census record. + * Enriches the given census record with generated IDs for Census and PopulationByDemographics. + * Validates user information, enriches audit details and effectiveFrom for create operation. + * + * @param request The CensusRequest to be enriched. + * @throws CustomException if user information is missing in the request. + */ + public void enrichCreate(CensusRequest request) { + Census census = request.getCensus(); + + // Generate id for census record + UUIDEnrichmentUtil.enrichRandomUuid(census, "id"); + + // Generate id for PopulationByDemographics + if (!CollectionUtils.isEmpty(census.getPopulationByDemographics())) { + census.getPopulationByDemographics().forEach(populationByDemographics -> UUIDEnrichmentUtil.enrichRandomUuid(populationByDemographics, "id")); + } + + // Generate id for additionalFields + census.getAdditionalFields().forEach(additionalField -> UUIDEnrichmentUtil.enrichRandomUuid(additionalField, "id")); + + // Set audit details for census record + census.setAuditDetails(prepareAuditDetails(census.getAuditDetails(), request.getRequestInfo(), Boolean.TRUE)); + + // Enrich effectiveFrom for the census record + census.setEffectiveFrom(census.getAuditDetails().getCreatedTime()); + + denormalizeAdditionalFields(request.getCensus()); + } + + private void denormalizeAdditionalFields(Census census) { + Map fieldsToAdd = census.getAdditionalFields().stream() + .collect(Collectors.toMap(AdditionalField::getKey, AdditionalField::getValue)); + + census.setAdditionalDetails(commonUtil.updateFieldInAdditionalDetails(census.getAdditionalDetails(), fieldsToAdd)); + } + + /** + * Enriches the boundary ancestral path and jurisdiction mapping for the provided boundary code in the census request. + * + * @param census The census record whose boundary ancestral path has to be enriched. + * @param tenantBoundary boundary relationship from the boundary service for the given boundary code. + */ + public void enrichBoundaryAncestralPath(Census census, HierarchyRelation tenantBoundary) { + EnrichedBoundary boundary = tenantBoundary.getBoundary().get(0); + Map jurisdictionMapping = new LinkedHashMap<>(); + + StringBuilder boundaryAncestralPath = new StringBuilder(boundary.getCode()); + jurisdictionMapping.put(boundary.getBoundaryType(), boundary.getCode()); + + // Iterate through the child boundary until there are no more + while (!CollectionUtils.isEmpty(boundary.getChildren())) { + boundary = boundary.getChildren().get(0); + boundaryAncestralPath.append("|").append(boundary.getCode()); + jurisdictionMapping.put(boundary.getBoundaryType(), boundary.getCode()); + } + + // Setting the boundary ancestral path for the provided boundary + census.setBoundaryAncestralPath(Collections.singletonList(boundaryAncestralPath.toString())); + + // Setting jurisdiction mapping for the provided boundary + census.setJurisdictionMapping(jurisdictionMapping); + } + + /** + * Enriches the CensusRequest for updating an existing census record. + * This method enriches the census record for update, validates user information and enriches audit details for update operation. + * + * @param request The CensusRequest to be enriched. + * @throws CustomException if user information is missing in the request. + */ + public void enrichUpdate(CensusRequest request) { + Census census = request.getCensus(); + + // Generate id for populationByDemographics + if (!CollectionUtils.isEmpty(census.getPopulationByDemographics())) { + census.getPopulationByDemographics().forEach(populationByDemographics -> { + if (ObjectUtils.isEmpty(populationByDemographics.getId())) { + UUIDEnrichmentUtil.enrichRandomUuid(populationByDemographics, "id"); + } + }); + } + + //Generate id for additionalFields + census.getAdditionalFields().forEach(additionalField -> { + if (ObjectUtils.isEmpty(additionalField.getId())) { + UUIDEnrichmentUtil.enrichRandomUuid(additionalField, "id"); + } + }); + + // Set last modified time on update call + census.getAuditDetails().setLastModifiedTime(System.currentTimeMillis()); + + denormalizeAdditionalFields(request.getCensus()); + } + + /** + * Helper method to enrich boundary hierarchy mapping. + * Creates a mapping of parentBoundaryType to childBoundaryType from the boundaryTypeHierarchy search response. + * + * @param boundaryTypeHierarchyDef Search response from boundary hierarchy search. + * @param boundaryHierarchyMapping boundary hierarchy map to be enriched. + * @return returns the highest boundary hierarchy for the given hierarchy type. + */ + private String getBoundaryHierarchyMapping(BoundaryTypeHierarchyDefinition boundaryTypeHierarchyDef, Map boundaryHierarchyMapping) { + String highestBoundaryHierarchy = null; + + for (BoundaryTypeHierarchy boundaryTypeHierarchy : boundaryTypeHierarchyDef.getBoundaryHierarchy()) { + if (ObjectUtils.isEmpty(boundaryTypeHierarchy.getParentBoundaryType())) + highestBoundaryHierarchy = boundaryTypeHierarchy.getBoundaryType(); + else + boundaryHierarchyMapping.put(boundaryTypeHierarchy.getParentBoundaryType(), boundaryTypeHierarchy.getBoundaryType()); + } + + return highestBoundaryHierarchy; + } + + /** + * Enriches jurisdiction mapping in census for the given boundary ancestral path. + * + * @param census census data with boundary ancestral path. + * @param boundaryTypeHierarchyDef boundary hierarchy for the given hierarchy type. + */ + public void enrichJurisdictionMapping(Census census, BoundaryTypeHierarchyDefinition boundaryTypeHierarchyDef) { + Map boundaryHierarchyMapping = new HashMap<>(); + + // Enriches the boundaryHierarchyMapping and returns the highest boundary hierarchy for the given hierarchy type. + String highestBoundaryHierarchy = getBoundaryHierarchyMapping(boundaryTypeHierarchyDef, boundaryHierarchyMapping); + + Map jurisdictionMapping = new LinkedHashMap<>(); + String boundaryHierarchy = highestBoundaryHierarchy; + + // Get the list of boundary codes from pipe separated boundaryAncestralPath. + List boundaryCode = getBoundaryCodeFromAncestralPath(census.getBoundaryAncestralPath()); + + // Creates the mapping of boundary hierarchy with the corresponding boundary code. + for (String boundary : boundaryCode) { + jurisdictionMapping.put(boundaryHierarchy, boundary); + boundaryHierarchy = boundaryHierarchyMapping.get(boundaryHierarchy); + } + + census.setJurisdictionMapping(jurisdictionMapping); + } + + /** + * Enriches jurisdiction mapping for the list of census records for the given boundary ancestral path. + * + * @param censusList list of census data with boundary ancestral paths. + * @param boundaryTypeHierarchyDef boundary hierarchy for the given hierarchy type. + */ + public void enrichJurisdictionMapping(List censusList, BoundaryTypeHierarchyDefinition boundaryTypeHierarchyDef) { + Map boundaryHierarchyMapping = new HashMap<>(); + + // Enriches the boundaryHierarchyMapping and returns the highest boundary hierarchy for the given hierarchy type. + String highestBoundaryHierarchy = getBoundaryHierarchyMapping(boundaryTypeHierarchyDef, boundaryHierarchyMapping); + + for (Census census : censusList) { + + Map jurisdictionMapping = new LinkedHashMap<>(); + String boundaryHierarchy = highestBoundaryHierarchy; + + // Get the list of boundary codes from pipe separated boundaryAncestralPath. + List boundaryCode = getBoundaryCodeFromAncestralPath(census.getBoundaryAncestralPath()); + + // Creates the mapping of boundary hierarchy with the corresponding boundary code. + for (String boundary : boundaryCode) { + jurisdictionMapping.put(boundaryHierarchy, boundary); + boundaryHierarchy = boundaryHierarchyMapping.get(boundaryHierarchy); + } + + census.setJurisdictionMapping(jurisdictionMapping); + } + } + + /** + * Converts the boundaryAncestral path from a pipe separated string to an array of boundary codes. + * + * @param boundaryAncestralPath pipe separated boundaryAncestralPath. + * @return a list of boundary codes. + */ + private List getBoundaryCodeFromAncestralPath(List boundaryAncestralPath) { + if (CollectionUtils.isEmpty(boundaryAncestralPath)) { + return Collections.emptyList(); + } + return Arrays.asList(boundaryAncestralPath.get(0).split("\\|")); + } +} diff --git a/health-services/census-service/src/main/java/digit/service/enrichment/CensusTimeframeEnrichment.java b/health-services/census-service/src/main/java/digit/service/enrichment/CensusTimeframeEnrichment.java new file mode 100644 index 00000000000..fdd131957df --- /dev/null +++ b/health-services/census-service/src/main/java/digit/service/enrichment/CensusTimeframeEnrichment.java @@ -0,0 +1,47 @@ +package digit.service.enrichment; + +import digit.repository.CensusRepository; +import digit.web.models.Census; +import digit.web.models.CensusRequest; +import digit.web.models.CensusSearchCriteria; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; + +@Component +public class CensusTimeframeEnrichment { + + private CensusRepository repository; + + public CensusTimeframeEnrichment(CensusRepository repository) { + this.repository = repository; + } + + /** + * Enriches the effectiveTo of previous census records for the same boundary. + * + * @param request The census request. + */ + public void enrichPreviousTimeframe(CensusRequest request) { + Census census = request.getCensus(); + List censusList = repository.search(CensusSearchCriteria.builder().tenantId(census.getTenantId()).areaCodes(Collections.singletonList(census.getBoundaryCode())).effectiveTo(0L).build()); + + if (!CollectionUtils.isEmpty(censusList)) { + censusList.forEach(censusData -> { + censusData.setEffectiveTo(census.getAuditDetails().getCreatedTime()); + updatePreviousCensus(CensusRequest.builder().requestInfo(request.getRequestInfo()).census(censusData).build()); + }); + } + } + + /** + * Updates the timeframe of the previous census records. + * + * @param request the census to be updated. + */ + private void updatePreviousCensus(CensusRequest request) { + repository.update(request); + } +} diff --git a/health-services/census-service/src/main/java/digit/service/validator/CensusValidator.java b/health-services/census-service/src/main/java/digit/service/validator/CensusValidator.java new file mode 100644 index 00000000000..526e2445fb8 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/service/validator/CensusValidator.java @@ -0,0 +1,337 @@ +package digit.service.validator; + +import digit.config.Configuration; +import digit.repository.CensusRepository; +import digit.service.enrichment.CensusEnrichment; +import digit.util.BoundaryUtil; +import digit.util.PlanEmployeeAssignmnetUtil; +import digit.web.models.*; +import digit.web.models.boundary.BoundarySearchResponse; +import digit.web.models.boundary.BoundaryTypeHierarchyResponse; +import digit.web.models.boundary.HierarchyRelation; +import digit.web.models.plan.PlanEmployeeAssignmentResponse; +import digit.web.models.plan.PlanEmployeeAssignmentSearchCriteria; +import digit.web.models.plan.PlanEmployeeAssignmentSearchRequest; +import org.egov.common.contract.request.User; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.stream.Collectors; + +import static digit.config.ServiceConstants.*; + +@Component +public class CensusValidator { + + private BoundaryUtil boundaryUtil; + + private PlanEmployeeAssignmnetUtil employeeAssignmnetUtil; + + private Configuration configs; + + private CensusRepository repository; + + private CensusEnrichment enrichment; + + public CensusValidator(BoundaryUtil boundaryUtil, PlanEmployeeAssignmnetUtil employeeAssignmnetUtil, Configuration configs, CensusRepository repository, CensusEnrichment enrichment) { + this.boundaryUtil = boundaryUtil; + this.employeeAssignmnetUtil = employeeAssignmnetUtil; + this.configs = configs; + this.repository = repository; + this.enrichment = enrichment; + } + + /** + * Validates boundary cade, partner assignment and jurisdiction for census create request + * + * @param request The create request for census + */ + public void validateCreate(CensusRequest request) { + Census census = request.getCensus(); + BoundarySearchResponse boundarySearchResponse = boundaryUtil.fetchBoundaryData(request.getRequestInfo(), census.getBoundaryCode(), census.getTenantId(), census.getHierarchyType(), Boolean.TRUE, Boolean.FALSE); + + // Validate duplicate records for census + validateDuplicateRecords(census); + + // Validate boundary code against boundary service + validateBoundaryCode(boundarySearchResponse, census); + + // Validate partner assignment and jurisdiction against plan service + validatePartnerForCensus(request); + + // Validate keys in additional field + validateAdditionalFields(request); + } + + private void validateDuplicateRecords(Census census) { + List censusResponseFromSearch = repository.search(CensusSearchCriteria.builder().source(census.getSource()).areaCodes(Collections.singletonList(census.getBoundaryCode())).build()); + + if(!CollectionUtils.isEmpty(censusResponseFromSearch)) { + throw new CustomException(CENSUS_ALREADY_EXISTS_CODE, CENSUS_ALREADY_EXISTS_MESSAGE); + } + } + + private void validateAdditionalFields(CensusRequest request) { + Set additionalFieldKeys = new HashSet<>(); + + request.getCensus().getAdditionalFields().forEach(additionalField -> { + if(additionalFieldKeys.contains(additionalField.getKey())) { + throw new CustomException(DUPLICATE_KEY_IN_ADDITIONAL_FIELD_CODE, DUPLICATE_KEY_IN_ADDITIONAL_FIELD_MESSGAE + additionalField.getKey()); + } + additionalFieldKeys.add(additionalField.getKey()); + }); + } + + /** + * Validates the boundary code provided in census request against boundary service. + * + * @param boundarySearchResponse response from the boundary service. + * @param census Census record whose boundary code is to be validated. + */ + private void validateBoundaryCode(BoundarySearchResponse boundarySearchResponse, Census census) { + HierarchyRelation tenantBoundary = boundarySearchResponse.getTenantBoundary().get(0); + + if (CollectionUtils.isEmpty(tenantBoundary.getBoundary())) { + throw new CustomException(NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_CODE, NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_MESSAGE); + } + + // Enrich the boundary ancestral path and jurisdiction mapping for the provided boundary code + enrichment.enrichBoundaryAncestralPath(census, tenantBoundary); + } + + /** + * Validates partner assignment and jurisdiction against plan service + * Also validates the user information within the provided CensusRequest. + * + * @param request the census request + */ + private void validatePartnerForCensus(CensusRequest request) { + + Census census = request.getCensus(); + + // Validate the user information in the request + if (ObjectUtils.isEmpty(request.getRequestInfo().getUserInfo())) { + throw new CustomException(USERINFO_MISSING_CODE, USERINFO_MISSING_MESSAGE); + } + + if (census.getPartnerAssignmentValidationEnabled()) { + User userInfo = request.getRequestInfo().getUserInfo(); + List jurisdiction = Arrays.asList(request.getCensus().getBoundaryAncestralPath().get(0).split("\\|")); + + Set roles = new HashSet<>(configs.getAllowedCensusRoles()); + validateWorkflowAccess(userInfo, census, roles); + + PlanEmployeeAssignmentSearchCriteria searchCriteria = PlanEmployeeAssignmentSearchCriteria.builder() + .employeeId(Collections.singletonList(userInfo.getUuid())) + .planConfigurationId(request.getCensus().getSource()) + .tenantId(request.getCensus().getTenantId()) + .role(roles.stream().toList()) + .jurisdiction(jurisdiction) + .build(); + + PlanEmployeeAssignmentResponse employeeAssignmentResponse = employeeAssignmnetUtil.fetchPlanEmployeeAssignment(PlanEmployeeAssignmentSearchRequest.builder() + .requestInfo(request.getRequestInfo()) + .planEmployeeAssignmentSearchCriteria(searchCriteria) + .build()); + + if (CollectionUtils.isEmpty(employeeAssignmentResponse.getPlanEmployeeAssignment())) { + throw new CustomException(INVALID_PARTNER_CODE, INVALID_PARTNER_MESSAGE); + } + + //enrich jurisdiction of current assignee + request.getCensus().setAssigneeJurisdiction(employeeAssignmentResponse.getPlanEmployeeAssignment().get(0).getJurisdiction()); + } + } + + private void validateWorkflowAccess(User userInfo, Census census, Set roles) { + Boolean hasCensusRoles = userInfo.getRoles().stream() + .anyMatch(role -> configs.getAllowedCensusRoles().contains(role.getCode())); + + Boolean hasWfRestrictedRoles = userInfo.getRoles().stream() + .anyMatch(role -> configs.getWorkflowRestrictedRoles().contains(role.getCode())); + + if(hasWfRestrictedRoles) { + roles.addAll(configs.getWorkflowRestrictedRoles()); + + if(!hasCensusRoles && !ObjectUtils.isEmpty(census.getWorkflow())) + throw new CustomException(UNAUTHORIZED_WORKFLOW_ACCESS_CODE, UNAUTHORIZED_WORKFLOW_ACCESS_MESSAGE); + } + } + + /** + * Validates partner assignment and jurisdiction for census update request. + * + * @param request the update request for Census. + */ + public void validateUpdate(CensusRequest request) { + BoundaryTypeHierarchyResponse boundaryTypeHierarchyResponse = boundaryUtil.fetchBoundaryHierarchy(request.getRequestInfo(), request.getCensus().getTenantId(), request.getCensus().getHierarchyType()); + + // Validate if Census record to be updated exists + validateCensusExistence(request); + + // Validate partner assignment and jurisdiction against plan service + validatePartnerForCensus(request); + + // Validate keys in additional field + validateAdditionalFields(request); + + // Enrich jurisdiction mapping in census + enrichment.enrichJurisdictionMapping(request.getCensus(), boundaryTypeHierarchyResponse.getBoundaryHierarchy().get(0)); + } + + /** + * Validates the existence of census record in the repository + * + * @param request The request containing the census to be validated + */ + private void validateCensusExistence(CensusRequest request) { + Census census = request.getCensus(); + CensusSearchCriteria searchCriteria = CensusSearchCriteria.builder() + .id(census.getId()) + .build(); + + List censusList = repository.search(searchCriteria); + + if (CollectionUtils.isEmpty(censusList)) { + throw new CustomException(INVALID_CENSUS_CODE, INVALID_CENSUS_MESSAGE); + } + + request.getCensus().setBoundaryAncestralPath(censusList.get(0).getBoundaryAncestralPath()); + } + + /** + * Validates census records for bulk update. + * Validates the census attributes, checks if census records to be validated exist. + * Also validates the partner assignment and jurisdiction for the given census records. + * + * @param request the census bulk update request. + */ + public void validateBulkUpdate(BulkCensusRequest request) { + BoundaryTypeHierarchyResponse boundaryTypeHierarchyResponse = boundaryUtil.fetchBoundaryHierarchy(request.getRequestInfo(), request.getCensus().get(0).getTenantId(), request.getCensus().get(0).getHierarchyType()); + + // Validate attributes across each census in the bulk update request + validateCensusAttributes(request); + + // Validate if census in request body exists + validateCensusExistence(request); + + // Validate partner assignment and jurisdiction against plan service + validatePartnerForCensus(request); + + // Enrich jurisdiction mapping in census + enrichment.enrichJurisdictionMapping(request.getCensus(), boundaryTypeHierarchyResponse.getBoundaryHierarchy().get(0)); + } + + /** + * Validates partner assignment and jurisdiction against plan service + * Also validates the user information within the provided CensusRequest. + * + * @param request the census bulk update request + */ + private void validatePartnerForCensus(BulkCensusRequest request) { + + // Validate the user information in the request + if (ObjectUtils.isEmpty(request.getRequestInfo().getUserInfo())) { + throw new CustomException(USERINFO_MISSING_CODE, USERINFO_MISSING_MESSAGE); + } + + List jurisdiction = Arrays.asList(request.getCensus().get(0).getBoundaryAncestralPath().get(0).split("\\|")); + + PlanEmployeeAssignmentSearchCriteria searchCriteria = PlanEmployeeAssignmentSearchCriteria.builder() + .employeeId(Collections.singletonList(request.getRequestInfo().getUserInfo().getUuid())) + .planConfigurationId(request.getCensus().get(0).getSource()) + .tenantId(request.getCensus().get(0).getTenantId()) + .role(configs.getAllowedCensusRoles()) + .jurisdiction(jurisdiction) + .build(); + + PlanEmployeeAssignmentResponse employeeAssignmentResponse = employeeAssignmnetUtil.fetchPlanEmployeeAssignment(PlanEmployeeAssignmentSearchRequest.builder() + .requestInfo(request.getRequestInfo()) + .planEmployeeAssignmentSearchCriteria(searchCriteria) + .build()); + + if (CollectionUtils.isEmpty(employeeAssignmentResponse.getPlanEmployeeAssignment())) { + throw new CustomException(INVALID_PARTNER_CODE, INVALID_PARTNER_MESSAGE); + } + + //enrich jurisdiction of current assignee in all census records + request.getCensus().forEach(census -> census.setAssigneeJurisdiction(employeeAssignmentResponse.getPlanEmployeeAssignment().get(0).getJurisdiction())); + } + + /** + * Validates the existence of bulk census records in the repository + * + * @param request The request containing all the census records to be validated + */ + private void validateCensusExistence(BulkCensusRequest request) { + + // Get all census ids to validate existence + List censusListFromDatabase = repository.search(CensusSearchCriteria.builder() + .ids(request.getCensus().stream().map(Census::getId).collect(Collectors.toSet())) + .offset(0) + .limit(request.getCensus().size()) + .build()); + + // If census id provided is invalid, throw an exception + if (censusListFromDatabase.size() != request.getCensus().size()) { + throw new CustomException(INVALID_CENSUS_CODE, INVALID_CENSUS_MESSAGE); + } + + // Enrich boundary ancestral path for each census object being passed in the request + enrichBoundaryAncestralPath(request, censusListFromDatabase); + + } + + /** + * Enriches the census records with boundary ancestral path from repository. + * + * @param request bulk request with all census records. + * @param censusListFromDatabase existing census records from the repository. + */ + private void enrichBoundaryAncestralPath(BulkCensusRequest request, List censusListFromDatabase) { + Map censusIdVsBoundaryAncestralPathMap = censusListFromDatabase.stream() + .collect(Collectors.toMap(Census::getId, census -> census.getBoundaryAncestralPath().get(0))); + + request.getCensus().forEach(census -> + census.setBoundaryAncestralPath(Collections.singletonList(censusIdVsBoundaryAncestralPathMap + .get(census.getId()))) + ); + } + + /** + * Validates if census records provided in bulk update are unique. + * Checks if all the records have same source and tenant id. + * Also validates if records have same workflow status and action to be taken. + * + * @param request the census bulk update request + */ + private void validateCensusAttributes(BulkCensusRequest request) { + + if (request.getCensus().stream().map(Census::getId).collect(Collectors.toSet()).size() + != request.getCensus().size()) { + throw new CustomException(DUPLICATE_CENSUS_ID_IN_BULK_UPDATE_CODE, DUPLICATE_CENSUS_ID_IN_BULK_UPDATE_MESSAGE); + } + + if (!request.getCensus().stream().allMatch(census -> + census.getTenantId().equals(request.getCensus().get(0).getTenantId()) && + census.getSource().equals(request.getCensus().get(0).getSource()))) { + throw new CustomException(INVALID_SOURCE_OR_TENANT_ID_FOR_BULK_UPDATE_CODE, INVALID_SOURCE_OR_TENANT_ID_FOR_BULK_UPDATE_MESSAGE); + } + + request.getCensus().forEach(census -> { + if (ObjectUtils.isEmpty(census.getWorkflow())) { + throw new CustomException(WORKFLOW_NOT_FOUND_FOR_BULK_UPDATE_CODE, WORKFLOW_NOT_FOUND_FOR_BULK_UPDATE_MESSAGE); + } + }); + + if (!request.getCensus().stream().allMatch(census -> + census.getStatus().equals(request.getCensus().get(0).getStatus()) && + census.getWorkflow().getAction().equals(request.getCensus().get(0).getWorkflow().getAction()))) { + throw new CustomException(DIFFERENT_WORKFLOW_FOR_BULK_UPDATE_CODE, DIFFERENT_WORKFLOW_FOR_BULK_UPDATE_MESSAGE); + } + } +} + diff --git a/health-services/census-service/src/main/java/digit/service/workflow/WorkflowService.java b/health-services/census-service/src/main/java/digit/service/workflow/WorkflowService.java new file mode 100644 index 00000000000..e842c5dae29 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/service/workflow/WorkflowService.java @@ -0,0 +1,331 @@ +package digit.service.workflow; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.config.Configuration; +import digit.repository.ServiceRequestRepository; +import digit.util.PlanEmployeeAssignmnetUtil; +import digit.web.models.BulkCensusRequest; +import digit.web.models.Census; +import digit.web.models.CensusRequest; +import digit.web.models.plan.PlanEmployeeAssignmentResponse; +import digit.web.models.plan.PlanEmployeeAssignmentSearchCriteria; +import digit.web.models.plan.PlanEmployeeAssignmentSearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.models.Workflow; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.request.User; +import org.egov.common.contract.workflow.*; +import org.egov.common.utils.AuditDetailsEnrichmentUtil; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.stream.Collectors; + +import static digit.config.ServiceConstants.*; + +@Service +@Slf4j +public class WorkflowService { + + private ServiceRequestRepository serviceRequestRepository; + + private Configuration config; + + private ObjectMapper mapper; + + private PlanEmployeeAssignmnetUtil planEmployeeAssignmnetUtil; + + public WorkflowService(ServiceRequestRepository serviceRequestRepository, Configuration config, ObjectMapper mapper, PlanEmployeeAssignmnetUtil planEmployeeAssignmnetUtil) { + this.serviceRequestRepository = serviceRequestRepository; + this.config = config; + this.mapper = mapper; + this.planEmployeeAssignmnetUtil = planEmployeeAssignmnetUtil; + } + + /** + * Integrates with the workflow for the given census request. + * If the action is null, it does not proceed with integration. + * + * @param censusRequest The request containing the census object to integrate with the workflow. + */ + public void invokeWorkflowForStatusUpdate(CensusRequest censusRequest) { + if (ObjectUtils.isEmpty(censusRequest.getCensus().getWorkflow())) + return; + + ProcessInstanceRequest processInstanceRequest = createWorkflowRequest(censusRequest); + ProcessInstanceResponse processInstanceResponse = callWorkflowTransition(processInstanceRequest); + + // Setting the status back to the census object from workflow response + censusRequest.getCensus().setStatus(processInstanceResponse.getProcessInstances().get(0).getState().getState()); + + // Enrich audit details after auto assignment is complete + censusRequest.getCensus().setAuditDetails(AuditDetailsEnrichmentUtil + .prepareAuditDetails(censusRequest.getCensus().getAuditDetails(), censusRequest.getRequestInfo(), Boolean.FALSE)); + + } + + /** + * Integrates with the workflow for the given bulk census request. + * + * @param request The request containing the list of census objects to integrate with the workflow. + */ + public void invokeWorkflowForStatusUpdate(BulkCensusRequest request) { + + ProcessInstanceRequest processInstanceRequest = createWorkflowRequest(request); + ProcessInstanceResponse processInstanceResponse = callWorkflowTransition(processInstanceRequest); + + enrichCensusPostTransition(processInstanceResponse, request); + } + + /** + * Enriches the census records in bulk update with audit details and workflow status. + * + * @param processInstanceResponse process instance response containing the current workflow status + * @param request the bulk census request + */ + private void enrichCensusPostTransition(ProcessInstanceResponse processInstanceResponse, BulkCensusRequest request) { + // Update status and audit information post transition + request.getCensus().forEach(census -> { + // Update status of Census + census.setStatus(processInstanceResponse.getProcessInstances().get(0).getState().getState()); + + // Update audit information of census + census.setAuditDetails(AuditDetailsEnrichmentUtil + .prepareAuditDetails(census.getAuditDetails(), request.getRequestInfo(), Boolean.FALSE)); + }); + } + + /** + * Creates a workflow request from the given list of census records in bulk request. + * + * @param request The request containing the list of census to create a workflow request. + * @return The constructed process instance request for the workflow. + */ + private ProcessInstanceRequest createWorkflowRequest(BulkCensusRequest request) { + List processInstanceList = new ArrayList<>(); + + // Perform auto assignment + List assignee = getAssigneeForAutoAssignment(request.getCensus().get(0), request.getRequestInfo()); + + for (Census census : request.getCensus()) { + if (config.getWfSendBackActions().contains(census.getWorkflow().getAction())) { + assignee = Collections.singletonList(census.getAuditDetails().getLastModifiedBy()); + } + + // Set assignee + if (!ObjectUtils.isEmpty(assignee)) + census.getWorkflow().setAssignes(assignee); + + census.setAssignee(assignee); + + // Create process instance object from census + ProcessInstance processInstance = ProcessInstance.builder() + .businessId(census.getId()) + .tenantId(census.getTenantId()) + .businessService(CENSUS_BUSINESS_SERVICE) + .moduleName(MODULE_NAME_VALUE) + .action(census.getWorkflow().getAction()) + .comment(census.getWorkflow().getComments()) + .documents(census.getWorkflow().getDocuments()) + .build(); + + // Enrich user list for process instance + enrichAssignesInProcessInstance(processInstance, census.getWorkflow()); + + // Add entry for bulk transition + processInstanceList.add(processInstance); + } + + return ProcessInstanceRequest.builder() + .requestInfo(request.getRequestInfo()) + .processInstances(processInstanceList) + .build(); + } + + /** + * Calls the workflow transition service and retrieves the process instance response. + * + * @param processInstanceRequest The request containing process instance details for the workflow transition. + * @return The response containing details of the process instances after the transition. + * @throws CustomException if there is an error during the workflow integration. + */ + public ProcessInstanceResponse callWorkflowTransition(ProcessInstanceRequest processInstanceRequest) { + ProcessInstanceResponse processInstanceResponse; + try { + Object response = serviceRequestRepository.fetchResult(getWorkflowTransitionUri(), processInstanceRequest); + processInstanceResponse = mapper.convertValue(response, ProcessInstanceResponse.class); + } catch (Exception e) { + throw new CustomException(WORKFLOW_INTEGRATION_ERROR_CODE, WORKFLOW_INTEGRATION_ERROR_MESSAGE + e.getMessage()); + } + + return processInstanceResponse; + } + + /** + * Creates a workflow request from the given census request. + * + * @param censusRequest The request containing the census to create a workflow request. + * @return The constructed process instance request for the workflow. + */ + public ProcessInstanceRequest createWorkflowRequest(CensusRequest censusRequest) { + Census census = censusRequest.getCensus(); + + // Create process instance object from census + ProcessInstance processInstance = ProcessInstance.builder() + .businessId(census.getId()) + .tenantId(census.getTenantId()) + .businessService(CENSUS_BUSINESS_SERVICE) + .moduleName(MODULE_NAME_VALUE) + .action(census.getWorkflow().getAction()) + .comment(census.getWorkflow().getComments()) + .documents(census.getWorkflow().getDocuments()) + .build(); + + // Perform auto assignment + List assignee = getAssigneeForAutoAssignment(census, censusRequest.getRequestInfo()); + + if (config.getWfSendBackActions().contains(census.getWorkflow().getAction())) { + assignee = Collections.singletonList(census.getAuditDetails().getLastModifiedBy()); + } + + // Set Assignee + if (!ObjectUtils.isEmpty(assignee)) + census.getWorkflow().setAssignes(assignee); + + census.setAssignee(assignee); + + // Enrich user for process instance + enrichAssignesInProcessInstance(processInstance, census.getWorkflow()); + + log.info("Process Instance assignes - " + processInstance.getAssignes()); + return ProcessInstanceRequest.builder() + .requestInfo(censusRequest.getRequestInfo()) + .processInstances(Collections.singletonList(processInstance)) + .build(); + } + + /** + * Enriches the process instance with assignees from the given workflow. + * + * @param processInstance The process instance to enrich with assignees. + * @param workflow The workflow containing assignees to be added to the process instance. + */ + public void enrichAssignesInProcessInstance(ProcessInstance processInstance, Workflow workflow) { + List userList = CollectionUtils.isEmpty(workflow.getAssignes()) + ? new LinkedList<>() + : workflow.getAssignes().stream() + .map(assignee -> User.builder().uuid(assignee).build()) + .toList(); + + processInstance.setAssignes(userList); + } + + /** + * Constructs the URI for the workflow service transition API. + * + * @return The StringBuilder containing the constructed workflow transition URI. + */ + private StringBuilder getWorkflowTransitionUri() { + return new StringBuilder().append(config.getWfHost()).append(config.getWfTransitionPath()); + } + + /** + * Returns a list of assignee based on the workflow action and jurisdiction hierarchy. + * Retrieves jurisdiction boundaries from the census request and searches for matching employee assignments. + * + * For INITIATE actions, assigns the employee from the lowest boundary. + * For INTERMEDIATE actions (non-ROOT_APPROVER), assigns an employee from a higher-level boundary. + * For SEND_BACK actions, assigns the last modified user. + * + * @param census the census object containing workflow and jurisdiction details + * @param requestInfo the requestInfo + */ + private List getAssigneeForAutoAssignment(Census census, RequestInfo requestInfo) { + String[] allHierarchiesBoundaryCodes = census.getBoundaryAncestralPath().get(0).split(PIPE_REGEX); + String[] hierarchiesBoundaryCodes = Arrays.copyOf(allHierarchiesBoundaryCodes, allHierarchiesBoundaryCodes.length - 1); + + PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria = + PlanEmployeeAssignmentSearchCriteria.builder() + .tenantId(census.getTenantId()) + .jurisdiction(Arrays.stream(hierarchiesBoundaryCodes).toList()) + .planConfigurationId(census.getSource()) + .role(config.getAllowedCensusRoles()) + .build(); + + //search for plan-employee assignments for the ancestral hierarchy codes. + PlanEmployeeAssignmentResponse planEmployeeAssignmentResponse = planEmployeeAssignmnetUtil.fetchPlanEmployeeAssignment(PlanEmployeeAssignmentSearchRequest.builder() + .planEmployeeAssignmentSearchCriteria(planEmployeeAssignmentSearchCriteria) + .requestInfo(requestInfo).build()); + + // Create a map of jurisdiction to list of employeeIds + Map> jurisdictionToEmployeeMap = planEmployeeAssignmentResponse.getPlanEmployeeAssignment().stream() + .filter(assignment -> !CollectionUtils.isEmpty(assignment.getJurisdiction())) + .flatMap(assignment -> { + String employeeId = assignment.getEmployeeId(); + return assignment.getJurisdiction().stream() + .filter(jurisdiction -> Arrays.asList(hierarchiesBoundaryCodes).contains(jurisdiction)) + .map(jurisdiction -> new AbstractMap.SimpleEntry<>(jurisdiction, employeeId)); + }) + .collect(Collectors.groupingBy( + Map.Entry::getKey, // jurisdiction as the key + LinkedHashMap::new, // Preserve insertion order + Collectors.mapping( + Map.Entry::getValue, // employee IDs as values + Collectors.toList() // Collect employee IDs into a List + ) + )); + + List assignee = null; //assignee will remain null in case terminate actions are being taken + + String action = census.getWorkflow().getAction(); + if (config.getWfInitiateActions().contains(action)) { + for (int i = hierarchiesBoundaryCodes.length - 1; i >= 0; i--) { + assignee = jurisdictionToEmployeeMap.get(hierarchiesBoundaryCodes[i]); + if (assignee != null) + break; // Stop iterating once an assignee is found + } + } else if (config.getWfIntermediateActions().contains(action)) { + assignee = assignToHigherBoundaryLevel(hierarchiesBoundaryCodes, census, jurisdictionToEmployeeMap); + } + + return assignee; + } + + /** + * Assigns a list of employees from a higher-level jurisdiction in the hierarchy. + * Iterates through boundary codes, checking if they match the assignee's jurisdiction. + * If a higher-level boundary has an assigned employee, returns that employee's ID. + * + * @param heirarchysBoundaryCodes boundary codes representing the hierarchy + * @param census the census object with jurisdiction details + * @param jurisdictionToEmployeeMap map of jurisdiction codes to employee IDs + * @return the list of employee IDs from the higher boundary, or null if + */ + public List assignToHigherBoundaryLevel(String[] heirarchysBoundaryCodes, Census census, Map> jurisdictionToEmployeeMap) { + for (int i = heirarchysBoundaryCodes.length - 1; i >= 0; i--) { + String boundaryCode = heirarchysBoundaryCodes[i]; + + // Check if this boundary code is present in assigneeJurisdiction + if (census.getAssigneeJurisdiction().contains(boundaryCode)) { + + for (int j = i - 1; j >= 0; j--) { + // Check the next higher level in the hierarchy (one index above the match) + String higherBoundaryCode = heirarchysBoundaryCodes[j]; + + // Fetch the employeeId from the map for the higher boundary code + List employeeId = jurisdictionToEmployeeMap.get(higherBoundaryCode); + + // If an employee is found, set them as the assignee and break the loop + if (employeeId != null) { + return employeeId; + } + } + } + } + return null; + } + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/util/BoundaryUtil.java b/health-services/census-service/src/main/java/digit/util/BoundaryUtil.java new file mode 100644 index 00000000000..8e977cf3ccb --- /dev/null +++ b/health-services/census-service/src/main/java/digit/util/BoundaryUtil.java @@ -0,0 +1,111 @@ +package digit.util; + +import digit.config.Configuration; +import digit.web.models.RequestInfoWrapper; +import digit.web.models.boundary.BoundarySearchResponse; +import digit.web.models.boundary.BoundaryTypeHierarchyResponse; +import digit.web.models.boundary.BoundaryTypeHierarchySearchCriteria; +import digit.web.models.boundary.BoundaryTypeHierarchySearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; + +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_BOUNDARY_DETAILS; +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_BOUNDARY_HIERARCHY_DETAILS; + +@Slf4j +@Component +public class BoundaryUtil { + + private RestTemplate restTemplate; + + private Configuration configs; + + public BoundaryUtil(RestTemplate restTemplate, Configuration configs) { + this.restTemplate = restTemplate; + this.configs = configs; + } + + /** + * This method fetches boundary relationships from Boundary service for the provided boundaryCode and hierarchyType. + * + * @param requestInfo request info from the request. + * @param boundaryCode boundary code from the request. + * @param tenantId tenant id from the request. + * @param hierarchyType hierarchy type from the request. + * @param includeParents is true if you want to include parent boundary. + * @param includeChildren is true if you want to include child boundary. + * @return returns the response from boundary service + */ + public BoundarySearchResponse fetchBoundaryData(RequestInfo requestInfo, String boundaryCode, String tenantId, String hierarchyType, Boolean includeParents, Boolean includeChildren) { + + // Create Boundary Relationship search uri + Map uriParameters = new HashMap<>(); + StringBuilder uri = getBoundaryRelationshipSearchUri(uriParameters, boundaryCode, tenantId, hierarchyType, includeParents, includeChildren); + + // Create request body + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + BoundarySearchResponse boundarySearchResponse = new BoundarySearchResponse(); + + try { + boundarySearchResponse = restTemplate.postForObject(uri.toString(), requestInfoWrapper, BoundarySearchResponse.class, uriParameters); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_BOUNDARY_DETAILS, e); + } + + return boundarySearchResponse; + } + + /** + * This method creates Boundary service uri with query parameters + * + * @param uriParameters map that stores values corresponding to the placeholder in uri + * @param boundaryCode boundary code from the request. + * @param tenantId tenant id from the request. + * @param hierarchyType hierarchy type from the request. + * @param includeParents is true if you want to include parent boundary. + * @param includeChildren is true if you want to include child boundary. + * @return a complete boundary service uri + */ + private StringBuilder getBoundaryRelationshipSearchUri(Map uriParameters, String boundaryCode, String tenantId, String hierarchyType, Boolean includeParents, Boolean includeChildren) { + StringBuilder uri = new StringBuilder(); + uri.append(configs.getBoundaryServiceHost()).append(configs.getBoundaryRelationshipSearchEndpoint()).append("?codes={boundaryCode}&includeParents={includeParents}&includeChildren={includeChildren}&tenantId={tenantId}&hierarchyType={hierarchyType}"); + + uriParameters.put("boundaryCode", boundaryCode); + uriParameters.put("tenantId", tenantId); + uriParameters.put("includeParents", includeParents.toString()); + uriParameters.put("includeChildren", includeChildren.toString()); + uriParameters.put("hierarchyType", hierarchyType); + + return uri; + } + + public BoundaryTypeHierarchyResponse fetchBoundaryHierarchy(RequestInfo requestInfo, String tenantId, String hierarchyType) { + + // Create Boundary hierarchy search uri + String uri = getBoundaryHierarchySearchUri(); + + // Create request body + BoundaryTypeHierarchySearchCriteria searchCriteria = BoundaryTypeHierarchySearchCriteria.builder().tenantId(tenantId).hierarchyType(hierarchyType).build(); + BoundaryTypeHierarchySearchRequest searchRequest = BoundaryTypeHierarchySearchRequest.builder().requestInfo(requestInfo).boundaryTypeHierarchySearchCriteria(searchCriteria).build(); + BoundaryTypeHierarchyResponse searchResponse = new BoundaryTypeHierarchyResponse(); + + try { + searchResponse = restTemplate.postForObject(uri, searchRequest, BoundaryTypeHierarchyResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_BOUNDARY_HIERARCHY_DETAILS, e); + } + + return searchResponse; + } + + private String getBoundaryHierarchySearchUri() { + StringBuilder uri = new StringBuilder(); + uri.append(configs.getBoundaryServiceHost()).append(configs.getBoundaryHierarchySearchEndpoint()); + return uri.toString(); + } +} diff --git a/health-services/census-service/src/main/java/digit/util/BusinessServiceUtil.java b/health-services/census-service/src/main/java/digit/util/BusinessServiceUtil.java new file mode 100644 index 00000000000..ce127d4c637 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/util/BusinessServiceUtil.java @@ -0,0 +1,81 @@ +package digit.util; + +import digit.config.Configuration; +import digit.models.coremodels.RequestInfoWrapper; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.workflow.BusinessService; +import org.egov.common.contract.workflow.BusinessServiceResponse; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class BusinessServiceUtil { + + private RestTemplate restTemplate; + + private Configuration config; + + public BusinessServiceUtil(RestTemplate restTemplate, Configuration configs) { + this.restTemplate = restTemplate; + this.config = configs; + } + + /** + * This method fetches business service details for the given tenant id and business service. + * + * @param requestInfo the request info from request. + * @param businessService businessService whose details are to be searched. + * @param tenantId tenantId from request. + * @return returns the business service response for the given tenant id and business service. + */ + public BusinessService fetchBusinessService(RequestInfo requestInfo, String businessService, String tenantId) { + + // Get business service uri + Map uriParameters = new HashMap<>(); + String uri = getBusinessServiceUri(businessService, tenantId, uriParameters); + + // Create request body + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + BusinessServiceResponse businessServiceResponse = new BusinessServiceResponse(); + + try { + businessServiceResponse = restTemplate.postForObject(uri, requestInfoWrapper, BusinessServiceResponse.class, uriParameters); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_BUSINESS_SERVICE_DETAILS, e); + } + + if (CollectionUtils.isEmpty(businessServiceResponse.getBusinessServices())) { + throw new CustomException(NO_BUSINESS_SERVICE_DATA_FOUND_CODE, NO_BUSINESS_SERVICE_DATA_FOUND_MESSAGE); + } + + return businessServiceResponse.getBusinessServices().get(0); + } + + /** + * This method creates business service uri with query parameters + * + * @param businessService businessService whose details are to be searched. + * @param tenantId tenant id from the request. + * @param uriParameters map that stores values corresponding to the placeholder in uri + * @return + */ + private String getBusinessServiceUri(String businessService, String tenantId, Map uriParameters) { + + StringBuilder uri = new StringBuilder(); + uri.append(config.getWfHost()).append(config.getBusinessServiceSearchEndpoint()).append("?tenantId={tenantId}&businessServices={businessService}"); + + uriParameters.put("tenantId", tenantId); + uriParameters.put("businessService", businessService); + + return uri.toString(); + } +} diff --git a/health-services/census-service/src/main/java/digit/util/CommonUtil.java b/health-services/census-service/src/main/java/digit/util/CommonUtil.java new file mode 100644 index 00000000000..bf473f57cac --- /dev/null +++ b/health-services/census-service/src/main/java/digit/util/CommonUtil.java @@ -0,0 +1,151 @@ +package digit.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import digit.web.models.CensusSearchCriteria; +import digit.web.models.CensusSearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.workflow.BusinessService; +import org.egov.common.contract.workflow.State; +import org.egov.tracer.model.CustomException; +import org.postgresql.util.PGobject; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.sql.SQLException; +import java.util.*; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class CommonUtil { + + private ObjectMapper objectMapper; + + private BusinessServiceUtil businessServiceUtil; + + public CommonUtil(ObjectMapper objectMapper, BusinessServiceUtil businessServiceUtil) { + this.objectMapper = objectMapper; + this.businessServiceUtil = businessServiceUtil; + } + + /** + * Adds or updates the value of provided field in the additional details object. + * @param additionalDetails + * @param fieldToUpdate + * @param updatedValue + * @return + */ + public Map updateFieldInAdditionalDetails(Object additionalDetails, String fieldToUpdate, String updatedValue) { + try { + + // Get or create the additionalDetails as an ObjectNode + ObjectNode objectNode = additionalDetails instanceof NullNode + ? objectMapper.createObjectNode() + : objectMapper.convertValue(additionalDetails, ObjectNode.class); + + // Update or Add the field in additional details object + objectNode.put(fieldToUpdate, updatedValue); + + // Convert updated ObjectNode back to a Map and set it in 'additionalDetails' + Map updatedDetails = objectMapper.convertValue(objectNode, Map.class); + return updatedDetails; + + } catch (Exception e) { + throw new CustomException(ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE, ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE); + } + } + + public Map updateFieldInAdditionalDetails(Object additionalDetails, Map fieldsToBeUpdated) { + try { + + // Get or create the additionalDetails as an ObjectNode + ObjectNode objectNode = (additionalDetails == null || additionalDetails instanceof NullNode) + ? objectMapper.createObjectNode() + : objectMapper.convertValue(additionalDetails, ObjectNode.class); + + // Update or add the field in additional details object + fieldsToBeUpdated.forEach((key, value) -> objectNode.set(key, objectMapper.valueToTree(value))); + + // Convert updated ObjectNode back to a Map + return objectMapper.convertValue(objectNode, Map.class); + + } catch (Exception e) { + throw new CustomException(ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE, ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE + e); + } + } + + /** + * Removes the field to be removed from the additional details object. + * @param additionalDetails + * @param fieldToBeRemoved + * @return + */ + public Map removeFieldFromAdditionalDetails(Object additionalDetails, String fieldToBeRemoved) { + Map additionalDetailsMap = objectMapper.convertValue(additionalDetails, Map.class); + additionalDetailsMap.remove(fieldToBeRemoved); + + return additionalDetailsMap; + } + + /** + * Creates the census search request for the provided details. + * @param tenantId + * @param planConfigId + * @param serviceBoundary + * @return + */ + public CensusSearchRequest getCensusSearchRequest(String tenantId, String planConfigId, String serviceBoundary, List initiallySetServiceBoundaries, RequestInfo requestInfo) { + Set areaCodesForSearch = new HashSet<>(); + + areaCodesForSearch.addAll(Arrays.asList(serviceBoundary.split(","))); + areaCodesForSearch.addAll(initiallySetServiceBoundaries); + + CensusSearchCriteria searchCriteria = CensusSearchCriteria.builder() + .tenantId(tenantId) + .source(planConfigId) + .areaCodes(areaCodesForSearch.stream().toList()) + .offset(0) + .limit(areaCodesForSearch.size()) + .build(); + + return CensusSearchRequest.builder().requestInfo(requestInfo).censusSearchCriteria(searchCriteria).build(); + } + + /** + * Creates a list of all the workflow states for the provided business service. + * @param requestInfo + * @param businessService + * @param tenantId + * @return + */ + public List getStatusFromBusinessService(RequestInfo requestInfo, String businessService, String tenantId) { + BusinessService businessServices = businessServiceUtil.fetchBusinessService(requestInfo, businessService, tenantId); + + return businessServices.getStates().stream() + .map(State::getState) + .filter(state -> !ObjectUtils.isEmpty(state)) + .toList(); + } + + public PGobject convertToPgObject(Object additionalDetails) { + PGobject pGobject = new PGobject(); + + try { + String json = objectMapper.writeValueAsString(additionalDetails); + + pGobject.setType("jsonb"); + pGobject.setValue(json); + } catch (JsonProcessingException e) { + log.error("Error while processing JSON object to string", e); + } catch (SQLException e) { + log.error("Error while setting JSONB object", e); + } + + return pGobject; + } +} diff --git a/health-services/census-service/src/main/java/digit/util/PlanEmployeeAssignmnetUtil.java b/health-services/census-service/src/main/java/digit/util/PlanEmployeeAssignmnetUtil.java new file mode 100644 index 00000000000..0cb18bdb6db --- /dev/null +++ b/health-services/census-service/src/main/java/digit/util/PlanEmployeeAssignmnetUtil.java @@ -0,0 +1,59 @@ +package digit.util; + +import digit.config.Configuration; +import digit.web.models.plan.PlanEmployeeAssignmentResponse; +import digit.web.models.plan.PlanEmployeeAssignmentSearchCriteria; +import digit.web.models.plan.PlanEmployeeAssignmentSearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.List; + +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_EMPLOYEE_ASSIGNMENT_DETAILS; + +@Slf4j +@Component +public class PlanEmployeeAssignmnetUtil { + + private RestTemplate restTemplate; + + private Configuration config; + + public PlanEmployeeAssignmnetUtil(RestTemplate restTemplate, Configuration configs) { + this.restTemplate = restTemplate; + this.config = configs; + } + + /** + * This method fetches plan employee assignment from plan service for provided employeeID. + * @param planEmployeeAssignmentSearchRequest request containint the planEmployeeAssignment search criteria + * @return returns planEmployeeAssignment for provided search criteria. + */ + public PlanEmployeeAssignmentResponse fetchPlanEmployeeAssignment(PlanEmployeeAssignmentSearchRequest planEmployeeAssignmentSearchRequest) { + + // Get plan employee assignment uri + StringBuilder uri = getPlanEmployeeAssignmentUri(); + + PlanEmployeeAssignmentResponse response = new PlanEmployeeAssignmentResponse(); + + try { + response = restTemplate.postForObject(uri.toString(), planEmployeeAssignmentSearchRequest, PlanEmployeeAssignmentResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_EMPLOYEE_ASSIGNMENT_DETAILS, e); + } + + return response; + } + + /** + * This method creates the uri for plan employee assignment search + * + * @return uri for plan employee assignment search + */ + private StringBuilder getPlanEmployeeAssignmentUri() { + StringBuilder uri = new StringBuilder(); + return uri.append(config.getPlanServiceHost()).append(config.getPlanEmployeeAssignmentSearchEndpoint()); + } +} diff --git a/health-services/census-service/src/main/java/digit/util/QueryUtil.java b/health-services/census-service/src/main/java/digit/util/QueryUtil.java new file mode 100644 index 00000000000..3d9354df7a3 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/util/QueryUtil.java @@ -0,0 +1,139 @@ +package digit.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.config.Configuration; +import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.model.CustomException; +import org.postgresql.util.PGobject; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.IntStream; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class QueryUtil { + + private Configuration config; + + private ObjectMapper objectMapper; + + private QueryUtil(Configuration config, ObjectMapper objectMapper) { + this.config = config; + this.objectMapper = objectMapper; + } + + /** + * This method adds "WHERE" clause and "AND" condition depending on preparedStatementList i.e., + * if preparedStatementList is empty, it will understand that it is the first clause being added so it + * will add "WHERE" to the query and otherwise it will + * + * @param query query to which Clause is to be added. + * @param preparedStmtList prepared statement list to check which clause to add. + */ + public void addClauseIfRequired(StringBuilder query, List preparedStmtList) { + if (preparedStmtList.isEmpty()) { + query.append(" WHERE "); + } else { + query.append(" AND "); + } + } + + /** + * This method returns a string with placeholders equal to the number of values that need to be put inside + * "IN" clause + * + * @param size number of placeholders to be added. + * @return a string with provided number of placeholders. + */ + public String createQuery(Integer size) { + StringBuilder builder = new StringBuilder(); + + IntStream.range(0, size).forEach(i -> { + builder.append(" ?"); + if (i != size - 1) + builder.append(","); + }); + + return builder.toString(); + } + + /** + * This method adds a set of String values into preparedStatementList. + * + * @param preparedStmtList prepared statement list to add Ids. + * @param ids set of Ids to be added. + */ + public void addToPreparedStatement(List preparedStmtList, Set ids) { + ids.forEach(id -> { + preparedStmtList.add(id); + }); + } + + public void addToPreparedStatement(List preparedStmtList, List ids) { + ids.forEach(id -> { + preparedStmtList.add(id); + }); + } + + /** + * This method appends order by clause to the query. + * + * @param query the query to which orderBy clause is to be added. + * @param orderByClause the orderBy clause to be added. + * @return a query with orderBy clause. + */ + public String addOrderByClause(String query, String orderByClause) { + return query + orderByClause; + } + + /** + * This method appends pagination i.e. limit and offset to the query. + * + * @param query the query to which pagination is to be added. + * @param preparedStmtList prepared statement list to add limit and offset. + * @return a query with pagination + */ + public String getPaginatedQuery(String query, List preparedStmtList) { + StringBuilder paginatedQuery = new StringBuilder(query); + + // Append offset + paginatedQuery.append(" OFFSET ? "); + preparedStmtList.add(config.getDefaultOffset()); + + // Append limit + paginatedQuery.append(" LIMIT ? "); + preparedStmtList.add(config.getDefaultLimit()); + + return paginatedQuery.toString(); + } + + /** + * This method is used to extract and parse JSON data into a JsonNode object. + * + * @param pgObject postgreSQL specific object. + * @return returns a JsonNode. + */ + public JsonNode parseJson(PGobject pgObject) { + + try { + if (!ObjectUtils.isEmpty(pgObject)) { + return objectMapper.readTree(pgObject.getValue()); + } + } catch (IOException e) { + log.error("Error parsing PGobject value: " + pgObject, e); + throw new CustomException(PARSING_ERROR_CODE, PARSING_ERROR_MESSAGE); + } + + // Return empty JsonNode if pgObject is empty + return objectMapper.createObjectNode(); + } + + +} diff --git a/health-services/census-service/src/main/java/digit/util/ResponseInfoFactory.java b/health-services/census-service/src/main/java/digit/util/ResponseInfoFactory.java new file mode 100644 index 00000000000..a10e7f50841 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/util/ResponseInfoFactory.java @@ -0,0 +1,27 @@ +package digit.util; + +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.stereotype.Component; + +import static digit.config.ServiceConstants.*; + +@Component +public class ResponseInfoFactory { + + public ResponseInfo createResponseInfoFromRequestInfo(final RequestInfo requestInfo, final Boolean success) { + + final String apiId = requestInfo != null ? requestInfo.getApiId() : ""; + final String ver = requestInfo != null ? requestInfo.getVer() : ""; + Long ts = null; + if (requestInfo != null) + ts = requestInfo.getTs(); + + final String msgId = requestInfo != null ? requestInfo.getMsgId() : ""; + final String responseStatus = success ? SUCCESSFUL : FAILED; + + return ResponseInfo.builder().apiId(apiId).ver(ver).ts(ts).resMsgId(RES_MSG_ID).msgId(msgId).resMsgId(RES_MSG_ID) + .status(responseStatus).build(); + } + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/web/controllers/CensusController.java b/health-services/census-service/src/main/java/digit/web/controllers/CensusController.java new file mode 100644 index 00000000000..8feabf4b455 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/controllers/CensusController.java @@ -0,0 +1,76 @@ +package digit.web.controllers; + +import digit.service.CensusService; +import digit.web.models.BulkCensusRequest; +import digit.web.models.CensusRequest; +import digit.web.models.CensusResponse; +import digit.web.models.CensusSearchRequest; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + + +@Controller +public class CensusController { + + private CensusService censusService; + + public CensusController(CensusService censusService) { + this.censusService = censusService; + } + + /** + * Request handler for serving census create requests + * + * @param body + * @return + */ + @RequestMapping(value = "/_create", method = RequestMethod.POST) + public ResponseEntity create(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody CensusRequest body) { + CensusResponse response = censusService.create(body); + return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); + } + + /** + * Request handler for serving census search requests + * + * @param body + * @return + */ + @RequestMapping(value = "/_search", method = RequestMethod.POST) + public ResponseEntity search(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody CensusSearchRequest body) { + CensusResponse response = censusService.search(body); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + /** + * Request handler for serving census update requests + * + * @param body + * @return + */ + @RequestMapping(value = "/_update", method = RequestMethod.POST) + public ResponseEntity update(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody CensusRequest body) { + CensusResponse response = censusService.update(body); + return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); + } + + /** + * Request handler for serving bulk census update requests + * + * @param body + * @return + */ + @RequestMapping(value = "/bulk/_update", method = RequestMethod.POST) + public ResponseEntity bulkUpdate(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody BulkCensusRequest body) { + CensusResponse response = censusService.bulkUpdate(body); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Assumption.java b/health-services/census-service/src/main/java/digit/web/models/AdditionalField.java similarity index 52% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Assumption.java rename to health-services/census-service/src/main/java/digit/web/models/AdditionalField.java index b8ed98d54e8..10351283cdb 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Assumption.java +++ b/health-services/census-service/src/main/java/digit/web/models/AdditionalField.java @@ -1,8 +1,9 @@ -package org.egov.processor.web.models; +package digit.web.models; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,34 +13,36 @@ import java.math.BigDecimal; /** - * Assumption + * AdditionalField */ @Validated @Data @AllArgsConstructor @NoArgsConstructor @Builder -public class Assumption { +public class AdditionalField { + @JsonProperty("id") @Valid @Size(min = 2, max = 64) private String id = null; @JsonProperty("key") + @Valid @NotNull - @Size(min = 1, max = 256) private String key = null; @JsonProperty("value") - @NotNull @Valid - @DecimalMin(value = "0.01", inclusive = true, message = "Assumption value must be greater than 0") - @DecimalMax(value = "999.99", inclusive = true, message = "Assumption value must be less than 1000") - @Digits(integer = 3, fraction = 2, message = "Assumption value must have up to 3 digits and up to 2 decimal points") + @NotNull private BigDecimal value = null; - @JsonProperty("active") - @NotNull - private Boolean active = true; + @JsonProperty("showOnUi") + private Boolean showOnUi = Boolean.TRUE; + + @JsonProperty("editable") + private Boolean editable = Boolean.TRUE; + @JsonProperty("order") + private Integer order = null; } diff --git a/health-services/census-service/src/main/java/digit/web/models/BulkCensusRequest.java b/health-services/census-service/src/main/java/digit/web/models/BulkCensusRequest.java new file mode 100644 index 00000000000..e88178822bb --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/BulkCensusRequest.java @@ -0,0 +1,33 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * BulkCensusRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BulkCensusRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("Census") + @Valid + private List census = null; + + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/Census.java b/health-services/census-service/src/main/java/digit/web/models/Census.java new file mode 100644 index 00000000000..55ed850ff18 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/Census.java @@ -0,0 +1,142 @@ +package digit.web.models; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.List; +import java.util.Map; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; + + +/** + * Census + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Census { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("tenantId") + @NotNull + private String tenantId = null; + + @JsonProperty("hierarchyType") + @NotNull + private String hierarchyType = null; + + @JsonProperty("boundaryCode") + @NotNull + private String boundaryCode = null; + + @JsonProperty("assignee") + private List assignee = null; + + @JsonProperty("status") + @Size(max = 64) + private String status = null; + + @JsonProperty("type") + @NotNull + private TypeEnum type = null; + + @JsonProperty("totalPopulation") + @NotNull + private Long totalPopulation = null; + + @JsonProperty("populationByDemographics") + @Valid + private List populationByDemographics = null; + + @JsonProperty("additionalFields") + @Valid + private List additionalFields = null; + + @JsonProperty("effectiveFrom") + private Long effectiveFrom = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("source") + @NotNull + private String source = null; + + @JsonIgnore + private List boundaryAncestralPath = null; + + @JsonIgnore + @Builder.Default + private Boolean partnerAssignmentValidationEnabled = Boolean.TRUE; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("jurisdictionMapping") + private Map jurisdictionMapping; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("auditDetails") + private @Valid AuditDetails auditDetails; + + /** + * Gets or Sets type + */ + public enum TypeEnum { + PEOPLE("people"), + ANIMALS("animals"), + PLANTS("plants"), + OTHER("other"); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String text) { + for (TypeEnum b : TypeEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + } + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/CensusDTO.java b/health-services/census-service/src/main/java/digit/web/models/CensusDTO.java new file mode 100644 index 00000000000..02d3c7d1b2b --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/CensusDTO.java @@ -0,0 +1,104 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.Map; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; + + +/** + * CensusDTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusDTO { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("tenantId") + @NotNull + private String tenantId = null; + + @JsonProperty("hierarchyType") + @NotNull + private String hierarchyType = null; + + @JsonProperty("boundaryCode") + @NotNull + private String boundaryCode = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("status") + private String status = null; + + @JsonProperty("type") + @NotNull + private String type = null; + + @JsonProperty("totalPopulation") + @NotNull + private Long totalPopulation = null; + + @JsonProperty("populationByDemographics") + @Valid + private List populationByDemographics = null; + + @JsonProperty("additionalFields") + @Valid + private List additionalFields = null; + + @JsonProperty("effectiveFrom") + private Long effectiveFrom = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("source") + @NotNull + private String source = null; + + @JsonProperty("boundaryAncestralPath") + private String boundaryAncestralPath = null; + + @JsonIgnore + private boolean partnerAssignmentValidationEnabled; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("jurisdictionMapping") + private Map jurisdictionMapping; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("auditDetails") + private @Valid AuditDetails auditDetails; +} diff --git a/health-services/census-service/src/main/java/digit/web/models/CensusRequest.java b/health-services/census-service/src/main/java/digit/web/models/CensusRequest.java new file mode 100644 index 00000000000..d8755bbbda9 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/CensusRequest.java @@ -0,0 +1,31 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("Census") + @Valid + private Census census = null; + + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/CensusRequestDTO.java b/health-services/census-service/src/main/java/digit/web/models/CensusRequestDTO.java new file mode 100644 index 00000000000..7b1ced9a7db --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/CensusRequestDTO.java @@ -0,0 +1,31 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusRequestDTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusRequestDTO { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("Census") + @Valid + private CensusDTO censusDTO = null; + + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/CensusResponse.java b/health-services/census-service/src/main/java/digit/web/models/CensusResponse.java new file mode 100644 index 00000000000..4aace9435f2 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/CensusResponse.java @@ -0,0 +1,42 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.Map; + +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("Census") + @Valid + private List census = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; + + @JsonProperty("StatusCount") + @Valid + private Map statusCount = null; + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/CensusSearchCriteria.java b/health-services/census-service/src/main/java/digit/web/models/CensusSearchCriteria.java new file mode 100644 index 00000000000..363da1907ae --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/CensusSearchCriteria.java @@ -0,0 +1,72 @@ +package digit.web.models; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.springframework.validation.annotation.Validated; +import jakarta.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusSearchCriteria + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusSearchCriteria { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("ids") + private Set ids = null; + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + private String tenantId = null; + + @JsonProperty("areaCodes") + private List areaCodes = null; + + @JsonProperty("status") + private String status = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("source") + private String source = null; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("jurisdiction") + private List jurisdiction = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("limit") + private Integer limit = null; + + @JsonProperty("offset") + private Integer offset = null; + + public CensusSearchCriteria addAreaCodesItem(String areaCodesItem) { + if (this.areaCodes == null) { + this.areaCodes = new ArrayList<>(); + } + this.areaCodes.add(areaCodesItem); + return this; + } + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/CensusSearchRequest.java b/health-services/census-service/src/main/java/digit/web/models/CensusSearchRequest.java new file mode 100644 index 00000000000..ac595972fe7 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/CensusSearchRequest.java @@ -0,0 +1,33 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusSearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusSearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("CensusSearchCriteria") + @Valid + @NotNull + private CensusSearchCriteria censusSearchCriteria = null; + + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/PopulationByDemographic.java b/health-services/census-service/src/main/java/digit/web/models/PopulationByDemographic.java new file mode 100644 index 00000000000..8b984e06d89 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/PopulationByDemographic.java @@ -0,0 +1,69 @@ +package digit.web.models; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * PopulationByDemographic + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PopulationByDemographic { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("demographicVariable") + private DemographicVariableEnum demographicVariable = null; + + @JsonProperty("populationDistribution") + private Object populationDistribution = null; + + /** + * Gets or Sets demographicVariable + */ + public enum DemographicVariableEnum { + AGE("age"), + + GENDER("gender"), + + ETHNICITY("ethnicity"); + + private String value; + + DemographicVariableEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static DemographicVariableEnum fromValue(String text) { + for (DemographicVariableEnum b : DemographicVariableEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + } + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/RequestInfoWrapper.java b/health-services/census-service/src/main/java/digit/web/models/RequestInfoWrapper.java new file mode 100644 index 00000000000..b36665e3e25 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/RequestInfoWrapper.java @@ -0,0 +1,18 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.egov.common.contract.request.RequestInfo; + + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RequestInfoWrapper { + + @JsonProperty("RequestInfo") + private RequestInfo requestInfo; + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/BoundarySearchResponse.java b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundarySearchResponse.java new file mode 100644 index 00000000000..7f92e1f53c8 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundarySearchResponse.java @@ -0,0 +1,42 @@ +package digit.web.models.boundary; + +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.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; + +/** + * BoundarySearchResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundarySearchResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("TenantBoundary") + @Valid + private List tenantBoundary = null; + + + public BoundarySearchResponse addTenantBoundaryItem(HierarchyRelation tenantBoundaryItem) { + if (this.tenantBoundary == null) { + this.tenantBoundary = new ArrayList<>(); + } + this.tenantBoundary.add(tenantBoundaryItem); + return this; + } + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchy.java b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchy.java new file mode 100644 index 00000000000..45ab5a19141 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchy.java @@ -0,0 +1,30 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +/** + * BoundaryTypeHierarchy + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchy { + + @JsonProperty("boundaryType") + private String boundaryType = null; + + @JsonProperty("parentBoundaryType") + private String parentBoundaryType = null; + + @JsonProperty("active") + private Boolean active = null; + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyDefinition.java b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyDefinition.java new file mode 100644 index 00000000000..6faf8ac0b49 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyDefinition.java @@ -0,0 +1,45 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * BoundaryTypeHierarchyDefinition + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchyDefinition { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("hierarchyType") + private String hierarchyType = null; + + @JsonProperty("boundaryHierarchy") + @Valid + private List boundaryHierarchy = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("boundaryHierarchyJsonNode") + private JsonNode boundaryHierarchyJsonNode = null; + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyResponse.java b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyResponse.java new file mode 100644 index 00000000000..6372620c43c --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyResponse.java @@ -0,0 +1,36 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * BoundaryTypeHierarchyResponse + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchyResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("totalCount") + private Integer totalCount = null; + + @JsonProperty("BoundaryHierarchy") + @Valid + private List boundaryHierarchy = null; + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchCriteria.java b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchCriteria.java new file mode 100644 index 00000000000..7ad5be0790f --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchCriteria.java @@ -0,0 +1,38 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * BoundaryTypeHierarchySearchCriteria + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchySearchCriteria { + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + @NotNull + private String tenantId = null; + + @JsonProperty("hierarchyType") + @Size(min = 1, max = 100) + private String hierarchyType = null; + + @JsonProperty("offset") + private Integer offset; + + @JsonProperty("limit") + private Integer limit; + +} + diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchRequest.java b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchRequest.java new file mode 100644 index 00000000000..b85ffffd857 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchRequest.java @@ -0,0 +1,33 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * BoundaryTypeHierarchySearchRequest + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchySearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("BoundaryTypeHierarchySearchCriteria") + @Valid + private BoundaryTypeHierarchySearchCriteria boundaryTypeHierarchySearchCriteria = null; + +} diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/EnrichedBoundary.java b/health-services/census-service/src/main/java/digit/web/models/boundary/EnrichedBoundary.java new file mode 100644 index 00000000000..c42af3737ce --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/EnrichedBoundary.java @@ -0,0 +1,42 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * EnrichedBoundary + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EnrichedBoundary { + + @JsonProperty("id") + private String id; + + @JsonProperty("code") + @NotNull + private String code; + + @JsonProperty("boundaryType") + private String boundaryType; + + @JsonProperty("children") + @Valid + private List children = null; + + @JsonIgnore + private String parent = null; + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/web/models/boundary/HierarchyRelation.java b/health-services/census-service/src/main/java/digit/web/models/boundary/HierarchyRelation.java new file mode 100644 index 00000000000..7a3e26f6595 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/boundary/HierarchyRelation.java @@ -0,0 +1,34 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import java.util.List; + +/** + * HierarchyRelation + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class HierarchyRelation { + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("hierarchyType") + private String hierarchyType = null; + + @JsonProperty("boundary") + @Valid + private List boundary = null; + + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignment.java b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignment.java new file mode 100644 index 00000000000..1cec6f46300 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignment.java @@ -0,0 +1,63 @@ +package digit.web.models.plan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * PlanEmployeeAssignment + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignment { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + @Size(min = 2, max = 64) + private String planConfigurationId = null; + + @JsonProperty("employeeId") + @NotNull + @Size(min = 2, max = 64) + private String employeeId = null; + + @JsonProperty("role") + @NotNull + @Size(min = 2, max = 64) + private String role = null; + + @JsonProperty("jurisdiction") + @Valid + @NotEmpty + private List jurisdiction = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("active") + private Boolean active = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; +} diff --git a/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentResponse.java b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentResponse.java new file mode 100644 index 00000000000..8e6e0260116 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentResponse.java @@ -0,0 +1,37 @@ +package digit.web.models.plan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + + +/** + * PlanEmployeeAssignmentResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("PlanEmployeeAssignment") + @Valid + private List planEmployeeAssignment = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; +} + diff --git a/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentSearchCriteria.java b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentSearchCriteria.java new file mode 100644 index 00000000000..6e38c1dad95 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentSearchCriteria.java @@ -0,0 +1,50 @@ +package digit.web.models.plan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * PlanEmployeeAssignmentSearchCriteria + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentSearchCriteria { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @Size(min = 2, max = 100) + @NotNull + private String tenantId = null; + + @JsonProperty("employeeId") + private List employeeId = null; + + @JsonProperty("planConfigurationId") + @Size(max = 64) + private String planConfigurationId = null; + + @JsonProperty("role") + @Size(min = 2, max = 64) + private List role = null; + + @JsonProperty("jurisdiction") + @Valid + private List jurisdiction = null; + + @JsonProperty("active") + private Boolean active = null; +} diff --git a/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentSearchRequest.java b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentSearchRequest.java new file mode 100644 index 00000000000..4a343fb8e45 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/plan/PlanEmployeeAssignmentSearchRequest.java @@ -0,0 +1,29 @@ +package digit.web.models.plan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * PlanEmployeeAssignmentSearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentSearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("PlanEmployeeAssignmentSearchCriteria") + @Valid + private PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria = null; +} diff --git a/health-services/census-service/src/main/java/digit/web/models/plan/PlanFacilityDTO.java b/health-services/census-service/src/main/java/digit/web/models/plan/PlanFacilityDTO.java new file mode 100644 index 00000000000..ce15f091cec --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/plan/PlanFacilityDTO.java @@ -0,0 +1,72 @@ +package digit.web.models.plan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * Plan Facility DTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilityDTO { + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + @Size(max = 64) + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("facilityId") + @NotNull + @Size(max = 64) + private String facilityId = null; + + @JsonProperty("residingBoundary") + @NotNull + @Size(min = 1, max = 64) + private String residingBoundary = null; + + // Changed List to String to store as JSON + @JsonProperty("serviceBoundaries") + @NotNull + @Size(min = 1) + private String serviceBoundaries = null; // Store as JSON string + + @JsonProperty("initiallySetServiceBoundaries") + private List initiallySetServiceBoundaries; + + @JsonProperty("facilityName") + private String facilityName = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("active") + @NotNull + private Boolean active = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + +} \ No newline at end of file diff --git a/health-services/census-service/src/main/java/digit/web/models/plan/PlanFacilityRequestDTO.java b/health-services/census-service/src/main/java/digit/web/models/plan/PlanFacilityRequestDTO.java new file mode 100644 index 00000000000..6bae9ac51c2 --- /dev/null +++ b/health-services/census-service/src/main/java/digit/web/models/plan/PlanFacilityRequestDTO.java @@ -0,0 +1,33 @@ +package digit.web.models.plan; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * PlanFacilityRequestDTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilityRequestDTO { + + @JsonProperty("RequestInfo") + @Valid + @NotNull + private RequestInfo requestInfo; + + @JsonProperty("PlanFacility") + @Valid + @NotNull + private PlanFacilityDTO planFacilityDTO; + +} diff --git a/health-services/census-service/src/main/resources/application.properties b/health-services/census-service/src/main/resources/application.properties new file mode 100644 index 00000000000..fcf43df5960 --- /dev/null +++ b/health-services/census-service/src/main/resources/application.properties @@ -0,0 +1,82 @@ +server.contextPath=/census-service +server.servlet.context-path=/census-service +management.endpoints.web.base-path=/ +server.port=8080 +app.timezone=UTC + +#DATABASE CONFIGURATION +spring.datasource.driver-class-name=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/censusDB +spring.datasource.username=postgres +spring.datasource.password=postgres + +#FLYWAY CONFIGURATION +spring.flyway.url=jdbc:postgresql://localhost:5432/censusDB +spring.flyway.user=postgres +spring.flyway.password=postgres +spring.flyway.table=public +spring.flyway.baseline-on-migrate=true +spring.flyway.outOfOrder=true +spring.flyway.locations=classpath:/db/migration/main +spring.flyway.enabled=false + +# KAFKA SERVER CONFIGURATIONS +kafka.config.bootstrap_server_config=localhost:9092 +spring.kafka.consumer.value-deserializer=org.egov.tracer.kafka.deserializer.HashMapDeserializer +spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer +spring.kafka.consumer.group-id=census-serivce +spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer +spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.listener.missing-topics-fatal=false +spring.kafka.consumer.properties.spring.json.use.type.headers=false +spring.kafka.producer.properties.max.request.size=3000000 + +# KAFKA CONSUMER CONFIGURATIONS +kafka.consumer.config.auto_commit=true +kafka.consumer.config.auto_commit_interval=100 +kafka.consumer.config.session_timeout=15000 +kafka.consumer.config.auto_offset_reset=earliest +# KAFKA PRODUCER CONFIGURATIONS +kafka.producer.config.retries_config=0 +kafka.producer.config.batch_size_config=16384 +kafka.producer.config.linger_ms_config=1 +kafka.producer.config.buffer_memory_config=33554432 + +#PERSISTER KAFKA TOPICS +census.create.topic=census-create-topic +census.update.topic=census-update-topic +census.bulk.update.topic=census-bulk-update-topic + +egov.sms.notification.topic=egov.core.notification.sms +kafka.topics.receipt.create=dss-collection + +#Boundary service urls +egov.boundary.service.host=https://unified-dev.digit.org +egov.boundary.relationship.search.endpoint=/boundary-service/boundary-relationships/_search +egov.boundary.hierarchy.search.endpoint=/boundary-service/boundary-hierarchy-definition/_search + +#Plan service urls +egov.plan.service.host=https://unified-dev.digit.org +egov.plan.employee.assignment.search.endpoint=/plan-service/employee/_search + +# Workflow +egov.workflow.host=https://unified-dev.digit.org +egov.workflow.transition.path=/egov-workflow-v2/egov-wf/process/_transition +egov.business.service.search.endpoint=/egov-workflow-v2/egov-wf/businessservice/_search +workflow.initiate.action=INITIATE +workflow.intermediate.action=EDIT_AND_SEND_FOR_APPROVAL,APPROVE +workflow.send.back.actions=SEND_BACK_FOR_CORRECTION + + +#Pagination config +census.default.offset=0 +census.default.limit=10 + +resource.config.consumer.census.create.topic=resource-census-create-topic +resource.config.consumer.census.update.topic=resource-census-update-topic + +plan.facility.update.topic=update-plan-facility + +#Roles for census +allowed.census.roles={'POPULATION_DATA_APPROVER','ROOT_POPULATION_DATA_APPROVER'} +workflow.restricted.roles={'ROOT_FACILITY_CATCHMENT_MAPPER','FACILITY_CATCHMENT_MAPPER'} \ No newline at end of file diff --git a/health-services/census-service/src/main/resources/db/Dockerfile b/health-services/census-service/src/main/resources/db/Dockerfile new file mode 100644 index 00000000000..f38638a269f --- /dev/null +++ b/health-services/census-service/src/main/resources/db/Dockerfile @@ -0,0 +1,9 @@ +FROM egovio/flyway:10.7.1 + +COPY ./migration/main /flyway/sql + +COPY migrate.sh /usr/bin/migrate.sh + +RUN chmod +x /usr/bin/migrate.sh + +ENTRYPOINT ["/usr/bin/migrate.sh"] diff --git a/health-services/resource-estimation-service/src/main/resources/db/migrate.sh b/health-services/census-service/src/main/resources/db/migrate.sh similarity index 67% rename from health-services/resource-estimation-service/src/main/resources/db/migrate.sh rename to health-services/census-service/src/main/resources/db/migrate.sh index 43960b25cdb..c58d6f91e3f 100644 --- a/health-services/resource-estimation-service/src/main/resources/db/migrate.sh +++ b/health-services/census-service/src/main/resources/db/migrate.sh @@ -1,3 +1,3 @@ #!/bin/sh -flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true -ignoreMissingMigrations=true migrate \ No newline at end of file +flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true migrate \ No newline at end of file diff --git a/health-services/census-service/src/main/resources/db/migration/main/V20240925155908__census_create_ddl.sql b/health-services/census-service/src/main/resources/db/migration/main/V20240925155908__census_create_ddl.sql new file mode 100644 index 00000000000..cf80c461292 --- /dev/null +++ b/health-services/census-service/src/main/resources/db/migration/main/V20240925155908__census_create_ddl.sql @@ -0,0 +1,36 @@ +-- Table: census +CREATE TABLE census ( + id character varying(64) NOT NULL, + tenant_id character varying(64) NOT NULL, + hierarchy_type character varying(64) NOT NULL, + boundary_code character varying(64) NOT NULL, + type character varying(64) NOT NULL, + total_population bigint NOT NULL, + effective_from bigint NOT NULL, + effective_to bigint, + source character varying(255) NOT NULL, + status character varying(255), + assignee character varying(255), + facility_assigned boolean, + boundary_ancestral_path TEXT NOT NULL, + additional_details JSONB, + created_by character varying(64) NOT NULL, + created_time bigint NOT NULL, + last_modified_by character varying(64) NOT NULL, + last_modified_time bigint NOT NULL, + CONSTRAINT uk_census_id PRIMARY KEY (id) +); + +-- Table: population_by_demographics +CREATE TABLE population_by_demographics ( + id character varying(64), + census_id character varying(64), + demographic_variable character varying(64), + population_distribution JSONB, + created_by character varying(64), + created_time bigint, + last_modified_by character varying(64), + last_modified_time bigint, + CONSTRAINT uk_population_by_demographics_id PRIMARY KEY (id), + FOREIGN KEY (census_id) REFERENCES census(id) +); diff --git a/health-services/census-service/src/main/resources/db/migration/main/V20241105152700__census_additional_field_create_ddl.sql b/health-services/census-service/src/main/resources/db/migration/main/V20241105152700__census_additional_field_create_ddl.sql new file mode 100644 index 00000000000..9887f73656f --- /dev/null +++ b/health-services/census-service/src/main/resources/db/migration/main/V20241105152700__census_additional_field_create_ddl.sql @@ -0,0 +1,12 @@ +-- Table: additional_field +CREATE TABLE additional_field ( + id character varying(64) NOT NULL, + census_id character varying(64) NOT NULL, + "key" character varying(64) NOT NULL, + "value" numeric(12,2) NOT NULL, + show_on_ui boolean DEFAULT true NOT NULL, + editable boolean DEFAULT true NOT NULL, + "order" bigint NOT NULL, + CONSTRAINT uk_additional_field_id PRIMARY KEY (id), + FOREIGN KEY (census_id) REFERENCES census(id) +); diff --git a/health-services/census-service/src/main/resources/db/migration/main/V20241126120700__alter_census_assignee_create_ddl.sql b/health-services/census-service/src/main/resources/db/migration/main/V20241126120700__alter_census_assignee_create_ddl.sql new file mode 100644 index 00000000000..7d271361aae --- /dev/null +++ b/health-services/census-service/src/main/resources/db/migration/main/V20241126120700__alter_census_assignee_create_ddl.sql @@ -0,0 +1 @@ +ALTER TABLE census ALTER COLUMN assignee TYPE TEXT; \ No newline at end of file diff --git a/health-services/census-service/src/main/resources/start.sh b/health-services/census-service/src/main/resources/start.sh new file mode 100644 index 00000000000..36eccc0ae97 --- /dev/null +++ b/health-services/census-service/src/main/resources/start.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +if [[ -z "${JAVA_OPTS}" ]];then +export JAVA_OPTS="-Xmx64m -Xms64m" +fi + +if [ x"${JAVA_ENABLE_DEBUG}" != x ] && [ "${JAVA_ENABLE_DEBUG}" != "false" ]; then +java_debug_args="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${JAVA_DEBUG_PORT:-5005}" +fi + +exec java ${java_debug_args} ${JAVA_OPTS} ${JAVA_ARGS} -jar /opt/egov/*.jar \ No newline at end of file diff --git a/health-services/census-service/src/test/java/digit/TestConfiguration.java b/health-services/census-service/src/test/java/digit/TestConfiguration.java new file mode 100644 index 00000000000..570236cfc94 --- /dev/null +++ b/health-services/census-service/src/test/java/digit/TestConfiguration.java @@ -0,0 +1,16 @@ +package digit; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.KafkaTemplate; + +import static org.mockito.Mockito.mock; + +@Configuration +public class TestConfiguration { + @Bean + @SuppressWarnings("unchecked") + public KafkaTemplate kafkaTemplate() { + return mock(KafkaTemplate.class); + } +} \ No newline at end of file diff --git a/health-services/census-service/src/test/java/digit/web/controllers/CensusControllerTest.java b/health-services/census-service/src/test/java/digit/web/controllers/CensusControllerTest.java new file mode 100644 index 00000000000..df3d8c23267 --- /dev/null +++ b/health-services/census-service/src/test/java/digit/web/controllers/CensusControllerTest.java @@ -0,0 +1,69 @@ +package digit.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.service.CensusService; +import digit.web.models.CensusRequest; +import digit.web.models.CensusSearchRequest; +import org.junit.Test; +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import digit.TestConfiguration; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * API tests for CensusApiController + */ +@Ignore +@RunWith(SpringRunner.class) +@WebMvcTest(CensusController.class) +@Import(TestConfiguration.class) +public class CensusControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private JdbcTemplate jdbcTemplate; + + @MockBean + private CensusService service; + + @Autowired + private ObjectMapper objectMapper; + + + @Test + public void censusCreatePostSuccess() throws Exception { + CensusRequest request = CensusRequest.builder().build(); + mockMvc.perform(post("/_create").contentType(MediaType + .APPLICATION_JSON_UTF8).content(objectMapper.writeValueAsString(request))) + .andExpect(status().isAccepted()); + } + + @Test + public void censusSearchPostSuccess() throws Exception { + CensusSearchRequest request = CensusSearchRequest.builder().build(); + mockMvc.perform(post("/_search").contentType(MediaType + .APPLICATION_JSON_UTF8).content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()); + } + + @Test + public void censusUpdatePostSuccess() throws Exception { + CensusRequest request = CensusRequest.builder().build(); + mockMvc.perform(post("/_update").contentType(MediaType + .APPLICATION_JSON_UTF8).content(objectMapper.writeValueAsString(request))) + .andExpect(status().isAccepted()); + } + +} diff --git a/health-services/plan-service/CHANGELOG.md b/health-services/plan-service/CHANGELOG.md index 650d79eae57..a73439d7658 100644 --- a/health-services/plan-service/CHANGELOG.md +++ b/health-services/plan-service/CHANGELOG.md @@ -1,10 +1,38 @@ # Changelog All notable changes to this module will be documented in this file. -## 1.0.0 - 2024-06-24 -#### Plan Service - 1. Plan Service manages: validation of plans, plan search, plan create, plan update. - 2. Validation of plan: Plan service validates plan request before it takes action on it like update or create. - 3. Plan Create: Plan service creates a plan after successful validation is done. It sends create request on topic to create plan. - 4. Plan Update : Plan service creates a plan after successful validation is done. It sends update request on topic to resource estimation service to further process. - 5. Plan Search: This enables to search plan based on provided search string. \ No newline at end of file +## 1.0.0 - 2024-12-03 +### Plan Service + +##### Plan Configuration + +1. Validation of Plan Configuration: Validates all plan configuration requests against MDMS and Project Factory before processing the requests. +2. Plan Configuration Create: Validates and enriches new plan configurations before publishing them to the Kafka topic for asynchronous processing. +3. Plan Configuration Update: Updates existing plan configurations post-validation and enrichment by pushing requests to the Kafka update topic. +4. Plan Configuration Search: Facilitates searching for plan configurations using the provided search criteria. +5. Resource Generator Consumer: Listens to resource generator plan configuration update topic to update plan configuration. + +#### Plan + +1. Plan manages: validation of plans, plan search, plan create, plan update. +2. Validation of plan: Plan service validates plan request before it takes action on it like update or create. +3. Plan Create: Plan service creates a plan after successful validation is done. It sends create request on topic to create plan. +4. Plan Update : Plan service creates a plan after successful validation is done. It sends update request on topic to resource estimation service to further process. +5. Plan Search: This enables to search plan based on provided search string. +6. Plan Bulk Update: Allows updating multiple plans in a single operation after validation. +7. Resource Generator Consumer: Listens to resource plan create topic to trigger the creation of new plans. + +#### Plan Facility + +1. Validation: Validates plan facility requests against MDMS, Facility Service and Project Factory before processing the requests. +2. Plan Facility Create: Creates a plan facility after validation and enrichment, pushing the create request to the designated kafka topic. +3. Plan Facility Update: Updates existing facilities post-validation by pushing update requests to the Kafka topic. Also sends the update request to Census service for facility mapping. +4. Plan Facility Search: Searches Plan Facility for the provided search criteria. +5. Project Factory Consumer: Listens to project factory consumer to enrich and create new plan facility. + +#### Plan Employee Assignment + +1. Validation: Validates plan employee assignment requests against MDMS, User service and Project Factory before processing the requests. +2. Plan Employee Assignment Create: Assigns employees to plans post-validation and enrichment, considering roles and jurisdictions by pushing request to create kafka topic. +3. Plan Employee Assignment Update: Updates existing assignments after validation, publishing the changes to the designated Kafka update topic. +4. Plan Employee Assignment Search: Enables searching for employee assignments using provided search criteria. \ No newline at end of file diff --git a/health-services/plan-service/pom.xml b/health-services/plan-service/pom.xml index 148766ff97d..c41f44a9f05 100644 --- a/health-services/plan-service/pom.xml +++ b/health-services/plan-service/pom.xml @@ -95,6 +95,11 @@ 2.9.0-SNAPSHOT compile + + org.apache.commons + commons-text + 1.10.0 + diff --git a/health-services/plan-service/src/main/java/digit/config/Configuration.java b/health-services/plan-service/src/main/java/digit/config/Configuration.java index 7cf4b3549f7..110be35646a 100644 --- a/health-services/plan-service/src/main/java/digit/config/Configuration.java +++ b/health-services/plan-service/src/main/java/digit/config/Configuration.java @@ -1,16 +1,13 @@ package digit.config; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.*; import org.egov.tracer.config.TracerConfiguration; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.stereotype.Component; -import java.util.TimeZone; +import java.util.List; +import java.util.Map; @Component @Data @@ -21,6 +18,13 @@ @Getter public class Configuration { + //Role Map + @Value("#{${role.map}}") + public Map roleMap; + + @Value("${plan.estimation.approver.roles}") + public List planEstimationApproverRoles; + //MDMS @Value("${egov.mdms.host}") private String mdmsHost; @@ -28,6 +32,32 @@ public class Configuration { @Value("${egov.mdms.search.endpoint}") private String mdmsEndPoint; + @Value("${egov.mdms.search.v2.endpoint}") + private String mdmsV2EndPoint; + + //Project Factory + @Value("${egov.project.factory.host}") + private String projectFactoryHost; + + @Value("${egov.project.factory.search.endpoint}") + private String projectFactorySearchEndPoint; + + //User Service + @Value("${egov.user.service.host}") + private String userServiceHost; + + @Value("${egov.user.search.endpoint}") + private String userSearchEndPoint; + + // Boundary Service + @Value("${egov.boundary.service.host}") + private String boundaryServiceHost; + + @Value("${egov.boundary.relationship.search.endpoint}") + private String boundaryRelationshipSearchEndpoint; + + @Value("${egov.boundary.hierarchy.search.endpoint}") + private String boundaryHierarchySearchEndpoint; //Persister Topic @Value("${plan.configuration.create.topic}") @@ -36,16 +66,64 @@ public class Configuration { @Value("${plan.configuration.update.topic}") private String planConfigUpdateTopic; + @Value("${plan.employee.assignment.create.topic}") + private String planEmployeeAssignmentCreateTopic; + + @Value("${plan.employee.assignment.update.topic}") + private String planEmployeeAssignmentUpdateTopic; + @Value("${plan.create.topic}") private String planCreateTopic; @Value("${plan.update.topic}") private String planUpdateTopic; + @Value("${plan.bulk.update.topic}") + private String planBulkUpdateTopic; + + @Value("${plan.facility.create.topic}") + private String planFacilityCreateTopic; + + @Value("${plan.facility.update.topic}") + private String planFacilityUpdateTopic; + @Value("${plan.default.offset}") private Integer defaultOffset; @Value("${plan.default.limit}") private Integer defaultLimit; + //Census + @Value("${egov.census.host}") + private String censusHost; + + @Value("${egov.census.search.endpoint}") + private String censusSearchEndPoint; + + //Facility + @Value("${egov.facility.host}") + private String facilityHost; + + @Value("${egov.facility.search.endpoint}") + private String facilitySearchEndPoint; + + //Workflow + @Value("${egov.workflow.host}") + private String wfHost; + + @Value("${egov.workflow.transition.path}") + private String wfTransitionPath; + + @Value("${egov.business.service.search.endpoint}") + private String businessServiceSearchEndpoint; + + @Value("${workflow.initiate.action}") + private List wfInitiateActions; + + @Value("${workflow.intermediate.action}") + private List wfIntermediateActions; + + @Value("${workflow.send.back.actions}") + private List wfSendBackActions; + } diff --git a/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java b/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java index ab694a05961..a2b9696b482 100644 --- a/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java +++ b/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java @@ -12,21 +12,41 @@ public class ServiceConstants { public static final String ERROR_WHILE_FETCHING_FROM_MDMS = "Exception occurred while fetching category lists from mdms: "; + public static final String ERROR_WHILE_FETCHING_FROM_USER_SERVICE = "Exception occurred while fetching user details from user service: "; + + public static final String ERROR_WHILE_FETCHING_FROM_PROJECT_FACTORY = "Exception occurred while fetching campaign details from project factory: "; + + public static final String ERROR_WHILE_FETCHING_FROM_CENSUS = "Exception occurred while fetching records from census: "; + + public static final String ERROR_WHILE_FETCHING_BOUNDARY_DETAILS = "Exception occurred while fetching boundary relationship from boundary service: "; + + public static final String ERROR_WHILE_FETCHING_BOUNDARY_HIERARCHY_DETAILS = "Exception occurred while fetching boundary hierarchy details from boundary service: "; + + public static final String ERROR_WHILE_FETCHING_DATA_FROM_HRMS = "Exception occurred while fetching employee from hrms: "; + public static final String RES_MSG_ID = "uief87324"; public static final String SUCCESSFUL = "successful"; public static final String FAILED = "failed"; + public static final String ROOT_PREFIX = "ROOT"; + public static final String USERINFO_MISSING_CODE = "USERINFO_MISSING"; public static final String USERINFO_MISSING_MESSAGE = "UserInfo is missing in Request Info "; public static final String ASSUMPTION_VALUE_NOT_FOUND_CODE = "ASSUMPTION_VALUE_NOT_FOUND"; - public static final String ASSUMPTION_VALUE_NOT_FOUND_MESSAGE = "Operation's Assumption value not found in active assumptions list "; - - public static final String FILESTORE_ID_INVALID_CODE = "FILESTORE_ID_INVALID"; - public static final String FILESTORE_ID_INVALID_MESSAGE = " Resource mapping does not have a Valid File Store Id "; + public static final String ASSUMPTION_VALUE_NOT_FOUND_MESSAGE = "Operation's Assumption value is not present in allowed columns, previous outputs, or active Assumption Keys "; public static final String ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_CODE = "ASSUMPTION_KEY_NOT_FOUND_IN_MDMS"; - public static final String ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_MESSAGE = "Assumption Key is not present in MDMS "; + public static final String ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_MESSAGE = "Assumption Key is not present in MDMS - "; + + public static final String DUPLICATE_ASSUMPTION_KEY_CODE = "DUPLICATE_ASSUMPTION_KEY"; + public static final String DUPLICATE_ASSUMPTION_KEY_MESSAGE = "Duplicate Assumption key found : "; + + public static final String VEHICLE_ID_NOT_FOUND_IN_MDMS_CODE = "VEHICLE_ID_NOT_FOUND_IN_MDMS"; + public static final String VEHICLE_ID_NOT_FOUND_IN_MDMS_MESSAGE = "Vehicle Id is not present in MDMS"; + + public static final String VEHICLE_IDS_INVALID_DATA_TYPE_CODE = "VEHICLE_IDS_INVALID_DATA_TYPE"; + public static final String VEHICLE_IDS_INVALID_DATA_TYPE_MESSAGE = "Vehicle IDs should be a list of strings."; public static final String TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_CODE = "TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS"; public static final String TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_MESSAGE = "Template Identifier is not present in MDMS "; @@ -38,29 +58,72 @@ public class ServiceConstants { public static final String ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_MESSAGE = "Only one file of the required template identifier should be present "; public static final String INPUT_KEY_NOT_FOUND_CODE = "INPUT_KEY_NOT_FOUND"; - public static final String INPUT_KEY_NOT_FOUND_MESSAGE = "Operation's Input key not present in MDMS "; - - public static final String LOCALITY_NOT_PRESENT_IN_MAPPED_TO_CODE = "LOCALITY_NOT_PRESENT_IN_MAPPED_TO"; - public static final String LOCALITY_NOT_PRESENT_IN_MAPPED_TO_MESSAGE = "Resource Mapping's MappedTo must contain 'Locality' "; - - public static final String DUPLICATE_MAPPED_TO_VALIDATION_ERROR_CODE = "DUPLICATE_MAPPED_TO_VALIDATION_ERROR"; - public static final String DUPLICATE_MAPPED_TO_VALIDATION_ERROR_MESSAGE = "Duplicate MappedTo found in Resource Mapping"; - - public static final String TENANT_NOT_FOUND_IN_MDMS_CODE = "TENANT_ID_NOT_FOUND_IN_MDMS"; - public static final String TENANT_NOT_FOUND_IN_MDMS_MESSAGE = "Tenant Id is not present in MDMS"; + public static final String INPUT_KEY_NOT_FOUND_MESSAGE = "Operation's Input key is not present in allowed columns or previous outputs - "; public static final String TENANT_ID_EMPTY_CODE = "TENANT_ID_EMPTY"; public static final String TENANT_ID_EMPTY_MESSAGE = "Tenant Id cannot be empty, TenantId should be present"; + public static final String PLAN_CONFIG_ID_EMPTY_CODE = "PLAN_CONFIG_ID_EMPTY"; + public static final String PLAN_CONFIG_ID_EMPTY_MESSAGE = "Plan config Id cannot be empty."; + public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE = "NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT"; public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE = "Invalid or incorrect TenantId. No mdms data found for provided Tenant."; + public static final String INVALID_EMPLOYEE_ID_CODE = "INVALID_EMPLOYEE_ID"; + public static final String INVALID_EMPLOYEE_ID_MESSAGE = "Invalid or incorrect employee id."; + + public static final String NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_CODE = "NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID"; + public static final String NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_MESSAGE = "Invalid or incorrect campaign id. No campaign details found for provided campaign id."; + + public static final String NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_CODE = "NO_CENSUS_FOUND_FOR_GIVEN_DETAILS"; + public static final String NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_MESSAGE = "Census records does not exists for the given details: "; + + + public static final String INVALID_ROOT_EMPLOYEE_JURISDICTION_CODE = "INVALID_ROOT_EMPLOYEE_JURISDICTION"; + public static final String INVALID_ROOT_EMPLOYEE_JURISDICTION_MESSAGE = "The root employee's jurisdiction should be at highest hierarchy"; + + public static final String INVALID_EMPLOYEE_JURISDICTION_CODE = "INVALID_EMPLOYEE_JURISDICTION"; + public static final String INVALID_EMPLOYEE_JURISDICTION_MESSAGE = "The employee's jurisdiction can't be at highest or lowest hierarchy"; + + public static final String INVALID_JURISDICTION_CODE = "INVALID_JURISDICTION"; + public static final String INVALID_JURISDICTION_MESSAGE = "The employee's jurisdiction provided is invalid"; + + public static final String INVALID_HIERARCHY_LEVEL_CODE = "INVALID_HIERARCHY_LEVEL"; + public static final String INVALID_HIERARCHY_LEVEL_MESSAGE = "The hierarchy level provided is invalid"; + + public static final String INVALID_EMPLOYEE_ROLE_CODE = "INVALID_EMPLOYEE_ROLE"; + public static final String INVALID_EMPLOYEE_ROLE_MESSAGE = "The employee's role provided is invalid"; + public static final String SEARCH_CRITERIA_EMPTY_CODE = "SEARCH_CRITERIA_EMPTY"; public static final String SEARCH_CRITERIA_EMPTY_MESSAGE = "Search criteria cannot be empty"; public static final String INVALID_PLAN_CONFIG_ID_CODE = "INVALID_PLAN_CONFIG_ID"; public static final String INVALID_PLAN_CONFIG_ID_MESSAGE = "Plan config id provided is invalid"; + public static final String INVALID_PLAN_EMPLOYEE_ASSIGNMENT_CODE = "INVALID_PLAN_EMPLOYEE_ASSIGNMENT"; + public static final String INVALID_PLAN_EMPLOYEE_ASSIGNMENT_MESSAGE = "Plan employee assignment to be updated doesn't exists"; + + public static final String PLAN_CONFIGURATION_ALREADY_EXISTS_CODE = "PLAN_CONFIGURATION_ALREADY_EXISTS"; + public static final String PLAN_CONFIGURATION_ALREADY_EXISTS_MESSAGE = "Plan Configuration for the provided name and campaign id already exists"; + + public static final String PLAN_EMPLOYEE_ASSIGNMENT_ALREADY_EXISTS_CODE = "PLAN_EMPLOYEE_ASSIGNMENT_ALREADY_EXISTS"; + public static final String PLAN_EMPLOYEE_ASSIGNMENT_ALREADY_EXISTS_MESSAGE = "Plan employee assignment for the provided details already exists"; + + public static final String PLAN_FACILITY_LINKAGE_ALREADY_EXISTS_CODE = "PLAN_FACILITY_LINKAGE_ALREADY_EXISTS"; + public static final String PLAN_FACILITY_LINKAGE_ALREADY_EXISTS_MESSAGE = "Plan facility linkage for the provided facilityId and planConfigId already exists"; + + public static final String PLAN_EMPLOYEE_ASSIGNMENT_ID_EMPTY_CODE = "PLAN_EMPLOYEE_ASSIGNMENT_ID_EMPTY"; + public static final String PLAN_EMPLOYEE_ASSIGNMENT_ID_EMPTY_MESSAGE = "Plan employee assignment id cannot be empty"; + + public static final String PLAN_EMPLOYEE_ASSIGNMENT_NOT_FOUND_CODE = "PLAN_EMPLOYEE_ASSIGNMENT_FOR_BOUNDARY_NOT_FOUND"; + public static final String PLAN_EMPLOYEE_ASSIGNMENT_NOT_FOUND_MESSAGE = "No plan-employee assignment found for the provided boundary - "; + + public static final String JURISDICTION_NOT_FOUND_CODE = "JURISDICTION_NOT_FOUND"; + public static final String JURISDICTION_NOT_FOUND_MESSAGE = "Employee doesn't have the jurisdiction to take action for the provided locality."; + + public static final String NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_CODE = "NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE"; + public static final String NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_MESSAGE = "Invalid or incorrect boundaryCode. No boundary data found."; + public static final String METRIC_NOT_FOUND_IN_MDMS_CODE = "METRIC_NOT_FOUND_IN_MDMS"; public static final String METRIC_NOT_FOUND_IN_MDMS_MESSAGE = "Metric key not found in MDMS"; @@ -73,9 +136,6 @@ public class ServiceConstants { public static final String JSONPATH_ERROR_CODE = "JSONPATH_ERROR"; public static final String JSONPATH_ERROR_MESSAGE = "Failed to parse mdms response with given Jsonpath" ; - public static final String BOUNDARY_CODE_MAPPING_NOT_FOUND_CODE = "BOUNDARY_CODE_MAPPING_NOT_FOUND"; - public static final String BOUNDARY_CODE_MAPPING_NOT_FOUND_MESSAGE = "Boundary Code Mapping is required column is not found."; - public static final String NAME_VALIDATION_LIST_EMPTY_CODE = "NAME_VALIDATION_LIST_EMPTY"; public static final String NAME_VALIDATION_LIST_EMPTY_MESSAGE = "Name Validation list from MDMS is empty"; @@ -109,9 +169,6 @@ public class ServiceConstants { public static final String PLAN_RESOURCES_MANDATORY_CODE = "PLAN_RESOURCES_MANDATORY"; public static final String PLAN_RESOURCES_MANDATORY_MESSAGE = "Resources are mandatory if plan configuration id is not provided"; - public static final String PLAN_RESOURCES_NOT_ALLOWED_CODE = "PLAN_RESOURCES_NOT_ALLOWED"; - public static final String PLAN_RESOURCES_NOT_ALLOWED_MESSAGE = "Resources are not allowed if plan configuration id is provided"; - public static final String INVALID_RESOURCE_ACTIVITY_LINKAGE_CODE = "INVALID_RESOURCE_ACTIVITY_LINKAGE"; public static final String INVALID_RESOURCE_ACTIVITY_LINKAGE_MESSAGE = "Resource-Activity linkage is invalid"; @@ -127,9 +184,38 @@ public class ServiceConstants { public static final String DUPLICATE_ACTIVITY_UUIDS_CODE = "DUPLICATE_ACTIVITY_UUIDS"; public static final String DUPLICATE_ACTIVITY_UUIDS_MESSAGE = "Activity UUIDs should be unique"; + public static final String ADDITIONAL_DETAILS_MISSING_CODE = "ADDITIONAL_DETAILS_MISSING"; + public static final String ADDITIONAL_DETAILS_MISSING_MESSAGE = "Additional details are missing in the plan configuration request."; + + public static final String PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_CODE = "PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT"; + public static final String PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_MESSAGE = "Key is not present in json object - "; + + public static final String ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE = "ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS"; + public static final String ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE = "Exception occurred while updating additional details : "; + + public static final String WORKFLOW_INTEGRATION_ERROR_CODE = "WORKFLOW_INTEGRATION_ERROR"; + public static final String WORKFLOW_INTEGRATION_ERROR_MESSAGE = "Exception occured while integrating with workflow : "; + + public static final String PROCESS_INSTANCE_NOT_FOUND_CODE = "PROCESS_INSTANCE_NOT_FOUND"; + public static final String PROCESS_INSTANCE_NOT_FOUND_MESSAGE = "No process instance found with businessId: "; + + public static final String FILES_NOT_FOUND_CODE = "FILES_NOT_FOUND"; + public static final String FILES_NOT_FOUND_MESSAGE = "Files are not present in Plan Configuration."; + + public static final String ASSUMPTIONS_NOT_FOUND_CODE = "ASSUMPTIONS_NOT_FOUND"; + public static final String ASSUMPTIONS_NOT_FOUND_MESSAGE = "Assumptions are not present in Plan Configuration."; + + public static final String OPERATIONS_NOT_FOUND_CODE = "OPERATIONS_NOT_FOUND"; + public static final String OPERATIONS_NOT_FOUND_MESSAGE = "Operations are not present in Plan Configuration."; + + public static final String NO_BUSINESS_SERVICE_DATA_FOUND_CODE = "NO_BUSINESS_SERVICE_DATA_FOUND"; + public static final String NO_BUSINESS_SERVICE_DATA_FOUND_MESSAGE = "Invalid or incorrect businessService. No business service data found."; //mdms constants public static final String MDMS_PLAN_MODULE_NAME = "hcm-microplanning"; + public static final String MDMS_ADMIN_CONSOLE_MODULE_NAME = "HCM-ADMIN-CONSOLE"; + public static final String MDMS_MASTER_HIERARCHY_CONFIG = "hierarchyConfig"; + public static final String MDMS_MASTER_HIERARCHY_SCHEMA = "HierarchySchema"; public static final String MDMS_MASTER_ASSUMPTION = "HypothesisAssumptions"; public static final String MDMS_MASTER_UPLOAD_CONFIGURATION = "UploadConfiguration"; public static final String MDMS_MASTER_RULE_CONFIGURE_INPUTS = "RuleConfigureInputs"; @@ -137,6 +223,17 @@ public class ServiceConstants { public static final String MDMS_MASTER_METRIC = "Metric"; public static final String MDMS_MASTER_UOM = "Uom"; public static final String MDMS_MASTER_NAME_VALIDATION= "MicroplanNamingRegex"; + public static final String MDMS_SCHEMA_ADMIN_SCHEMA = "adminSchema"; + public static final String BOUNDARY = "boundary"; + public static final String HIERARCHY_TYPE = "hierarchyType"; + + //MDMS field Constants + public static final String PROPERTIES = "properties"; + public static final String NUMBER_PROPERTIES = "numberProperties"; + public static final String STRING_PROPERTIES = "stringProperties"; + public static final String NAME = "name"; + + public static final String MICROPLAN_PREFIX = "MP-"; public static final String JSON_ROOT_PATH = "$."; @@ -144,10 +241,14 @@ public class ServiceConstants { public static final String DOT_REGEX = "\\."; + public static final String PIPE_REGEX = "\\|"; + public static final String FILTER_CODE = "$.*.code"; public static final String FILTER_ID = "$.*.id"; + public static final String FILTER_TO_GET_ALL_IDS = "*.id"; + public static final String FILTER_DATA = "$.*.data"; public static final String LOCALITY_CODE = "Locality"; @@ -163,8 +264,114 @@ public class ServiceConstants { public static final String MDMS_SCHEMA_PROPERTIES_IS_RULE_CONFIGURE_INPUT = "isRuleConfigureInputs"; public static final String MDMS_SCHEMA_PROPERTIES_IS_REQUIRED = "isRequired"; + + public static final String MDMS_SCHEMA_VEHICLE_DETAILS = "VehicleDetails"; + public static final String BOUNDARY_CODE = "boundaryCode"; + public static final String FILTER_ALL_ASSUMPTIONS = ".assumptionCategories[*].assumptions[*]"; + + public static final String HIERARCHY_CONFIG_FOR_MICROPLAN = "[?(@.type == 'microplan')]"; + + public static final String HIGHEST_HIERARCHY_FIELD_FOR_MICROPLAN = "highestHierarchy"; + + public static final String LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN = "lowestHierarchy"; + public static final String NAME_VALIDATION_DATA = "Data"; + // Constants for constructing logical expressions or queries + public static final String AND = " && "; + public static final String EQUALS = " == "; + public static final String SINGLE_QUOTE = "'"; + + // JSON field constants for campaign details + public static final String JSON_FIELD_CAMPAIGN_TYPE = "campaignType"; + public static final String JSON_FIELD_DISTRIBUTION_PROCESS = "DistributionProcess"; + public static final String JSON_FIELD_REGISTRATION_PROCESS = "RegistrationProcess"; + public static final String JSON_FIELD_RESOURCE_DISTRIBUTION_STRATEGY_CODE = "resourceDistributionStrategyCode"; + public static final String JSON_FIELD_IS_REGISTRATION_AND_DISTRIBUTION_TOGETHER = "isRegistrationAndDistributionHappeningTogetherOrSeparately"; + public static final String JSON_FIELD_VEHICLE_ID = "vehicleIds"; + + // JSON path constants for campaign details + public static final String JSONPATH_FILTER_PREFIX = "[?("; + public static final String JSONPATH_FILTER_SUFFIX = ")]"; + public static final String JSON_PATH_FILTER_CAMPAIGN_TYPE = "@.campaignType"; + public static final String JSON_PATH_FILTER_DISTRIBUTION_PROCESS = "@.DistributionProcess"; + public static final String JSON_PATH_FILTER_REGISTRATION_PROCESS = "@.RegistrationProcess"; + public static final String JSON_PATH_FILTER_RESOURCE_DISTRIBUTION_STRATEGY_CODE = "@.resourceDistributionStrategyCode"; + public static final String JSON_PATH_FILTER_IS_REGISTRATION_AND_DISTRIBUTION_TOGETHER = "@.isRegistrationAndDistributionHappeningTogetherOrSeparately"; + + // Workflow Constants + public static final String PLAN_CONFIGURATION_BUSINESS_SERVICE = "PLAN_CONFIGURATION"; + + public static final String PLAN_ESTIMATION_BUSINESS_SERVICE = "PLAN_ESTIMATION"; + + public static final String MODULE_NAME_VALUE = "plan-service"; + + public static final String DRAFT_STATUS = "DRAFT"; + + public static final String SETUP_COMPLETED_ACTION = "INITIATE"; + + public static final String URI_TENANT_ID_PARAM = "tenantId"; + + public static final String URI_BUSINESS_SERVICE_PARAM = "businessService"; + + public static final String URI_BUSINESS_SERVICE_QUERY_TEMPLATE = "?tenantId={tenantId}&businessServices={businessService}"; + + public static final String APPROVE_CENSUS_DATA_ACTION = "APPROVE_CENSUS_DATA"; + + public static final String FINALIZE_CATCHMENT_MAPPING_ACTION = "FINALIZE_CATCHMENT_MAPPING"; + + public static final String APPROVE_ESTIMATIONS_ACTION = "APPROVE_ESTIMATIONS"; + + public static final String VALIDATED_STATUS = "VALIDATED"; + + //Query constants + public static final String PERCENTAGE_WILDCARD = "%"; + + public static final String MDMS_MASTER_HIERARCHY= "hierarchy"; + + public static final String ERROR_WHILE_FETCHING_FROM_FACILITY = "Exception occurred while fetching facility details from facility service "; + + public static final String ERROR_WHILE_FETCHING_BUSINESS_SERVICE_DETAILS = "Exception occurred while fetching business service details: "; + + public static final String INVALID_PLAN_FACILITY_ID_CODE = "INVALID_PLAN_FACILITY_ID"; + public static final String INVALID_PLAN_FACILITY_ID_MESSAGE = "Plan facility id provided is invalid"; + + public static final String INVALID_SERVICE_BOUNDARY_CODE = "INVALID_SERVICE_BOUNDARY"; + public static final String INVALID_SERVICE_BOUNDARY_MESSAGE = "The provided service boundary is invalid"; + + public static final String INVALID_RESIDING_BOUNDARY_CODE = "INVALID_RESIDING_BOUNDARY"; + public static final String INVALID_RESIDING_BOUNDARY_MESSAGE = "The provided residing boundary is invalid"; + + public static final String CANNOT_APPROVE_CENSUS_DATA_CODE = "CANNOT_APPROVE_CENSUS_DATA"; + public static final String CANNOT_APPROVE_CENSUS_DATA_MESSAGE = "Census data can't be approved until all the census records are validated"; + + public static final String CANNOT_APPROVE_ESTIMATIONS_CODE = "CANNOT_APPROVE_ESTIMATIONS"; + public static final String CANNOT_APPROVE_ESTIMATIONS_MESSAGE = "Estimations can't be approved until all the estimations are validated"; + + public static final String CANNOT_FINALIZE_CATCHMENT_MAPPING_CODE = "CANNOT_FINALIZE_CATCHMENT_MAPPING"; + public static final String CANNOT_FINALIZE_CATCHMENT_MAPPING_MESSAGE = "Catchment mapping can't be finalized until all boundaries have facility assigned"; + + public static final String HIERARCHY_NOT_FOUND_IN_MDMS_CODE = "HIERARCHY_NOT_FOUND_IN_MDMS"; + public static final String HIERARCHY_NOT_FOUND_IN_MDMS_MESSAGE = "Hierarchy key not found in mdms"; + + public static final String FAILED_MESSAGE = "Failed to push message to topic"; + + public static final String FACILITY_NAME_SEARCH_PARAMETER_KEY = "facilityName"; + + public static final String FACILITY_STATUS_SEARCH_PARAMETER_KEY = "facilityStatus"; + + public static final String FACILITY_TYPE_SEARCH_PARAMETER_KEY = "facilityType"; + + public static final String COMMA_DELIMITER = ","; + + public static final String SERVING_POPULATION_CODE = "servingPopulation"; + + public static final String CONFIRMED_TARGET_POPULATION_AGE_3TO11 = "CONFIRMED_HCM_ADMIN_CONSOLE_TARGET_POPULATION_AGE_3TO11"; + + public static final String CONFIRMED_TARGET_POPULATION_AGE_12TO59 = "CONFIRMED_HCM_ADMIN_CONSOLE_TARGET_POPULATION_AGE_12TO59"; + + public static final String CONFIRMED_TARGET_POPULATION = "CONFIRMED_HCM_ADMIN_CONSOLE_TARGET_POPULATION"; + } diff --git a/health-services/plan-service/src/main/java/digit/kafka/ProjectFactoryCreatePlanFacilityConsumer.java b/health-services/plan-service/src/main/java/digit/kafka/ProjectFactoryCreatePlanFacilityConsumer.java new file mode 100644 index 00000000000..f01da2db239 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/kafka/ProjectFactoryCreatePlanFacilityConsumer.java @@ -0,0 +1,57 @@ +package digit.kafka; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.repository.PlanFacilityRepository; +import digit.service.enrichment.PlanFacilityEnricher; +import digit.util.CommonUtil; +import digit.web.models.PlanFacilityRequest; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Map; + +import static digit.config.ServiceConstants.HIERARCHY_TYPE; + +@Slf4j +@Component +public class ProjectFactoryCreatePlanFacilityConsumer { + + private ObjectMapper objectMapper; + + private PlanFacilityEnricher enrichment; + + private CommonUtil commonUtil; + + private PlanFacilityRepository repository; + + public ProjectFactoryCreatePlanFacilityConsumer(ObjectMapper objectMapper, PlanFacilityEnricher enrichment, CommonUtil commonUtil, PlanFacilityRepository repository) { + this.objectMapper = objectMapper; + this.enrichment = enrichment; + this.commonUtil = commonUtil; + this.repository = repository; + } + + @KafkaListener(topics = {"${project.factory.save.plan.facility.consumer.topic}"}) + public void listen(Map consumerRecord, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { + try { + PlanFacilityRequest planFacilityRequest = objectMapper.convertValue(consumerRecord, PlanFacilityRequest.class); + String hierarchyType = commonUtil.extractFieldsFromJsonObject(planFacilityRequest.getPlanFacility().getAdditionalDetails(), HIERARCHY_TYPE).toString(); + + if(!StringUtils.isEmpty(hierarchyType)) + enrichment.enrichJurisdictionMapping(planFacilityRequest, hierarchyType); + + if(CollectionUtils.isEmpty(planFacilityRequest.getPlanFacility().getServiceBoundaries())) + planFacilityRequest.getPlanFacility().setServiceBoundaries(new ArrayList<>()); + + repository.create(planFacilityRequest); + } catch (Exception exception) { + log.error("Error in census consumer", exception); + } + } +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/kafka/UpdatePlanConfigConsumer.java b/health-services/plan-service/src/main/java/digit/kafka/UpdatePlanConfigConsumer.java index ca72bb21058..b2a97f1d77d 100644 --- a/health-services/plan-service/src/main/java/digit/kafka/UpdatePlanConfigConsumer.java +++ b/health-services/plan-service/src/main/java/digit/kafka/UpdatePlanConfigConsumer.java @@ -28,6 +28,7 @@ public UpdatePlanConfigConsumer(PlanConfigurationService planConfigurationServic public void listen(Map consumerRecord, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { try { PlanConfigurationRequest planConfigurationRequest = objectMapper.convertValue(consumerRecord, PlanConfigurationRequest.class); + log.info("Update plan config from resource generator."); planConfigurationService.update(planConfigurationRequest); } catch (Exception exception) { log.error("Error in update plan configuration consumer while processing topic {}: {}", topic, consumerRecord, exception); diff --git a/health-services/plan-service/src/main/java/digit/repository/PlanEmployeeAssignmentRepository.java b/health-services/plan-service/src/main/java/digit/repository/PlanEmployeeAssignmentRepository.java new file mode 100644 index 00000000000..370141a8697 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/PlanEmployeeAssignmentRepository.java @@ -0,0 +1,16 @@ +package digit.repository; + +import digit.web.models.*; + +import java.util.List; + +public interface PlanEmployeeAssignmentRepository { + + public void create(PlanEmployeeAssignmentRequest planEmployeeAssignmentRequest); + + public List search(PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria); + + public void update(PlanEmployeeAssignmentRequest planEmployeeAssignmentRequest); + + public Integer count(PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria); +} diff --git a/health-services/plan-service/src/main/java/digit/repository/PlanFacilityRepository.java b/health-services/plan-service/src/main/java/digit/repository/PlanFacilityRepository.java new file mode 100644 index 00000000000..ce94f569086 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/PlanFacilityRepository.java @@ -0,0 +1,15 @@ +package digit.repository; + +import digit.web.models.*; + +import java.util.List; + +public interface PlanFacilityRepository { + public void create(PlanFacilityRequest planFacilityRequest); + + public List search(PlanFacilitySearchCriteria planFacilitySearchCriteria); + + void update(PlanFacilityRequest planFacilityRequest); + + public Integer count(PlanFacilitySearchCriteria planFacilitySearchCriteria); +} diff --git a/health-services/plan-service/src/main/java/digit/repository/PlanRepository.java b/health-services/plan-service/src/main/java/digit/repository/PlanRepository.java index eb76b357701..1b8c334ed7c 100644 --- a/health-services/plan-service/src/main/java/digit/repository/PlanRepository.java +++ b/health-services/plan-service/src/main/java/digit/repository/PlanRepository.java @@ -1,10 +1,9 @@ package digit.repository; -import digit.web.models.Plan; -import digit.web.models.PlanRequest; -import digit.web.models.PlanSearchCriteria; +import digit.web.models.*; import java.util.List; +import java.util.Map; public interface PlanRepository { public void create(PlanRequest planRequest); @@ -13,4 +12,9 @@ public interface PlanRepository { public void update(PlanRequest planRequest); + public Integer count(PlanSearchCriteria planSearchCriteria); + + public Map statusCount(PlanSearchRequest planSearchRequest); + + public void bulkUpdate(BulkPlanRequest body); } diff --git a/health-services/plan-service/src/main/java/digit/repository/ServiceRequestRepository.java b/health-services/plan-service/src/main/java/digit/repository/ServiceRequestRepository.java index d09d230e4fa..5a724b5b219 100644 --- a/health-services/plan-service/src/main/java/digit/repository/ServiceRequestRepository.java +++ b/health-services/plan-service/src/main/java/digit/repository/ServiceRequestRepository.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.model.CustomException; import org.egov.tracer.model.ServiceCallException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @@ -35,9 +35,10 @@ public Object fetchResult(StringBuilder uri, Object request) { response = restTemplate.postForObject(uri.toString(), request, Map.class); } catch (HttpClientErrorException e) { log.error(EXTERNAL_SERVICE_EXCEPTION, e); - throw new ServiceCallException(e.getResponseBodyAsString()); + throw new CustomException(EXTERNAL_SERVICE_EXCEPTION, e.getResponseBodyAsString()); } catch (Exception e) { log.error(SEARCHER_SERVICE_EXCEPTION, e); + throw new CustomException(EXTERNAL_SERVICE_EXCEPTION, e.getMessage()); } return response; diff --git a/health-services/plan-service/src/main/java/digit/repository/impl/PlanEmployeeAssignmentImpl.java b/health-services/plan-service/src/main/java/digit/repository/impl/PlanEmployeeAssignmentImpl.java new file mode 100644 index 00000000000..bba32818812 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/impl/PlanEmployeeAssignmentImpl.java @@ -0,0 +1,121 @@ +package digit.repository.impl; + +import digit.config.Configuration; +import digit.kafka.Producer; +import digit.repository.PlanEmployeeAssignmentRepository; +import digit.repository.querybuilder.PlanEmployeeAssignmentQueryBuilder; +import digit.repository.rowmapper.PlanEmployeeAssignmentRowMapper; +import digit.web.models.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Repository +public class PlanEmployeeAssignmentImpl implements PlanEmployeeAssignmentRepository { + + private Producer producer; + + private Configuration config; + + private PlanEmployeeAssignmentQueryBuilder queryBuilder; + + private JdbcTemplate jdbcTemplate; + + private PlanEmployeeAssignmentRowMapper rowMapper; + + public PlanEmployeeAssignmentImpl(Producer producer, Configuration config, PlanEmployeeAssignmentQueryBuilder queryBuilder, JdbcTemplate jdbcTemplate, PlanEmployeeAssignmentRowMapper rowMapper) { + this.producer = producer; + this.config = config; + this.queryBuilder = queryBuilder; + this.jdbcTemplate = jdbcTemplate; + this.rowMapper = rowMapper; + } + + /** + * Pushes a new plan employee assignment to persister kafka topic. + * + * @param planEmployeeAssignmentRequest The request containing the plan employee assignment details. + */ + @Override + public void create(PlanEmployeeAssignmentRequest planEmployeeAssignmentRequest) { + PlanEmployeeAssignmentRequestDTO requestDTO = convertToReqDTO(planEmployeeAssignmentRequest); + producer.push(config.getPlanEmployeeAssignmentCreateTopic(), requestDTO); + } + + /** + * Searches for Plan employee assignments based on provided search criteria + * + * @param searchCriteria The criteria used for searching plan employee assignments + * @return A list of Plan employee assignments that matches the search criteria + */ + @Override + public List search(PlanEmployeeAssignmentSearchCriteria searchCriteria) { + List preparedStmtList = new ArrayList<>(); + String searchQuery = queryBuilder.getPlanEmployeeAssignmentQuery(searchCriteria, preparedStmtList); + log.info("Plan Employee Assignment search query : " + searchQuery); + + List planEmployeeAssignments = jdbcTemplate.query(searchQuery, rowMapper, preparedStmtList.toArray()); + return planEmployeeAssignments; + } + + /** + * Counts the number of plan employee assignments based on the provided search criteria. + * + * @param searchCriteria The search criteria for filtering plan employee assignments. + * @return The total count of plan employee assignment matching the search criteria. + */ + @Override + public Integer count(PlanEmployeeAssignmentSearchCriteria searchCriteria) { + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getPlanEmployeeAssignmentCountQuery(searchCriteria, preparedStmtList); + Integer count = jdbcTemplate.queryForObject(query, preparedStmtList.toArray(), Integer.class); + + return count; + } + + /** + * Pushes an updated existing plan employee assignment to persister kafka topic. + * + * @param planEmployeeAssignmentRequest The request containing the updated plan employee assignment details. + */ + @Override + public void update(PlanEmployeeAssignmentRequest planEmployeeAssignmentRequest) { + PlanEmployeeAssignmentRequestDTO requestDTO = convertToReqDTO(planEmployeeAssignmentRequest); + producer.push(config.getPlanEmployeeAssignmentUpdateTopic(), requestDTO); + } + + /** + * Converts the PlanEmployeeAssignmentRequest to a data transfer object (DTO) + * + * @param planEmployeeAssignmentRequest The request to be converted to DTO + * @return a DTO for PlanEmployeeAssignmentRequest + */ + public PlanEmployeeAssignmentRequestDTO convertToReqDTO(PlanEmployeeAssignmentRequest planEmployeeAssignmentRequest) { + PlanEmployeeAssignment planEmployeeAssignment = planEmployeeAssignmentRequest.getPlanEmployeeAssignment(); + + // Creating a new data transfer object (DTO) for PlanEmployeeAssignment + PlanEmployeeAssignmentDTO planEmployeeAssignmentDTO = PlanEmployeeAssignmentDTO.builder() + .id(planEmployeeAssignment.getId()) + .tenantId(planEmployeeAssignment.getTenantId()) + .planConfigurationId(planEmployeeAssignment.getPlanConfigurationId()) + .employeeId(planEmployeeAssignment.getEmployeeId()) + .role(planEmployeeAssignment.getRole()) + .planConfigurationName(planEmployeeAssignment.getPlanConfigurationName()) + .hierarchyLevel(planEmployeeAssignment.getHierarchyLevel()) + .jurisdiction(String.join(",", planEmployeeAssignment.getJurisdiction())) + .additionalDetails(planEmployeeAssignment.getAdditionalDetails()) + .active(planEmployeeAssignment.getActive()) + .auditDetails(planEmployeeAssignment.getAuditDetails()) + .build(); + + return PlanEmployeeAssignmentRequestDTO.builder() + .requestInfo(planEmployeeAssignmentRequest.getRequestInfo()) + .planEmployeeAssignmentDTO(planEmployeeAssignmentDTO) + .build(); + } + +} diff --git a/health-services/plan-service/src/main/java/digit/repository/impl/PlanFacilityRepositoryImpl.java b/health-services/plan-service/src/main/java/digit/repository/impl/PlanFacilityRepositoryImpl.java new file mode 100644 index 00000000000..2d3c2304bae --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/impl/PlanFacilityRepositoryImpl.java @@ -0,0 +1,150 @@ +package digit.repository.impl; + +import digit.config.Configuration; +import digit.kafka.Producer; +import digit.repository.PlanFacilityRepository; +import digit.repository.querybuilder.PlanFacilityQueryBuilder; +import digit.repository.rowmapper.PlanFacilityRowMapper; +import digit.web.models.*; +import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.model.CustomException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import java.util.ArrayList; +import java.util.List; +import static digit.config.ServiceConstants.*; + +@Repository +@Slf4j +public class PlanFacilityRepositoryImpl implements PlanFacilityRepository { + + private Producer producer; + + private JdbcTemplate jdbcTemplate; + + private PlanFacilityQueryBuilder planFacilityQueryBuilder; + + private PlanFacilityRowMapper planFacilityRowMapper; + + private Configuration config; + + public PlanFacilityRepositoryImpl(Producer producer, JdbcTemplate jdbcTemplate, PlanFacilityQueryBuilder planFacilityQueryBuilder, PlanFacilityRowMapper planFacilityRowMapper, Configuration config) { + this.producer = producer; + this.jdbcTemplate = jdbcTemplate; + this.planFacilityQueryBuilder = planFacilityQueryBuilder; + this.planFacilityRowMapper = planFacilityRowMapper; + this.config = config; + } + + /** + * This method emits an event to the persister for it to save the plan facility linkage in the database. + * @param planFacilityRequest + */ + @Override + public void create(PlanFacilityRequest planFacilityRequest) { + // Convert the incoming PlanFacilityRequest to PlanFacilityRequestDTO + PlanFacilityRequestDTO requestDTO = convertToDTO(planFacilityRequest); + + // Push the requestDTO to the producer for processing + producer.push(config.getPlanFacilityCreateTopic(), requestDTO); + } + + public PlanFacilityRequestDTO convertToDTO(PlanFacilityRequest planFacilityRequest) { + PlanFacility planFacility = planFacilityRequest.getPlanFacility(); + + // Create a new PlanFacilityDTO + PlanFacilityDTO planFacilityDTO = PlanFacilityDTO.builder() + .id(planFacility.getId()) + .tenantId(planFacility.getTenantId()) + .planConfigurationId(planFacility.getPlanConfigurationId()) + .planConfigurationName(planFacility.getPlanConfigurationName()) + .facilityId(planFacility.getFacilityId()) + .facilityName(planFacility.getFacilityName()) + .jurisdictionMapping(planFacility.getJurisdictionMapping()) + .boundaryAncestralPath(planFacility.getBoundaryAncestralPath()) + .residingBoundary(planFacility.getResidingBoundary()) + .serviceBoundaries(convertArrayToString(planFacility.getServiceBoundaries())) + .initiallySetServiceBoundaries(planFacility.getInitiallySetServiceBoundaries()) + .additionalDetails(planFacility.getAdditionalDetails()) + .active(planFacility.getActive()) + .auditDetails(planFacility.getAuditDetails()) + .build(); + + // Return the complete PlanFacilityRequestDTO + return PlanFacilityRequestDTO.builder() + .requestInfo(planFacilityRequest.getRequestInfo()) + .planFacilityDTO(planFacilityDTO) + .build(); + } + + /** + * This is a helper function to convert an array of string to comma separated string + * + * @param stringList Array of string to be converted + * @return a string + */ + private String convertArrayToString(List stringList) { + return String.join(COMMA_DELIMITER, stringList); + } + + + + /** + * This method searches for plans based on the search criteria. + * + * @param planFacilitySearchCriteria + * @return List + */ + @Override + public List search(PlanFacilitySearchCriteria planFacilitySearchCriteria) { + // Fetch plan facility from database + return queryDatabaseForPlanFacilities(planFacilitySearchCriteria); + } + + /** + * This method emits an event to the persister for it to update the plan facility in the database. + * + * @param planFacilityRequest + */ + @Override + public void update(PlanFacilityRequest planFacilityRequest) { + try { + PlanFacilityRequestDTO requestDTO = convertToDTO(planFacilityRequest); + producer.push(config.getPlanFacilityUpdateTopic(), requestDTO); + log.info("Successfully pushed update for plan facility: {}", planFacilityRequest.getPlanFacility().getId()); + } catch (Exception e) { + throw new CustomException(FAILED_MESSAGE,config.getPlanFacilityUpdateTopic()); + } + } + + /** + * Counts the number of plan facilities based on the provided search criteria. + * + * @param planFacilitySearchCriteria The search criteria for filtering plan facilities. + * @return The total count of plan facilities matching the search criteria. + */ + @Override + public Integer count(PlanFacilitySearchCriteria planFacilitySearchCriteria) { + List preparedStmtList = new ArrayList<>(); + String query = planFacilityQueryBuilder.getPlanFacilityCountQuery(planFacilitySearchCriteria, preparedStmtList); + Integer count = jdbcTemplate.queryForObject(query, preparedStmtList.toArray(), Integer.class); + + return count; + } + + /** + * Helper method to query database for plan facilities based on the provided search criteria. + * + * @param planFacilitySearchCriteria + * @return List + */ + private List queryDatabaseForPlanFacilities(PlanFacilitySearchCriteria planFacilitySearchCriteria) { + List preparedStmtList = new ArrayList<>(); + String query = planFacilityQueryBuilder.getPlanFacilitySearchQuery(planFacilitySearchCriteria, preparedStmtList); + log.info("Plan facility search {}", query); + log.info(preparedStmtList.toString()); + return jdbcTemplate.query(query, planFacilityRowMapper, preparedStmtList.toArray()); + } + + +} diff --git a/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java b/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java index c6acbb55221..99e4b9487de 100644 --- a/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java +++ b/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java @@ -5,17 +5,21 @@ import digit.repository.PlanRepository; import digit.repository.querybuilder.PlanQueryBuilder; import digit.repository.rowmapper.PlanRowMapper; -import digit.web.models.Plan; -import digit.web.models.PlanRequest; -import digit.web.models.PlanSearchCriteria; +import digit.repository.rowmapper.PlanStatusCountRowMapper; +import digit.service.workflow.WorkflowService; +import digit.web.models.*; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.stereotype.Repository; import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import static digit.config.ServiceConstants.PLAN_ESTIMATION_BUSINESS_SERVICE; @Slf4j @Repository @@ -31,13 +35,19 @@ public class PlanRepositoryImpl implements PlanRepository { private Configuration config; + private PlanStatusCountRowMapper statusCountRowMapper; + + private WorkflowService workflowService; + public PlanRepositoryImpl(Producer producer, PlanQueryBuilder planQueryBuilder, PlanRowMapper planRowMapper, - JdbcTemplate jdbcTemplate, Configuration config) { + JdbcTemplate jdbcTemplate, Configuration config, PlanStatusCountRowMapper statusCountRowMapper, WorkflowService workflowService) { this.producer = producer; this.planQueryBuilder = planQueryBuilder; this.planRowMapper = planRowMapper; this.jdbcTemplate = jdbcTemplate; this.config = config; + this.statusCountRowMapper = statusCountRowMapper; + this.workflowService = workflowService; } /** @@ -46,11 +56,8 @@ public PlanRepositoryImpl(Producer producer, PlanQueryBuilder planQueryBuilder, */ @Override public void create(PlanRequest planRequest) { - try { - producer.push(config.getPlanCreateTopic(), planRequest); - } catch (Exception e) { - log.info("Pushing message to topic " + config.getPlanCreateTopic() + " failed.", e); - } + PlanRequestDTO planRequestDTO = convertToPlanReqDTO(planRequest); + producer.push(config.getPlanCreateTopic(), planRequestDTO); } /** @@ -70,9 +77,29 @@ public List search(PlanSearchCriteria planSearchCriteria) { } // Fetch plans from database based on the acquired ids - List plans = searchPlanByIds(planIds); + return searchPlanByIds(planIds); + } - return plans; + /** + * Counts the plan based on their current status for the provided search criteria. + * + * @param planSearchRequest The search criteria for filtering plans. + * @return The status count of plans for the given search criteria. + */ + @Override + public Map statusCount(PlanSearchRequest planSearchRequest) { + List preparedStmtList = new ArrayList<>(); + List statusList = workflowService.getStatusFromBusinessService(planSearchRequest.getRequestInfo(), PLAN_ESTIMATION_BUSINESS_SERVICE, planSearchRequest.getPlanSearchCriteria().getTenantId()); + + String query = planQueryBuilder.getPlanStatusCountQuery(planSearchRequest.getPlanSearchCriteria(), preparedStmtList); + Map statusCountMap = jdbcTemplate.query(query, statusCountRowMapper, preparedStmtList.toArray()); + + statusList.forEach(status -> { + if(ObjectUtils.isEmpty(statusCountMap.get(status))) + statusCountMap.put(status, 0); + }); + + return statusCountMap; } /** @@ -81,13 +108,41 @@ public List search(PlanSearchCriteria planSearchCriteria) { */ @Override public void update(PlanRequest planRequest) { - try { - producer.push(config.getPlanUpdateTopic(), planRequest); - } catch (Exception e) { - log.info("Pushing message to topic " + config.getPlanUpdateTopic() + " failed.", e); - } + PlanRequestDTO planRequestDTO = convertToPlanReqDTO(planRequest); + producer.push(config.getPlanUpdateTopic(), planRequestDTO); } + @Override + public void bulkUpdate(BulkPlanRequest body) { + // Get bulk plan update query + String bulkPlanUpdateQuery = planQueryBuilder.getBulkPlanQuery(); + + // Prepare rows for bulk update + List rows = body.getPlans().stream().map(plan -> new Object[] { + plan.getStatus(), + !CollectionUtils.isEmpty(plan.getAssignee()) ? String.join(",", plan.getAssignee()) : plan.getAssignee(), + plan.getAuditDetails().getLastModifiedBy(), + plan.getAuditDetails().getLastModifiedTime(), + plan.getId() + }).toList(); + + // Perform batch update + jdbcTemplate.batchUpdate(bulkPlanUpdateQuery, rows); + producer.push(config.getPlanBulkUpdateTopic(), body); + } + + /** + * Counts the number of plans based on the provided search criteria. + * @param planSearchCriteria The search criteria for filtering plans. + * @return The total count of plans matching the search criteria. + */ + @Override + public Integer count(PlanSearchCriteria planSearchCriteria) { + List preparedStmtList = new ArrayList<>(); + String query = planQueryBuilder.getPlanCountQuery(planSearchCriteria, preparedStmtList); + return jdbcTemplate.queryForObject(query, preparedStmtList.toArray(), Integer.class); + } + /** * Helper method to query database for plan ids based on the provided search criteria. * @param planSearchCriteria @@ -112,4 +167,41 @@ private List searchPlanByIds(List planIds) { return jdbcTemplate.query(query, planRowMapper, preparedStmtList.toArray()); } + /** + * Converts the PlanRequest to a data transfer object (DTO) + * + * @param planRequest The request to be converted to DTO + * @return a DTO for PlanRequest + */ + private PlanRequestDTO convertToPlanReqDTO(PlanRequest planRequest) { + Plan plan = planRequest.getPlan(); + + String assignee = !CollectionUtils.isEmpty(plan.getAssignee()) ? String.join(",", plan.getAssignee()) : null; + + // Creating a new data transfer object (DTO) for Plan + PlanDTO planDTO = PlanDTO.builder() + .id(plan.getId()) + .tenantId(plan.getTenantId()) + .locality(plan.getLocality()) + .campaignId(plan.getCampaignId()) + .planConfigurationId(plan.getPlanConfigurationId()) + .status(plan.getStatus()) + .assignee(assignee) + .additionalDetails(plan.getAdditionalDetails()) + .jurisdictionMapping(plan.getJurisdictionMapping()) + .activities(plan.getActivities()) + .resources(plan.getResources()) + .targets(plan.getTargets()) + .auditDetails(plan.getAuditDetails()) + .boundaryAncestralPath(plan.getBoundaryAncestralPath()) + .build(); + + // Returning the PlanRequestDTO + return PlanRequestDTO.builder() + .requestInfo(planRequest.getRequestInfo()) + .planDTO(planDTO) + .build(); + } + + } diff --git a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanConfigQueryBuilder.java b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanConfigQueryBuilder.java index 6dc4449b55f..e8caef51d94 100644 --- a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanConfigQueryBuilder.java +++ b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanConfigQueryBuilder.java @@ -1,30 +1,35 @@ package digit.repository.querybuilder; import digit.config.Configuration; - import digit.util.QueryUtil; import digit.web.models.PlanConfigurationSearchCriteria; -import java.util.LinkedHashSet; -import java.util.List; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; +import java.util.LinkedHashSet; +import java.util.List; + +import static digit.config.ServiceConstants.PERCENTAGE_WILDCARD; + @Component public class PlanConfigQueryBuilder { private Configuration config; - public PlanConfigQueryBuilder(Configuration config) { + private QueryUtil queryUtil; + + public PlanConfigQueryBuilder(Configuration config, QueryUtil queryUtil) { this.config = config; + this.queryUtil = queryUtil; } private static final String PLAN_CONFIG_SEARCH_BASE_QUERY = "SELECT id FROM plan_configuration pc "; - private static final String PLAN_CONFIG_QUERY = "SELECT pc.id as plan_configuration_id, pc.tenant_id as plan_configuration_tenant_id, pc.name as plan_configuration_name, pc.execution_plan_id as plan_configuration_execution_plan_id, pc.status as plan_configuration_status, pc.created_by as plan_configuration_created_by, pc.created_time as plan_configuration_created_time, pc.last_modified_by as plan_configuration_last_modified_by, pc.last_modified_time as plan_configuration_last_modified_time, \n" + + private static final String PLAN_CONFIG_QUERY = "SELECT pc.id as plan_configuration_id, pc.tenant_id as plan_configuration_tenant_id, pc.name as plan_configuration_name, pc.campaign_id as plan_configuration_campaign_id, pc.status as plan_configuration_status, pc.additional_details as plan_configuration_additional_details, pc.created_by as plan_configuration_created_by, pc.created_time as plan_configuration_created_time, pc.last_modified_by as plan_configuration_last_modified_by, pc.last_modified_time as plan_configuration_last_modified_time, \n" + "\t pcf.id as plan_configuration_files_id, pcf.plan_configuration_id as plan_configuration_files_plan_configuration_id, pcf.filestore_id as plan_configuration_files_filestore_id, pcf.input_file_type as plan_configuration_files_input_file_type, pcf.template_identifier as plan_configuration_files_template_identifier, pcf.active as plan_configuration_files_active, pcf.created_by as plan_configuration_files_created_by, pcf.created_time as plan_configuration_files_created_time, pcf.last_modified_by as plan_configuration_files_last_modified_by, pcf.last_modified_time as plan_configuration_files_last_modified_time,\n" + - "\t pca.id as plan_configuration_assumptions_id, pca.key as plan_configuration_assumptions_key, pca.value as plan_configuration_assumptions_value, pca.active as plan_configuration_assumptions_active, pca.plan_configuration_id as plan_configuration_assumptions_plan_configuration_id, pca.created_by as plan_configuration_assumptions_created_by, pca.created_time as plan_configuration_assumptions_created_time, pca.last_modified_by as plan_configuration_assumptions_last_modified_by, pca.last_modified_time as plan_configuration_assumptions_last_modified_time,\n" + - "\t pco.id as plan_configuration_operations_id, pco.input as plan_configuration_operations_input, pco.operator as plan_configuration_operations_operator, pco.assumption_value as plan_configuration_operations_assumption_value, pco.output as plan_configuration_operations_output, pco.active as plan_configuration_operations_active, pco.plan_configuration_id as plan_configuration_operations_plan_configuration_id, pco.created_by as plan_configuration_operations_created_by, pco.created_time as plan_configuration_operations_created_time, pco.last_modified_by as plan_configuration_operations_last_modified_by, pco.last_modified_time as plan_configuration_operations_last_modified_time,\n" + + "\t pca.id as plan_configuration_assumptions_id, pca.key as plan_configuration_assumptions_key, pca.value as plan_configuration_assumptions_value, pca.source as plan_configuration_assumptions_source, pca.category as plan_configuration_assumptions_category, pca.active as plan_configuration_assumptions_active, pca.plan_configuration_id as plan_configuration_assumptions_plan_configuration_id, pca.created_by as plan_configuration_assumptions_created_by, pca.created_time as plan_configuration_assumptions_created_time, pca.last_modified_by as plan_configuration_assumptions_last_modified_by, pca.last_modified_time as plan_configuration_assumptions_last_modified_time,\n" + + "\t pco.id as plan_configuration_operations_id, pco.input as plan_configuration_operations_input, pco.operator as plan_configuration_operations_operator, pco.assumption_value as plan_configuration_operations_assumption_value, pco.output as plan_configuration_operations_output, pco.source as plan_configuration_operations_source, pco.category as plan_configuration_operations_category, pco.active as plan_configuration_operations_active, pco.execution_order as plan_configuration_execution_order, pco.show_on_estimation_dashboard as plan_configuration_operations_show_on_estimation_dashboard,pco.plan_configuration_id as plan_configuration_operations_plan_configuration_id, pco.created_by as plan_configuration_operations_created_by, pco.created_time as plan_configuration_operations_created_time, pco.last_modified_by as plan_configuration_operations_last_modified_by, pco.last_modified_time as plan_configuration_operations_last_modified_time,\n" + "\t pcm.id as plan_configuration_mapping_id, pcm.filestore_id as plan_configuration_mapping_filestore_id, pcm.mapped_from as plan_configuration_mapping_mapped_from, pcm.mapped_to as plan_configuration_mapping_mapped_to, pcm.active as plan_configuration_mapping_active, pcm.plan_configuration_id as plan_configuration_mapping_plan_configuration_id, pcm.created_by as plan_configuration_mapping_created_by, pcm.created_time as plan_configuration_mapping_created_time, pcm.last_modified_by as plan_configuration_mapping_last_modified_by, pcm.last_modified_time as plan_configuration_mapping_last_modified_time\n" + "\t FROM plan_configuration pc\n" + "\t LEFT JOIN plan_configuration_files pcf ON pc.id = pcf.plan_configuration_id\n" + @@ -44,14 +49,14 @@ private String buildPlanConfigQuery(List ids, List preparedStmtL StringBuilder builder = new StringBuilder(PLAN_CONFIG_QUERY); if (!CollectionUtils.isEmpty(ids)) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); - builder.append(" pc.id IN ( ").append(QueryUtil.createQuery(ids.size())).append(" )"); - QueryUtil.addToPreparedStatement(preparedStmtList, new LinkedHashSet<>(ids)); + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pc.id IN ( ").append(queryUtil.createQuery(ids.size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, ids); } addActiveWhereClause(builder, preparedStmtList); - return QueryUtil.addOrderByClause(builder.toString(), PLAN_CONFIG_SEARCH_QUERY_ORDER_BY_CLAUSE); + return queryUtil.addOrderByClause(builder.toString(), PLAN_CONFIG_SEARCH_QUERY_ORDER_BY_CLAUSE); } /** @@ -64,7 +69,7 @@ private String buildPlanConfigQuery(List ids, List preparedStmtL */ public String getPlanConfigSearchQuery(PlanConfigurationSearchCriteria criteria, List preparedStmtList) { String query = buildPlanConfigSearchQuery(criteria, preparedStmtList, Boolean.FALSE); - query = QueryUtil.addOrderByClause(query, PLAN_CONFIG_SEARCH_QUERY_ORDER_BY_CLAUSE); + query = queryUtil.addOrderByClause(query, PLAN_CONFIG_SEARCH_QUERY_ORDER_BY_CLAUSE); query = getPaginatedQuery(query, criteria, preparedStmtList); return query; @@ -97,22 +102,28 @@ private String buildPlanConfigSearchQuery(PlanConfigurationSearchCriteria criter preparedStmtList.add(criteria.getId()); } - if (criteria.getExecutionPlanId() != null) { + if (!CollectionUtils.isEmpty(criteria.getIds())) { + addClauseIfRequired(preparedStmtList, builder); + builder.append(" pc.id IN ( ").append(queryUtil.createQuery(criteria.getIds().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, criteria.getIds()); + } + + if (criteria.getCampaignId() != null) { addClauseIfRequired(preparedStmtList, builder); - builder.append(" pc.execution_plan_id = ?"); - preparedStmtList.add(criteria.getExecutionPlanId()); + builder.append(" pc.campaign_id = ?"); + preparedStmtList.add(criteria.getCampaignId()); } if (criteria.getName() != null) { addClauseIfRequired(preparedStmtList, builder); - builder.append(" pc.name = ?"); - preparedStmtList.add(criteria.getName()); + builder.append(" pc.name ILIKE ?"); + preparedStmtList.add(PERCENTAGE_WILDCARD + criteria.getName() + PERCENTAGE_WILDCARD); } - if (criteria.getStatus() != null) { + if (!CollectionUtils.isEmpty(criteria.getStatus())) { addClauseIfRequired(preparedStmtList, builder); - builder.append(" pc.status = ?"); - preparedStmtList.add(criteria.getStatus()); + builder.append(" pc.status IN ( ").append(queryUtil.createQuery(criteria.getStatus().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, criteria.getStatus()); } if (criteria.getUserUuid() != null) { @@ -162,22 +173,21 @@ private String getPaginatedQuery(String query, PlanConfigurationSearchCriteria p return paginatedQuery.toString(); } - public void addActiveWhereClause(StringBuilder builder, List preparedStmtList) - { + public void addActiveWhereClause(StringBuilder builder, List preparedStmtList) { addClauseIfRequired(preparedStmtList, builder); - builder.append(" pcf.active = ?"); + builder.append(" ( pcf.active = ? OR pcf.active IS NULL )"); preparedStmtList.add(Boolean.TRUE); addClauseIfRequired(preparedStmtList, builder); - builder.append(" pca.active = ?"); + builder.append(" ( pca.active = ? OR pca.active IS NULL )"); preparedStmtList.add(Boolean.TRUE); addClauseIfRequired(preparedStmtList, builder); - builder.append(" pco.active = ?"); + builder.append(" ( pco.active = ? OR pco.active IS NULL )"); preparedStmtList.add(Boolean.TRUE); addClauseIfRequired(preparedStmtList, builder); - builder.append(" pcm.active = ?"); + builder.append(" ( pcm.active = ? OR pcm.active IS NULL )"); preparedStmtList.add(Boolean.TRUE); } diff --git a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanEmployeeAssignmentQueryBuilder.java b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanEmployeeAssignmentQueryBuilder.java new file mode 100644 index 00000000000..b63f790f806 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanEmployeeAssignmentQueryBuilder.java @@ -0,0 +1,170 @@ +package digit.repository.querybuilder; + +import digit.config.Configuration; +import digit.util.QueryUtil; +import digit.web.models.PlanEmployeeAssignmentSearchCriteria; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; + +import static digit.config.ServiceConstants.PERCENTAGE_WILDCARD; + +@Component +public class PlanEmployeeAssignmentQueryBuilder { + + private QueryUtil queryUtil; + + private Configuration config; + + public PlanEmployeeAssignmentQueryBuilder(QueryUtil queryUtil, Configuration config) { + + this.queryUtil = queryUtil; + this.config = config; + } + + private static final String PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_BASE_QUERY = "SELECT pa.id, pa.tenant_id, pa.plan_configuration_id, pa.plan_configuration_name, pa.employee_id, pa.role, pa.hierarchy_level, pa.jurisdiction, pa.additional_details, pa.active, pa.created_by, pa.created_time, pa.last_modified_by, pa.last_modified_time FROM plan_employee_assignment pa "; + + private static final String PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_QUERY_ORDER_BY_CLAUSE = " ORDER BY pa.last_modified_time DESC"; + + private static final String UNIQUE_PLAN_EMPLOYEE_ASSIGNMENT_RANKED_QUERY = "WITH ranked_assignments AS ( SELECT pa.id, pa.tenant_id, pa.plan_configuration_id, pa.plan_configuration_name,pa.employee_id, pa.role, pa.hierarchy_level, pa.jurisdiction, pa.additional_details, pa.active, pa.created_by, pa.created_time, pa.last_modified_by, pa.last_modified_time, pc.last_modified_time AS pc_last_modified_time, ROW_NUMBER() OVER ( PARTITION BY pa.plan_configuration_id ORDER BY pc.last_modified_time DESC ) AS row_num FROM plan_employee_assignment pa JOIN plan_configuration pc ON pa.plan_configuration_id = pc.id "; + + private static final String UNIQUE_PLAN_EMPLOYEE_ASSIGNMENT_MAIN_SEARCH_QUERY = " SELECT id, tenant_id, plan_configuration_id, plan_configuration_name, employee_id, role, hierarchy_level, jurisdiction, additional_details, active, created_by, created_time, last_modified_by, last_modified_time FROM ranked_assignments WHERE row_num = 1 "; + + private static final String UNIQUE_PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_QUERY_ORDER_BY_CLAUSE = " ORDER BY pc_last_modified_time DESC "; + + private static final String PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_QUERY_COUNT_WRAPPER = "SELECT COUNT(id) AS total_count FROM ( "; + + /** + * Constructs a SQL query string for searching PlanEmployeeAssignment objects based on the provided search criteria. + * Also adds an ORDER BY clause and handles pagination. + * + * @param searchCriteria The criteria used for filtering PlanEmployeeAssignment objects. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A complete SQL query string for searching PlanEmployeeAssignment objects. + */ + public String getPlanEmployeeAssignmentQuery(PlanEmployeeAssignmentSearchCriteria searchCriteria, List preparedStmtList) { + String query = buildPlanEmployeeAssignmentQuery(searchCriteria, preparedStmtList, Boolean.FALSE); + query = queryUtil.addOrderByClause(query, Boolean.TRUE.equals(searchCriteria.getFilterUniqueByPlanConfig()) ? + UNIQUE_PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_QUERY_ORDER_BY_CLAUSE : PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_QUERY_ORDER_BY_CLAUSE); + query = getPaginatedQuery(query, searchCriteria, preparedStmtList); + return query; + } + + /** + * Constructs the count query to get the total count of plan employee assignments based on search criteria + * + * @param searchCriteria The criteria used for filtering PlanEmployeeAssignment objects. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A SQL query string to get the total count of plan employee assignments + */ + public String getPlanEmployeeAssignmentCountQuery(PlanEmployeeAssignmentSearchCriteria searchCriteria, List preparedStmtList) { + String query = buildPlanEmployeeAssignmentQuery(searchCriteria, preparedStmtList, Boolean.TRUE); + return query; + } + + /** + * Constructs query based on the provided search criteria + * + * @param searchCriteria The criteria used for filtering PlanEmployeeAssignment objects. + * @param preparedStmtList A list to store prepared statement parameters. + * @param isCount is true if count query is required for the provided search criteria + * @return A SQL query string for searching planEmployeeAssignment + */ + private String buildPlanEmployeeAssignmentQuery(PlanEmployeeAssignmentSearchCriteria searchCriteria, List preparedStmtList, Boolean isCount) { + StringBuilder builder = Boolean.TRUE.equals(searchCriteria.getFilterUniqueByPlanConfig()) ? + new StringBuilder(UNIQUE_PLAN_EMPLOYEE_ASSIGNMENT_RANKED_QUERY) : new StringBuilder(PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_BASE_QUERY); + + if (searchCriteria.getId() != null) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.id = ?"); + preparedStmtList.add(searchCriteria.getId()); + } + + if (searchCriteria.getTenantId() != null) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.tenant_id = ?"); + preparedStmtList.add(searchCriteria.getTenantId()); + } + + if (searchCriteria.getPlanConfigurationId() != null) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.plan_configuration_id = ?"); + preparedStmtList.add(searchCriteria.getPlanConfigurationId()); + } + + if (searchCriteria.getPlanConfigurationName() != null) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.plan_configuration_name ILIKE ?"); + preparedStmtList.add(PERCENTAGE_WILDCARD + searchCriteria.getPlanConfigurationName() + PERCENTAGE_WILDCARD); + } + + if (!CollectionUtils.isEmpty(searchCriteria.getEmployeeId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.employee_id IN ( ").append(queryUtil.createQuery(searchCriteria.getEmployeeId().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, searchCriteria.getEmployeeId()); + } + + if (!CollectionUtils.isEmpty(searchCriteria.getPlanConfigurationStatus())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pc.status IN ( ").append(queryUtil.createQuery(searchCriteria.getPlanConfigurationStatus().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, searchCriteria.getPlanConfigurationStatus()); + } + + if (!CollectionUtils.isEmpty(searchCriteria.getRole())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.role IN ( ").append(queryUtil.createQuery(searchCriteria.getRole().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, searchCriteria.getRole()); + } + + if(searchCriteria.getHierarchyLevel() != null) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.hierarchy_level = ?"); + preparedStmtList.add(searchCriteria.getHierarchyLevel()); + } + + if (searchCriteria.getActive() != null) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" pa.active = ?"); + preparedStmtList.add(searchCriteria.getActive()); + } + + if (!CollectionUtils.isEmpty(searchCriteria.getJurisdiction())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" ARRAY [ ").append(queryUtil.createQuery(searchCriteria.getJurisdiction().size())).append(" ]").append("::text[] "); + builder.append(" && string_to_array(jurisdiction, ',') "); + queryUtil.addToPreparedStatement(preparedStmtList, searchCriteria.getJurisdiction()); + } + + if(searchCriteria.getFilterUniqueByPlanConfig()) { + builder.append(" )").append(UNIQUE_PLAN_EMPLOYEE_ASSIGNMENT_MAIN_SEARCH_QUERY); + } + + StringBuilder countQuery = new StringBuilder(); + if (isCount) { + countQuery.append(PLAN_EMPLOYEE_ASSIGNMENT_SEARCH_QUERY_COUNT_WRAPPER).append(builder); + countQuery.append(") AS subquery"); + + return countQuery.toString(); + } + + return builder.toString(); + } + + private String getPaginatedQuery(String query, PlanEmployeeAssignmentSearchCriteria searchCriteria, List preparedStmtList) { + StringBuilder paginatedQuery = new StringBuilder(query); + + // Append offset + paginatedQuery.append(" OFFSET ? "); + preparedStmtList.add(ObjectUtils.isEmpty(searchCriteria.getOffset()) ? config.getDefaultOffset() : searchCriteria.getOffset()); + + // Append limit + paginatedQuery.append(" LIMIT ? "); + preparedStmtList.add(ObjectUtils.isEmpty(searchCriteria.getLimit()) ? config.getDefaultLimit() : searchCriteria.getLimit()); + + return paginatedQuery.toString(); + } +} diff --git a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanFacilityQueryBuilder.java b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanFacilityQueryBuilder.java new file mode 100644 index 00000000000..0d475c4e577 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanFacilityQueryBuilder.java @@ -0,0 +1,154 @@ +package digit.repository.querybuilder; + +import digit.config.Configuration; +import digit.util.QueryUtil; +import digit.web.models.PlanFacilitySearchCriteria; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static digit.config.ServiceConstants.PERCENTAGE_WILDCARD; + +@Component +public class PlanFacilityQueryBuilder { + + private Configuration config; + + private QueryUtil queryUtil; + + public PlanFacilityQueryBuilder(Configuration config, QueryUtil queryUtil) { + this.config = config; + this.queryUtil = queryUtil; + } + + private static final String PLAN_FACILITY_QUERY = + "SELECT plan_facility_linkage.id as plan_facility_id, " + + "plan_facility_linkage.tenant_id as plan_facility_tenant_id, " + + "plan_facility_linkage.plan_configuration_id as plan_facility_plan_configuration_id, " + + "plan_facility_linkage.plan_configuration_name as plan_facility_plan_configuration_name, " + + "plan_facility_linkage.facility_id as plan_facility_facility_id, " + + "plan_facility_linkage.facility_name as plan_facility_facility_name, " + + "plan_facility_linkage.boundary_ancestral_path as plan_facility_boundary_ancestral_path, " + + "plan_facility_linkage.residing_boundary as plan_facility_residing_boundary, " + + "plan_facility_linkage.service_boundaries as plan_facility_service_boundaries, " + + "plan_facility_linkage.additional_details as plan_facility_additional_details, " + + "plan_facility_linkage.created_by as plan_facility_created_by, " + + "plan_facility_linkage.created_time as plan_facility_created_time, " + + "plan_facility_linkage.last_modified_by as plan_facility_last_modified_by, " + + "plan_facility_linkage.last_modified_time as plan_facility_last_modified_time, " + + "plan_facility_linkage.active as plan_facility_active " + + "FROM plan_facility_linkage"; + + private static final String PLAN_FACILITY_SEARCH_QUERY_ORDER_BY_CLAUSE = " order by plan_facility_linkage.last_modified_time desc "; + + private static final String PLAN_FACILITY_SEARCH_QUERY_COUNT_WRAPPER = "SELECT COUNT(*) AS total_count FROM ( "; + + public String getPlanFacilitySearchQuery(PlanFacilitySearchCriteria planFacilitySearchCriteria, List preparedStmtList) { + String query = buildPlanFacilitySearchQuery(planFacilitySearchCriteria, preparedStmtList, Boolean.FALSE); + query = queryUtil.addOrderByClause(query, PLAN_FACILITY_SEARCH_QUERY_ORDER_BY_CLAUSE); + query = getPaginatedQuery(query, planFacilitySearchCriteria, preparedStmtList); + return query; + } + + /** + * Constructs the count query to get the total count of plan facilities based on search criteria. + * + * @param planFacilitySearchCriteria The criteria used for filtering PlanFacility objects. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A SQL query string to get the total count of plan facilities. + */ + public String getPlanFacilityCountQuery(PlanFacilitySearchCriteria planFacilitySearchCriteria, List preparedStmtList) { + String query = buildPlanFacilitySearchQuery(planFacilitySearchCriteria, preparedStmtList, Boolean.TRUE); + return query; + } + + private String buildPlanFacilitySearchQuery(PlanFacilitySearchCriteria planFacilitySearchCriteria, List preparedStmtList, boolean isCount) { + StringBuilder builder = new StringBuilder(PLAN_FACILITY_QUERY); + + if (!CollectionUtils.isEmpty(planFacilitySearchCriteria.getIds())) { + Set ids = planFacilitySearchCriteria.getIds(); + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" id IN ( ").append(queryUtil.createQuery(ids.size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, ids); + } + + if (!ObjectUtils.isEmpty(planFacilitySearchCriteria.getTenantId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" tenant_id = ? "); + preparedStmtList.add(planFacilitySearchCriteria.getTenantId()); + } + + if (!ObjectUtils.isEmpty(planFacilitySearchCriteria.getPlanConfigurationId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" plan_configuration_id = ? "); + preparedStmtList.add(planFacilitySearchCriteria.getPlanConfigurationId()); + } + + if (!ObjectUtils.isEmpty(planFacilitySearchCriteria.getPlanConfigurationName())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" plan_configuration_name ILIKE ? "); + preparedStmtList.add(PERCENTAGE_WILDCARD + planFacilitySearchCriteria.getPlanConfigurationName() + PERCENTAGE_WILDCARD); + } + + if (!ObjectUtils.isEmpty(planFacilitySearchCriteria.getFacilityId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" facility_id = ? "); + preparedStmtList.add(planFacilitySearchCriteria.getFacilityId()); + } + + if (!ObjectUtils.isEmpty(planFacilitySearchCriteria.getFacilityName())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" facility_name ILIKE ? "); + preparedStmtList.add(PERCENTAGE_WILDCARD + planFacilitySearchCriteria.getFacilityName() + PERCENTAGE_WILDCARD); + } + + if (!ObjectUtils.isEmpty(planFacilitySearchCriteria.getResidingBoundaries())) { + List residingBoundaries = planFacilitySearchCriteria.getResidingBoundaries(); + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" residing_boundary IN ( ").append(queryUtil.createQuery(residingBoundaries.size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, residingBoundaries); + } + + if (!CollectionUtils.isEmpty(planFacilitySearchCriteria.getJurisdiction())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" ARRAY [ ").append(queryUtil.createQuery(planFacilitySearchCriteria.getJurisdiction().size())).append(" ]::text[] "); + builder.append(" && string_to_array(boundary_ancestral_path, '|') "); + queryUtil.addToPreparedStatement(preparedStmtList, planFacilitySearchCriteria.getJurisdiction()); + } + + if(!CollectionUtils.isEmpty(planFacilitySearchCriteria.getFiltersMap())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" additional_details @> CAST( ? AS jsonb )"); + String partialQueryJsonString = queryUtil.preparePartialJsonStringFromFilterMap(planFacilitySearchCriteria.getFiltersMap()); + preparedStmtList.add(partialQueryJsonString); + } + + StringBuilder countQuery = new StringBuilder(); + if (isCount) { + countQuery.append(PLAN_FACILITY_SEARCH_QUERY_COUNT_WRAPPER).append(builder); + countQuery.append(") AS subquery"); + + return countQuery.toString(); + } + + return builder.toString(); + } + + private String getPaginatedQuery(String query, PlanFacilitySearchCriteria planFacilitySearchCriteria, List preparedStmtList) { + StringBuilder paginatedQuery = new StringBuilder(query); + + // Append offset + paginatedQuery.append(" OFFSET ? "); + preparedStmtList.add(ObjectUtils.isEmpty(planFacilitySearchCriteria.getOffset()) ? config.getDefaultOffset() : planFacilitySearchCriteria.getOffset()); + + // Append limit + paginatedQuery.append(" LIMIT ? "); + preparedStmtList.add(ObjectUtils.isEmpty(planFacilitySearchCriteria.getLimit()) ? config.getDefaultLimit() : planFacilitySearchCriteria.getLimit()); + + return paginatedQuery.toString(); + } + +} diff --git a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java index 1b0adb59213..5c8bc91097b 100644 --- a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java +++ b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java @@ -6,6 +6,9 @@ import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; + +import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -14,13 +17,16 @@ public class PlanQueryBuilder { private Configuration config; - public PlanQueryBuilder(Configuration config) { + private QueryUtil queryUtil; + + public PlanQueryBuilder(Configuration config, QueryUtil queryUtil) { this.config = config; + this.queryUtil = queryUtil; } private static final String PLAN_SEARCH_BASE_QUERY = "SELECT id FROM plan "; - private static final String PLAN_QUERY = "SELECT plan.id as plan_id, plan.tenant_id as plan_tenant_id, plan.locality as plan_locality, plan.execution_plan_id as plan_execution_plan_id, plan.plan_configuration_id as plan_plan_configuration_id, plan.additional_details as plan_additional_details, plan.created_by as plan_created_by, plan.created_time as plan_created_time, plan.last_modified_by as plan_last_modified_by, plan.last_modified_time as plan_last_modified_time,\n" + + private static final String PLAN_QUERY = "SELECT plan.id as plan_id, plan.tenant_id as plan_tenant_id, plan.locality as plan_locality, plan.campaign_id as plan_campaign_id, plan.plan_configuration_id as plan_plan_configuration_id, plan.boundary_ancestral_path as plan_boundary_ancestral_path, plan.status as plan_status, plan.assignee as plan_assignee, plan.additional_details as plan_additional_details, plan.created_by as plan_created_by, plan.created_time as plan_created_time, plan.last_modified_by as plan_last_modified_by, plan.last_modified_time as plan_last_modified_time,\n" + "\t plan_activity.id as plan_activity_id, plan_activity.code as plan_activity_code, plan_activity.description as plan_activity_description, plan_activity.planned_start_date as plan_activity_planned_start_date, plan_activity.planned_end_date as plan_activity_planned_end_date, plan_activity.dependencies as plan_activity_dependencies, plan_activity.plan_id as plan_activity_plan_id, plan_activity.created_by as plan_activity_created_by, plan_activity.created_time as plan_activity_created_time, plan_activity.last_modified_by as plan_activity_last_modified_by, plan_activity.last_modified_time as plan_activity_last_modified_time,\n" + "\t plan_activity_condition.id as plan_activity_condition_id, plan_activity_condition.entity as plan_activity_condition_entity, plan_activity_condition.entity_property as plan_activity_condition_entity_property, plan_activity_condition.expression as plan_activity_condition_expression, plan_activity_condition.activity_id as plan_activity_condition_activity_id, plan_activity_condition.is_active as plan_activity_condition_is_active, plan_activity_condition.created_by as plan_activity_condition_created_by, plan_activity_condition.created_time as plan_activity_condition_created_time, plan_activity_condition.last_modified_by as plan_activity_condition_last_modified_by, plan_activity_condition.last_modified_time as plan_activity_condition_last_modified_time,\n" + "\t plan_resource.id as plan_resource_id, plan_resource.resource_type as plan_resource_resource_type, plan_resource.estimated_number as plan_resource_estimated_number, plan_resource.plan_id as plan_resource_plan_id, plan_resource.activity_code as plan_resource_activity_code, plan_resource.created_by as plan_resource_created_by, plan_resource.created_time as plan_resource_created_time, plan_resource.last_modified_by as plan_resource_last_modified_by, plan_resource.last_modified_time as plan_resource_last_modified_time,\n" + @@ -31,8 +37,14 @@ public PlanQueryBuilder(Configuration config) { "\t LEFT JOIN plan_resource ON plan.id = plan_resource.plan_id\n" + "\t LEFT JOIN plan_target ON plan.id = plan_target.plan_id"; + private static final String BULK_PLAN_UPDATE_QUERY = "UPDATE plan SET status = ?, assignee = ?, last_modified_by = ?, last_modified_time = ? WHERE id = ?"; + private static final String PLAN_SEARCH_QUERY_ORDER_BY_CLAUSE = " order by plan.last_modified_time desc "; + private static final String PLAN_SEARCH_QUERY_COUNT_WRAPPER = "SELECT COUNT(id) AS total_count FROM ( "; + + private static final String PLAN_STATUS_COUNT_QUERY = "SELECT COUNT(id) as plan_status_count, status FROM (SELECT id, status FROM plan {INTERNAL_QUERY}) as plan_status_map GROUP BY status"; + public String getPlanQuery(List ids, List preparedStmtList) { return buildPlanQuery(ids, preparedStmtList); } @@ -41,60 +53,137 @@ private String buildPlanQuery(List ids, List preparedStmtList) { StringBuilder builder = new StringBuilder(PLAN_QUERY); if (!CollectionUtils.isEmpty(ids)) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); - builder.append(" plan.id IN ( ").append(QueryUtil.createQuery(ids.size())).append(" )"); - QueryUtil.addToPreparedStatement(preparedStmtList, new LinkedHashSet<>(ids)); + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" plan.id IN ( ").append(queryUtil.createQuery(ids.size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, ids); } - return builder.toString(); + return queryUtil.addOrderByClause(builder.toString(), PLAN_SEARCH_QUERY_ORDER_BY_CLAUSE); } public String getPlanSearchQuery(PlanSearchCriteria planSearchCriteria, List preparedStmtList) { - String query = buildPlanSearchQuery(planSearchCriteria, preparedStmtList); - query = QueryUtil.addOrderByClause(query, PLAN_SEARCH_QUERY_ORDER_BY_CLAUSE); + String query = buildPlanSearchQuery(planSearchCriteria, preparedStmtList, Boolean.FALSE, Boolean.FALSE); + query = queryUtil.addOrderByClause(query, PLAN_SEARCH_QUERY_ORDER_BY_CLAUSE); query = getPaginatedQuery(query, planSearchCriteria, preparedStmtList); return query; } + /** + * Method to build a query to get the toatl count of plans based on the given search criteria + * + * @param criteria + * @param preparedStmtList + * @return + */ + public String getPlanCountQuery(PlanSearchCriteria criteria, List preparedStmtList) { + String query = buildPlanSearchQuery(criteria, preparedStmtList, Boolean.TRUE, Boolean.FALSE); + return query; + } + + /** + * Constructs the status count query to get the count of plans based on their current status for the given search criteria + * + * @param searchCriteria The criteria used for filtering Plans. + * @param preparedStmtList A list to store prepared statement parameters. + * @return A SQL query string to get the status count of Plans for a given search criteria. + */ + public String getPlanStatusCountQuery(PlanSearchCriteria searchCriteria, List preparedStmtList) { + PlanSearchCriteria planSearchCriteria = PlanSearchCriteria.builder() + .tenantId(searchCriteria.getTenantId()) + .planConfigurationId(searchCriteria.getPlanConfigurationId()) + .campaignId(searchCriteria.getCampaignId()) + .jurisdiction(searchCriteria.getJurisdiction()) + .build(); + return buildPlanSearchQuery(planSearchCriteria, preparedStmtList, Boolean.FALSE, Boolean.TRUE); + } + /** * Method to build query dynamically based on the criteria passed to the method + * * @param planSearchCriteria * @param preparedStmtList * @return */ - private String buildPlanSearchQuery(PlanSearchCriteria planSearchCriteria, List preparedStmtList) { + private String buildPlanSearchQuery(PlanSearchCriteria planSearchCriteria, List preparedStmtList, boolean isCount, boolean isStatusCount) { StringBuilder builder = new StringBuilder(PLAN_SEARCH_BASE_QUERY); + if(isStatusCount) { + builder = new StringBuilder(); + } + if (!ObjectUtils.isEmpty(planSearchCriteria.getTenantId())) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); + queryUtil.addClauseIfRequired(builder, preparedStmtList); builder.append(" tenant_id = ? "); preparedStmtList.add(planSearchCriteria.getTenantId()); } if (!CollectionUtils.isEmpty(planSearchCriteria.getIds())) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); - builder.append(" id IN ( ").append(QueryUtil.createQuery(planSearchCriteria.getIds().size())).append(" )"); - QueryUtil.addToPreparedStatement(preparedStmtList, planSearchCriteria.getIds()); + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" id IN ( ").append(queryUtil.createQuery(planSearchCriteria.getIds().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, planSearchCriteria.getIds()); } - if (!ObjectUtils.isEmpty(planSearchCriteria.getLocality())) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); - builder.append(" locality = ? "); - preparedStmtList.add(planSearchCriteria.getLocality()); + if (!CollectionUtils.isEmpty(planSearchCriteria.getLocality())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" locality IN ( ").append(queryUtil.createQuery(planSearchCriteria.getLocality().size())).append(" )"); + queryUtil.addToPreparedStatement(preparedStmtList, planSearchCriteria.getLocality()); } - if (!ObjectUtils.isEmpty(planSearchCriteria.getExecutionPlanId())) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); - builder.append(" execution_plan_id = ? "); - preparedStmtList.add(planSearchCriteria.getExecutionPlanId()); + if (!ObjectUtils.isEmpty(planSearchCriteria.getCampaignId())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" campaign_id = ? "); + preparedStmtList.add(planSearchCriteria.getCampaignId()); } if (!ObjectUtils.isEmpty(planSearchCriteria.getPlanConfigurationId())) { - QueryUtil.addClauseIfRequired(builder, preparedStmtList); + queryUtil.addClauseIfRequired(builder, preparedStmtList); builder.append(" plan_configuration_id = ? "); preparedStmtList.add(planSearchCriteria.getPlanConfigurationId()); } + if (!ObjectUtils.isEmpty(planSearchCriteria.getStatus())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" status = ? "); + preparedStmtList.add(planSearchCriteria.getStatus()); + } + + if (!ObjectUtils.isEmpty(planSearchCriteria.getAssignee())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" assignee = ? "); + preparedStmtList.add(planSearchCriteria.getAssignee()); + } + + if (!ObjectUtils.isEmpty(planSearchCriteria.getAssignee())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" ARRAY [ ").append(queryUtil.createQuery(Collections.singleton(planSearchCriteria.getAssignee()).size())).append(" ]").append("::text[] "); + builder.append(" && string_to_array(assignee, ',') "); + queryUtil.addToPreparedStatement(preparedStmtList, Collections.singleton(planSearchCriteria.getAssignee())); + } + + if (!CollectionUtils.isEmpty(planSearchCriteria.getJurisdiction())) { + queryUtil.addClauseIfRequired(builder, preparedStmtList); + builder.append(" ARRAY [ ") + .append(queryUtil.createQuery(planSearchCriteria.getJurisdiction().size())) + .append(" ]::text[] "); + + builder.append(" && string_to_array(boundary_ancestral_path, '|') "); + queryUtil.addToPreparedStatement(preparedStmtList, planSearchCriteria.getJurisdiction()); + } + + + StringBuilder countQuery = new StringBuilder(); + if (isCount) { + + countQuery.append(PLAN_SEARCH_QUERY_COUNT_WRAPPER).append(builder); + countQuery.append(") AS subquery"); + + return countQuery.toString(); + } + + if (isStatusCount) { + return PLAN_STATUS_COUNT_QUERY.replace("{INTERNAL_QUERY}", builder); + } + return builder.toString(); } @@ -112,4 +201,7 @@ private String getPaginatedQuery(String query, PlanSearchCriteria planSearchCrit return paginatedQuery.toString(); } + public String getBulkPlanQuery() { + return BULK_PLAN_UPDATE_QUERY; + } } diff --git a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanConfigRowMapper.java b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanConfigRowMapper.java index f8aebd12a9a..4645ce10b5e 100644 --- a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanConfigRowMapper.java +++ b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanConfigRowMapper.java @@ -1,33 +1,35 @@ package digit.repository.rowmapper; -import digit.web.models.Assumption; -import digit.web.models.File; -import digit.web.models.Operation; -import digit.web.models.PlanConfiguration; -import digit.web.models.ResourceMapping; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import digit.util.QueryUtil; +import digit.web.models.*; import org.egov.common.contract.models.AuditDetails; +import org.postgresql.util.PGobject; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + @Component public class PlanConfigRowMapper implements ResultSetExtractor> { + private QueryUtil queryUtil; + + public PlanConfigRowMapper(QueryUtil queryUtil) { + this.queryUtil = queryUtil; + } + @Override public List extractData(ResultSet rs) throws SQLException, DataAccessException { Map planConfigurationMap = new LinkedHashMap<>(); - Map fileMap = new LinkedHashMap<>(); - Map operationMap = new LinkedHashMap<>(); - Map assumptionMap = new LinkedHashMap<>(); - Map resourceMappingMap = new LinkedHashMap<>(); + Set fileSet = new HashSet<>(); + Set operationSet = new HashSet<>(); + Set assumptionSet = new HashSet<>(); + Set resourceMappingSet = new HashSet<>(); while (rs.next()) { @@ -37,6 +39,10 @@ public List extractData(ResultSet rs) throws SQLException, Da if (ObjectUtils.isEmpty(planConfigEntry)) { planConfigEntry = new PlanConfiguration(); + fileSet.clear(); + operationSet.clear(); + assumptionSet.clear(); + resourceMappingSet.clear(); // Prepare audit details AuditDetails auditDetails = AuditDetails.builder().createdBy(rs.getString("plan_configuration_created_by")).createdTime(rs.getLong("plan_configuration_created_time")).lastModifiedBy(rs.getString("plan_configuration_last_modified_by")).lastModifiedTime(rs.getLong("plan_configuration_last_modified_time")).build(); @@ -45,15 +51,16 @@ public List extractData(ResultSet rs) throws SQLException, Da planConfigEntry.setId(planConfigId); planConfigEntry.setTenantId(rs.getString("plan_configuration_tenant_id")); planConfigEntry.setName(rs.getString("plan_configuration_name")); - planConfigEntry.setExecutionPlanId(rs.getString("plan_configuration_execution_plan_id")); - planConfigEntry.setStatus(PlanConfiguration.StatusEnum.valueOf(rs.getString("plan_configuration_status").toUpperCase())); + planConfigEntry.setCampaignId(rs.getString("plan_configuration_campaign_id")); + planConfigEntry.setStatus(rs.getString("plan_configuration_status")); + planConfigEntry.setAdditionalDetails(queryUtil.getAdditionalDetail((PGobject) rs.getObject("plan_configuration_additional_details"))); planConfigEntry.setAuditDetails(auditDetails); } - addFiles(rs, planConfigEntry, fileMap); - addAssumptions(rs, planConfigEntry, assumptionMap); - addOperations(rs, planConfigEntry, operationMap); - addResourceMappings(rs, planConfigEntry, resourceMappingMap); + addFiles(rs, planConfigEntry, fileSet); + addAssumptions(rs, planConfigEntry, assumptionSet); + addOperations(rs, planConfigEntry, operationSet); + addResourceMappings(rs, planConfigEntry, resourceMappingSet); planConfigurationMap.put(planConfigId, planConfigEntry); } @@ -65,13 +72,13 @@ public List extractData(ResultSet rs) throws SQLException, Da * * @param rs The ResultSet containing the data. * @param planConfigEntry The PlanConfiguration entry to which the File object will be added. - * @param fileMap A map to keep track of added File objects. + * @param fileSet A set to keep track of added File objects. * @throws SQLException If an SQL error occurs. */ - private void addFiles(ResultSet rs, PlanConfiguration planConfigEntry, Map fileMap) throws SQLException { + private void addFiles(ResultSet rs, PlanConfiguration planConfigEntry, Set fileSet) throws SQLException { String fileId = rs.getString("plan_configuration_files_id"); - if (ObjectUtils.isEmpty(fileId) || fileMap.containsKey(fileId)) { + if (ObjectUtils.isEmpty(fileId) || fileSet.contains(fileId)) { return; } @@ -89,7 +96,7 @@ private void addFiles(ResultSet rs, PlanConfiguration planConfigEntry, Map assumptionMap) throws SQLException { + private void addAssumptions(ResultSet rs, PlanConfiguration planConfigEntry, Set assumptionSet) throws SQLException { String assumptionId = rs.getString("plan_configuration_assumptions_id"); - if (ObjectUtils.isEmpty(assumptionId) || assumptionMap.containsKey(assumptionId)) { + if (ObjectUtils.isEmpty(assumptionId) || assumptionSet.contains(assumptionId)) { return; } @@ -113,6 +120,8 @@ private void addAssumptions(ResultSet rs, PlanConfiguration planConfigEntry, Map assumption.setKey(rs.getString("plan_configuration_assumptions_key")); assumption.setValue(rs.getBigDecimal("plan_configuration_assumptions_value")); assumption.setActive(rs.getBoolean("plan_configuration_assumptions_active")); + assumption.setSource(Source.valueOf(rs.getString("plan_configuration_assumptions_source"))); + assumption.setCategory(rs.getString("plan_configuration_assumptions_category")); if (CollectionUtils.isEmpty(planConfigEntry.getAssumptions())) { List assumptionList = new ArrayList<>(); @@ -122,7 +131,7 @@ private void addAssumptions(ResultSet rs, PlanConfiguration planConfigEntry, Map planConfigEntry.getAssumptions().add(assumption); } - assumptionMap.put(assumptionId, assumption); + assumptionSet.add(assumptionId); } /** @@ -130,13 +139,13 @@ private void addAssumptions(ResultSet rs, PlanConfiguration planConfigEntry, Map * * @param rs The ResultSet containing the data. * @param planConfigEntry The PlanConfiguration entry to which the Operation object will be added. - * @param operationMap A map to keep track of added Operation objects. + * @param operationSet A set to keep track of added Operation objects. * @throws SQLException If an SQL error occurs. */ - private void addOperations(ResultSet rs, PlanConfiguration planConfigEntry, Map operationMap) throws SQLException { + private void addOperations(ResultSet rs, PlanConfiguration planConfigEntry, Set operationSet) throws SQLException { String operationId = rs.getString("plan_configuration_operations_id"); - if (ObjectUtils.isEmpty(operationId) || operationMap.containsKey(operationId)) { + if (ObjectUtils.isEmpty(operationId) || operationSet.contains(operationId)) { return; } @@ -147,6 +156,10 @@ private void addOperations(ResultSet rs, PlanConfiguration planConfigEntry, Map< operation.setAssumptionValue(rs.getString("plan_configuration_operations_assumption_value")); operation.setOutput(rs.getString("plan_configuration_operations_output")); operation.setActive(rs.getBoolean("plan_configuration_operations_active")); + operation.setShowOnEstimationDashboard(rs.getBoolean("plan_configuration_operations_show_on_estimation_dashboard")); + operation.setSource(Source.valueOf(rs.getString("plan_configuration_operations_source"))); + operation.setCategory(rs.getString("plan_configuration_operations_category")); + operation.setExecutionOrder(rs.getInt("plan_configuration_execution_order")); if (CollectionUtils.isEmpty(planConfigEntry.getOperations())) { List operationList = new ArrayList<>(); @@ -156,7 +169,7 @@ private void addOperations(ResultSet rs, PlanConfiguration planConfigEntry, Map< planConfigEntry.getOperations().add(operation); } - operationMap.put(operationId, operation); + operationSet.add(operationId); } /** @@ -164,13 +177,13 @@ private void addOperations(ResultSet rs, PlanConfiguration planConfigEntry, Map< * * @param rs The ResultSet containing the data. * @param planConfigEntry The PlanConfiguration entry to which the ResourceMapping object will be added. - * @param mappingMap A map to keep track of added ResourceMapping objects. + * @param resourceMappingSet A set to keep track of added ResourceMapping objects. * @throws SQLException If an SQL error occurs. */ - private void addResourceMappings(ResultSet rs, PlanConfiguration planConfigEntry, Map mappingMap) throws SQLException { + private void addResourceMappings(ResultSet rs, PlanConfiguration planConfigEntry, Set resourceMappingSet) throws SQLException { String mappingId = rs.getString("plan_configuration_mapping_id"); - if (ObjectUtils.isEmpty(mappingId) || mappingMap.containsKey(mappingId)) { + if (ObjectUtils.isEmpty(mappingId) || resourceMappingSet.contains(mappingId)) { return; } @@ -189,7 +202,7 @@ private void addResourceMappings(ResultSet rs, PlanConfiguration planConfigEntry planConfigEntry.getResourceMapping().add(mapping); } - mappingMap.put(mappingId, mapping); + resourceMappingSet.add(mappingId); } } diff --git a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanEmployeeAssignmentRowMapper.java b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanEmployeeAssignmentRowMapper.java new file mode 100644 index 00000000000..edf79b72c02 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanEmployeeAssignmentRowMapper.java @@ -0,0 +1,66 @@ +package digit.repository.rowmapper; + +import digit.util.QueryUtil; +import digit.web.models.PlanEmployeeAssignment; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.models.AuditDetails; +import org.postgresql.util.PGobject; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class PlanEmployeeAssignmentRowMapper implements ResultSetExtractor> { + + private QueryUtil queryUtil; + + public PlanEmployeeAssignmentRowMapper(QueryUtil queryUtil) { + this.queryUtil = queryUtil; + } + + @Override + public List extractData(ResultSet rs) throws SQLException, DataAccessException { + Map planEmployeeAssignmentMap = new LinkedHashMap<>(); + + while (rs.next()) { + String planEmployeeAssignmentId = rs.getString("id"); + + PlanEmployeeAssignment planEmployeeAssignment = planEmployeeAssignmentMap.get(planEmployeeAssignmentId); + + if (ObjectUtils.isEmpty(planEmployeeAssignment)) { + planEmployeeAssignment = new PlanEmployeeAssignment(); + + // Prepare audit details + AuditDetails auditDetails = AuditDetails.builder().createdBy(rs.getString("created_by")).createdTime(rs.getLong("created_time")).lastModifiedBy(rs.getString("last_modified_by")).lastModifiedTime(rs.getLong("last_modified_time")).build(); + + // Converting jurisdiction from comma separated string to a list of string + String jurisdiction = rs.getString("jurisdiction"); + Set jurisdictionList = Arrays.stream(jurisdiction.split(",")).collect(Collectors.toSet()); + + // Prepare PlanEmployeeAssignment object + planEmployeeAssignment.setId(planEmployeeAssignmentId); + planEmployeeAssignment.setTenantId(rs.getString("tenant_id")); + planEmployeeAssignment.setPlanConfigurationId(rs.getString("plan_configuration_id")); + planEmployeeAssignment.setPlanConfigurationName(rs.getString("plan_configuration_name")); + planEmployeeAssignment.setEmployeeId(rs.getString("employee_id")); + planEmployeeAssignment.setRole(rs.getString("role")); + planEmployeeAssignment.setHierarchyLevel(rs.getString("hierarchy_level")); + planEmployeeAssignment.setJurisdiction(jurisdictionList); + planEmployeeAssignment.setActive(rs.getBoolean("active")); + planEmployeeAssignment.setAdditionalDetails(queryUtil.getAdditionalDetail((PGobject) rs.getObject("additional_details"))); + planEmployeeAssignment.setAuditDetails(auditDetails); + + planEmployeeAssignmentMap.put(planEmployeeAssignmentId, planEmployeeAssignment); + } + } + + return new ArrayList<>(planEmployeeAssignmentMap.values()); + } +} diff --git a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanFacilityRowMapper.java b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanFacilityRowMapper.java new file mode 100644 index 00000000000..c7ea2667ef1 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanFacilityRowMapper.java @@ -0,0 +1,79 @@ +package digit.repository.rowmapper; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.web.models.PlanFacility; +import org.egov.common.contract.models.AuditDetails; +import org.egov.tracer.model.CustomException; +import org.postgresql.util.PGobject; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +@Component +public class PlanFacilityRowMapper implements ResultSetExtractor> { + + private final ObjectMapper objectMapper; + + public PlanFacilityRowMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public List extractData(ResultSet rs) throws SQLException, DataAccessException { + Map planFacilityMap = new LinkedHashMap<>(); + while (rs.next()) { + String planFacilityId = rs.getString("plan_facility_id"); + + PlanFacility planFacilityEntry = planFacilityMap.get(planFacilityId); + if (planFacilityEntry == null || ObjectUtils.isEmpty(planFacilityEntry)) { + planFacilityEntry = new PlanFacility(); + + // Prepare audit details + AuditDetails auditDetails = AuditDetails.builder() + .createdBy(rs.getString("plan_facility_created_by")) + .createdTime(rs.getLong("plan_facility_created_time")) + .lastModifiedBy(rs.getString("plan_facility_last_modified_by")) + .lastModifiedTime(rs.getLong("plan_facility_last_modified_time")) + .build(); + + // Prepare plan facility object + planFacilityEntry.setId(planFacilityId); + planFacilityEntry.setTenantId(rs.getString("plan_facility_tenant_id")); + planFacilityEntry.setPlanConfigurationId(rs.getString("plan_facility_plan_configuration_id")); + planFacilityEntry.setPlanConfigurationName(rs.getString("plan_facility_plan_configuration_name")); + planFacilityEntry.setFacilityId(rs.getString("plan_facility_facility_id")); + planFacilityEntry.setFacilityName(rs.getString("plan_facility_facility_name")); + planFacilityEntry.setResidingBoundary(rs.getString("plan_facility_residing_boundary")); + planFacilityEntry.setBoundaryAncestralPath(rs.getString("plan_facility_boundary_ancestral_path")); + String serviceBoundaries = rs.getString("plan_facility_service_boundaries"); + planFacilityEntry.setServiceBoundaries(ObjectUtils.isEmpty(serviceBoundaries) ? new ArrayList<>() : Arrays.asList(serviceBoundaries.split(","))); + planFacilityEntry.setAdditionalDetails(getAdditionalDetail((PGobject) rs.getObject("plan_facility_additional_details"))); + planFacilityEntry.setAuditDetails(auditDetails); + planFacilityEntry.setActive(rs.getBoolean("plan_facility_active")); + } + + planFacilityMap.put(planFacilityId, planFacilityEntry); + } + return new ArrayList<>(planFacilityMap.values()); + } + + private JsonNode getAdditionalDetail(PGobject pGobject) { + JsonNode additionalDetail = null; + + try { + if (!ObjectUtils.isEmpty(pGobject)) { + additionalDetail = objectMapper.readTree(pGobject.getValue()); + } + } catch (IOException e) { + throw new CustomException("PARSING_ERROR", "Failed to parse additionalDetails object"); + } + + return additionalDetail; + } +} diff --git a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java index a82919fed46..aa00207a741 100644 --- a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java +++ b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java @@ -1,10 +1,8 @@ package digit.repository.rowmapper; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import digit.util.QueryUtil; import digit.web.models.*; import org.egov.common.contract.models.AuditDetails; -import org.egov.tracer.model.CustomException; import org.postgresql.util.PGobject; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; @@ -12,7 +10,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; -import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; @@ -20,28 +17,32 @@ @Component public class PlanRowMapper implements ResultSetExtractor> { - private ObjectMapper objectMapper; + private QueryUtil queryUtil; - public PlanRowMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; + public PlanRowMapper(QueryUtil queryUtil) { + this.queryUtil = queryUtil; } @Override public List extractData(ResultSet rs) throws SQLException, DataAccessException { Map planMap = new LinkedHashMap<>(); Map activityMap = new LinkedHashMap<>(); - Map conditionMap = new LinkedHashMap<>(); - Map resourceMap = new LinkedHashMap<>(); - Map targetMap = new LinkedHashMap<>(); + Set conditionSet = new HashSet<>(); + Set resourceSet = new HashSet<>(); + Set targetSet = new HashSet<>(); // Traverse through result set and create plan objects - while(rs.next()) { + while (rs.next()) { String planId = rs.getString("plan_id"); Plan planEntry = planMap.get(planId); - if(ObjectUtils.isEmpty(planEntry)) { + if (ObjectUtils.isEmpty(planEntry)) { planEntry = new Plan(); + activityMap.clear(); + conditionSet.clear(); + resourceSet.clear(); + targetSet.clear(); // Prepare audit details AuditDetails auditDetails = AuditDetails.builder() @@ -51,20 +52,26 @@ public List extractData(ResultSet rs) throws SQLException, DataAccessExcep .lastModifiedTime(rs.getLong("plan_last_modified_time")) .build(); + String commaSeparatedAssignee = rs.getString("plan_assignee"); + List assignee = !ObjectUtils.isEmpty(commaSeparatedAssignee) ? Arrays.asList(commaSeparatedAssignee.split(",")) : null; + // Prepare plan object planEntry.setId(planId); planEntry.setTenantId(rs.getString("plan_tenant_id")); planEntry.setLocality(rs.getString("plan_locality")); - planEntry.setExecutionPlanId(rs.getString("plan_execution_plan_id")); + planEntry.setCampaignId(rs.getString("plan_campaign_id")); + planEntry.setStatus(rs.getString("plan_status")); + planEntry.setAssignee(assignee); planEntry.setPlanConfigurationId(rs.getString("plan_plan_configuration_id")); - planEntry.setAdditionalDetails(getAdditionalDetail((PGobject) rs.getObject("plan_additional_details"))); + planEntry.setBoundaryAncestralPath(rs.getString("plan_boundary_ancestral_path")); + planEntry.setAdditionalDetails(queryUtil.getAdditionalDetail((PGobject) rs.getObject("plan_additional_details"))); planEntry.setAuditDetails(auditDetails); } - addActivities(rs, planEntry, activityMap, conditionMap); - addResources(rs, planEntry, resourceMap); - addTargets(rs, planEntry, targetMap); + addActivities(rs, planEntry, activityMap, conditionSet); + addResources(rs, planEntry, resourceSet); + addTargets(rs, planEntry, targetSet); planMap.put(planId, planEntry); } @@ -72,15 +79,14 @@ public List extractData(ResultSet rs) throws SQLException, DataAccessExcep } private void addActivities(ResultSet rs, Plan plan, - Map activityMap, Map conditionMap) throws SQLException, DataAccessException { + Map activityMap, Set conditionSet) throws SQLException, DataAccessException { String activityId = rs.getString("plan_activity_id"); - if(!ObjectUtils.isEmpty(activityId) && activityMap.containsKey(activityId)) { - addActivityConditions(rs, activityMap.get(activityId), conditionMap); + if (!ObjectUtils.isEmpty(activityId) && activityMap.containsKey(activityId)) { + addActivityConditions(rs, activityMap.get(activityId), conditionSet); return; - } - else if (ObjectUtils.isEmpty(activityId)) { + } else if (ObjectUtils.isEmpty(activityId)) { // Set activities list to empty if no activity found plan.setActivities(new ArrayList<>()); return; @@ -103,7 +109,7 @@ else if (ObjectUtils.isEmpty(activityId)) { .dependencies(ObjectUtils.isEmpty(dependencies) ? new ArrayList<>() : Arrays.asList(rs.getString("plan_activity_dependencies").split(","))) .build(); - addActivityConditions(rs, activity, conditionMap); + addActivityConditions(rs, activity, conditionSet); if (CollectionUtils.isEmpty(plan.getActivities())) { List activityList = new ArrayList<>(); @@ -117,10 +123,10 @@ else if (ObjectUtils.isEmpty(activityId)) { } - private void addActivityConditions(ResultSet rs, Activity activity, Map conditionMap) throws SQLException, DataAccessException { + private void addActivityConditions(ResultSet rs, Activity activity, Set conditionSet) throws SQLException, DataAccessException { String conditionId = rs.getString("plan_activity_condition_id"); - if(ObjectUtils.isEmpty(conditionId) || conditionMap.containsKey(conditionId)) { + if (ObjectUtils.isEmpty(conditionId) || conditionSet.contains(conditionId)) { List conditionList = new ArrayList<>(); activity.setConditions(conditionList); return; @@ -140,7 +146,7 @@ private void addActivityConditions(ResultSet rs, Activity activity, Map conditionList = new ArrayList<>(); conditionList.add(condition); activity.setConditions(conditionList); @@ -148,15 +154,15 @@ private void addActivityConditions(ResultSet rs, Activity activity, Map resourceMap) throws SQLException, DataAccessException { + private void addResources(ResultSet rs, Plan planEntry, Set resourceSet) throws SQLException, DataAccessException { String resourceId = rs.getString("plan_resource_id"); - if(ObjectUtils.isEmpty(resourceId) || resourceMap.containsKey(resourceId)) { + if (ObjectUtils.isEmpty(resourceId) || resourceSet.contains(resourceId)) { List resourceList = new ArrayList<>(); planEntry.setResources(resourceList); return; @@ -184,14 +190,14 @@ private void addResources(ResultSet rs, Plan planEntry, Map re planEntry.getResources().add(resource); } - resourceMap.put(resource.getId(), resource); + resourceSet.add(resource.getId()); } - private void addTargets(ResultSet rs, Plan planEntry, Map targetMap) throws SQLException, DataAccessException { + private void addTargets(ResultSet rs, Plan planEntry, Set targetSet) throws SQLException, DataAccessException { String targetId = rs.getString("plan_target_id"); - if(ObjectUtils.isEmpty(targetId) || targetMap.containsKey(targetId)) { + if (ObjectUtils.isEmpty(targetId) || targetSet.contains(targetId)) { List targetList = new ArrayList<>(); planEntry.setTargets(targetList); return; @@ -225,23 +231,7 @@ private void addTargets(ResultSet rs, Plan planEntry, Map target planEntry.getTargets().add(target); } - targetMap.put(target.getId(), target); - - } - - private JsonNode getAdditionalDetail(PGobject pGobject){ - JsonNode additionalDetail = null; + targetSet.add(target.getId()); - try { - if(ObjectUtils.isEmpty(pGobject)){ - additionalDetail = objectMapper.readTree(pGobject.getValue()); - } - } - catch (IOException e){ - throw new CustomException("PARSING_ERROR", "Failed to parse additionalDetails object"); - } - - return additionalDetail; } - } diff --git a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanStatusCountRowMapper.java b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanStatusCountRowMapper.java new file mode 100644 index 00000000000..11e14d37763 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanStatusCountRowMapper.java @@ -0,0 +1,31 @@ +package digit.repository.rowmapper; + +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class PlanStatusCountRowMapper implements ResultSetExtractor> { + + @Override + public Map extractData(ResultSet rs) throws SQLException, DataAccessException { + + Map statusCountMap = new HashMap<>(); + + while (rs.next()) { + String status = rs.getString("status"); + Integer statusCount = rs.getInt("plan_status_count"); + + if(!ObjectUtils.isEmpty(status)) + statusCountMap.put(status, statusCount); + } + + return statusCountMap; + } +} diff --git a/health-services/plan-service/src/main/java/digit/service/PlanConfigurationService.java b/health-services/plan-service/src/main/java/digit/service/PlanConfigurationService.java index 85238f723a5..c9602b1a543 100644 --- a/health-services/plan-service/src/main/java/digit/service/PlanConfigurationService.java +++ b/health-services/plan-service/src/main/java/digit/service/PlanConfigurationService.java @@ -1,48 +1,54 @@ package digit.service; -import digit.config.Configuration; -import digit.kafka.Producer; import digit.repository.PlanConfigurationRepository; -import digit.repository.impl.PlanConfigurationRepositoryImpl; import digit.service.enrichment.EnrichmentService; import digit.service.validator.PlanConfigurationValidator; +import digit.service.validator.WorkflowValidator; +import digit.service.workflow.WorkflowService; +import digit.util.CommonUtil; import digit.util.ResponseInfoFactory; +import digit.web.models.PlanConfiguration; import digit.web.models.PlanConfigurationRequest; import digit.web.models.PlanConfigurationResponse; import digit.web.models.PlanConfigurationSearchRequest; -import java.util.Collections; import lombok.extern.slf4j.Slf4j; import org.egov.common.utils.ResponseInfoUtil; import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.List; + @Service @Slf4j public class PlanConfigurationService { - private Producer producer; - private EnrichmentService enrichmentService; - private Configuration config; - private PlanConfigurationValidator validator; private PlanConfigurationRepository repository; private ResponseInfoFactory responseInfoFactory; - public PlanConfigurationService(Producer producer, EnrichmentService enrichmentService, Configuration config - , PlanConfigurationValidator validator, PlanConfigurationRepository repository, ResponseInfoFactory responseInfoFactory) { - this.producer = producer; + private WorkflowValidator workflowValidator; + + private WorkflowService workflowService; + + private CommonUtil commonUtil; + + public PlanConfigurationService(EnrichmentService enrichmentService, PlanConfigurationValidator validator, PlanConfigurationRepository repository, ResponseInfoFactory responseInfoFactory, WorkflowService workflowService, WorkflowValidator workflowValidator, CommonUtil commonUtil) { this.enrichmentService = enrichmentService; - this.config = config; this.validator = validator; this.repository = repository; this.responseInfoFactory = responseInfoFactory; + this.workflowService = workflowService; + this.workflowValidator = workflowValidator; + this.commonUtil = commonUtil; } /** * Creates a new plan configuration based on the provided request. + * * @param request The request containing the plan configuration details. * @return The created plan configuration request. */ @@ -51,41 +57,43 @@ public PlanConfigurationResponse create(PlanConfigurationRequest request) { validator.validateCreate(request); enrichmentService.enrichCreate(request); repository.create(request); - PlanConfigurationResponse response = PlanConfigurationResponse.builder() + + return PlanConfigurationResponse.builder() .planConfiguration(Collections.singletonList(request.getPlanConfiguration())) - .responseInfo(responseInfoFactory - .createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) .build(); - return response; } /** * Searches for plan configurations based on the provided search criteria. + * * @param request The search request containing the criteria. * @return A list of plan configurations that match the search criteria. */ public PlanConfigurationResponse search(PlanConfigurationSearchRequest request) { validator.validateSearchRequest(request); - PlanConfigurationResponse response = PlanConfigurationResponse.builder(). + List planConfigurations = repository.search(request.getPlanConfigurationSearchCriteria()); + commonUtil.sortOperationsByExecutionOrder(planConfigurations); + return PlanConfigurationResponse.builder(). responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), true)) - .planConfiguration(repository.search(request.getPlanConfigurationSearchCriteria())) + .planConfiguration(planConfigurations) .totalCount(repository.count(request.getPlanConfigurationSearchCriteria())) .build(); - - return response; } /** * Updates an existing plan configuration based on the provided request. + * * @param request The request containing the updated plan configuration details. * @return The response containing the updated plan configuration. */ public PlanConfigurationResponse update(PlanConfigurationRequest request) { validator.validateUpdateRequest(request); enrichmentService.enrichUpdate(request); + workflowValidator.validateWorkflow(request); + workflowService.invokeWorkflowForStatusUpdate(request); repository.update(request); - // Build and return response back to controller return PlanConfigurationResponse.builder() .responseInfo(ResponseInfoUtil.createResponseInfoFromRequestInfo(request.getRequestInfo(), Boolean.TRUE)) .planConfiguration(Collections.singletonList(request.getPlanConfiguration())) diff --git a/health-services/plan-service/src/main/java/digit/service/PlanEmployeeService.java b/health-services/plan-service/src/main/java/digit/service/PlanEmployeeService.java new file mode 100644 index 00000000000..afa9ee46f38 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/PlanEmployeeService.java @@ -0,0 +1,91 @@ +package digit.service; + +import digit.config.Configuration; +import digit.kafka.Producer; +import digit.repository.PlanEmployeeAssignmentRepository; +import digit.service.enrichment.PlanEmployeeAssignmentEnricher; +import digit.service.validator.PlanEmployeeAssignmentValidator; +import digit.util.ResponseInfoFactory; +import digit.web.models.*; +import org.egov.common.utils.ResponseInfoUtil; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Service +public class PlanEmployeeService { + + Producer producer; + + Configuration config; + + ResponseInfoFactory responseInfoFactory; + + PlanEmployeeAssignmentRepository repository; + + PlanEmployeeAssignmentEnricher enricher; + + PlanEmployeeAssignmentValidator validator; + + public PlanEmployeeService(Producer producer, Configuration config, ResponseInfoFactory responseInfoFactory, PlanEmployeeAssignmentRepository repository, PlanEmployeeAssignmentEnricher enricher, PlanEmployeeAssignmentValidator validator) { + this.producer = producer; + this.config = config; + this.responseInfoFactory = responseInfoFactory; + this.repository = repository; + this.enricher = enricher; + this.validator = validator; + } + + /** + * Creates a new plan employee assignment based on the provided request. + * + * @param request The request containing the plan employee assignment details. + * @return The response containing the created plan employee assignment. + */ + public PlanEmployeeAssignmentResponse create(PlanEmployeeAssignmentRequest request) { + validator.validateCreate(request); + enricher.enrichCreate(request); + repository.create(request); + + return PlanEmployeeAssignmentResponse.builder() + .planEmployeeAssignment(Collections.singletonList(request.getPlanEmployeeAssignment())) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), Boolean.TRUE)) + .build(); + } + + /** + * Searches for plan employee assignment based on the provided search criteria. + * + * @param request The search request containing the criteria. + * @return A list of plan employee assignments that matches the search criteria. + */ + public PlanEmployeeAssignmentResponse search(PlanEmployeeAssignmentSearchRequest request) { + // Delegate search request to repository + List planEmployeeAssignmentList = repository.search(request.getPlanEmployeeAssignmentSearchCriteria()); + + // Build and return response back to controller + return PlanEmployeeAssignmentResponse.builder() + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(request.getRequestInfo(), Boolean.TRUE)) + .planEmployeeAssignment(planEmployeeAssignmentList) + .totalCount(repository.count(request.getPlanEmployeeAssignmentSearchCriteria())) + .build(); + } + + /** + * Updates an existing plan employee assignment based on the provided request. + * + * @param request The request containing the updated plan employee assignment details. + * @return The response containing the updated plan employee assignment. + */ + public PlanEmployeeAssignmentResponse update(PlanEmployeeAssignmentRequest request) { + validator.validateUpdate(request); + enricher.enrichUpdate(request); + repository.update(request); + + return PlanEmployeeAssignmentResponse.builder() + .responseInfo(ResponseInfoUtil.createResponseInfoFromRequestInfo(request.getRequestInfo(), Boolean.TRUE)) + .planEmployeeAssignment(Collections.singletonList(request.getPlanEmployeeAssignment())) + .build(); + } +} diff --git a/health-services/plan-service/src/main/java/digit/service/PlanEnricher.java b/health-services/plan-service/src/main/java/digit/service/PlanEnricher.java index 75d79c840a3..b01c78ea82a 100644 --- a/health-services/plan-service/src/main/java/digit/service/PlanEnricher.java +++ b/health-services/plan-service/src/main/java/digit/service/PlanEnricher.java @@ -1,16 +1,18 @@ package digit.service; +import digit.web.models.Plan; import digit.web.models.PlanRequest; +import digit.web.models.boundary.BoundaryTypeHierarchy; +import digit.web.models.boundary.BoundaryTypeHierarchyDefinition; +import digit.web.models.boundary.EnrichedBoundary; +import digit.web.models.boundary.HierarchyRelation; import org.egov.common.utils.AuditDetailsEnrichmentUtil; import org.egov.common.utils.UUIDEnrichmentUtil; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; @Component public class PlanEnricher { @@ -105,5 +107,125 @@ public void enrichPlanUpdate(PlanRequest body) { } }); + // Enriching last modified time for update + body.getPlan().getAuditDetails().setLastModifiedTime(System.currentTimeMillis()); + } + + /** + * Enriches the boundary ancestral path and jurisdiction mapping for the provided boundary code in the plan request. + * + * @param plan The plan record whose boundary ancestral path has to be enriched. + * @param tenantBoundary boundary relationship from the boundary service for the given boundary code. + */ + public void enrichBoundaryAncestralPath(Plan plan, HierarchyRelation tenantBoundary) { + EnrichedBoundary boundary = tenantBoundary.getBoundary().get(0); + Map jurisdictionMapping = new LinkedHashMap<>(); + + StringBuilder boundaryAncestralPath = new StringBuilder(boundary.getCode()); + jurisdictionMapping.put(boundary.getBoundaryType(), boundary.getCode()); + + // Iterate through the child boundary until there are no more + while (!CollectionUtils.isEmpty(boundary.getChildren())) { + boundary = boundary.getChildren().get(0); + boundaryAncestralPath.append("|").append(boundary.getCode()); + jurisdictionMapping.put(boundary.getBoundaryType(), boundary.getCode()); + } + + // Setting the boundary ancestral path for the provided boundary + plan.setBoundaryAncestralPath(boundaryAncestralPath.toString()); + + // Setting jurisdiction mapping for the provided boundary + plan.setJurisdictionMapping(jurisdictionMapping); + } + + /** + * Helper method to enrich boundary hierarchy mapping. + * Creates a mapping of parentBoundaryType to childBoundaryType from the boundaryTypeHierarchy search response. + * + * @param boundaryTypeHierarchyDef Search response from boundary hierarchy search. + * @param boundaryHierarchyMapping boundary hierarchy map to be enriched. + * @return returns the highest boundary hierarchy for the given hierarchy type. + */ + private String getBoundaryHierarchyMapping(BoundaryTypeHierarchyDefinition boundaryTypeHierarchyDef, Map boundaryHierarchyMapping) { + String highestBoundaryHierarchy = null; + + for (BoundaryTypeHierarchy boundaryTypeHierarchy : boundaryTypeHierarchyDef.getBoundaryHierarchy()) { + if (ObjectUtils.isEmpty(boundaryTypeHierarchy.getParentBoundaryType())) + highestBoundaryHierarchy = boundaryTypeHierarchy.getBoundaryType(); + else + boundaryHierarchyMapping.put(boundaryTypeHierarchy.getParentBoundaryType(), boundaryTypeHierarchy.getBoundaryType()); + } + + return highestBoundaryHierarchy; + } + + /** + * Enriches jurisdiction mapping in plan for the given boundary ancestral path. + * + * @param plan plan with boundary ancestral path. + * @param boundaryTypeHierarchyDef boundary hierarchy for the given hierarchy type. + */ + public void enrichJurisdictionMapping(Plan plan, BoundaryTypeHierarchyDefinition boundaryTypeHierarchyDef) { + Map boundaryHierarchyMapping = new HashMap<>(); + + // Enriches the boundaryHierarchyMapping and returns the highest boundary hierarchy for the given hierarchy type. + String highestBoundaryHierarchy = getBoundaryHierarchyMapping(boundaryTypeHierarchyDef, boundaryHierarchyMapping); + + Map jurisdictionMapping = new LinkedHashMap<>(); + String boundaryHierarchy = highestBoundaryHierarchy; + + // Get the list of boundary codes from pipe separated boundaryAncestralPath. + List boundaryCode = getBoundaryCodeFromAncestralPath(plan.getBoundaryAncestralPath()); + + // Creates the mapping of boundary hierarchy with the corresponding boundary code. + for (String boundary : boundaryCode) { + jurisdictionMapping.put(boundaryHierarchy, boundary); + boundaryHierarchy = boundaryHierarchyMapping.get(boundaryHierarchy); + } + + plan.setJurisdictionMapping(jurisdictionMapping); + } + + /** + * Enriches jurisdiction mapping for the list of plans for the given boundary ancestral path. + * + * @param planList list of plans with boundary ancestral paths. + * @param boundaryTypeHierarchyDef boundary hierarchy for the given hierarchy type. + */ + public void enrichJurisdictionMapping(List planList, BoundaryTypeHierarchyDefinition boundaryTypeHierarchyDef) { + Map boundaryHierarchyMapping = new HashMap<>(); + + // Enriches the boundaryHierarchyMapping and returns the highest boundary hierarchy for the given hierarchy type. + String highestBoundaryHierarchy = getBoundaryHierarchyMapping(boundaryTypeHierarchyDef, boundaryHierarchyMapping); + + for (Plan plan : planList) { + + Map jurisdictionMapping = new LinkedHashMap<>(); + String boundaryHierarchy = highestBoundaryHierarchy; + + // Get the list of boundary codes from pipe separated boundaryAncestralPath. + List boundaryCode = getBoundaryCodeFromAncestralPath(plan.getBoundaryAncestralPath()); + + // Creates the mapping of boundary hierarchy with the corresponding boundary code. + for (String boundary : boundaryCode) { + jurisdictionMapping.put(boundaryHierarchy, boundary); + boundaryHierarchy = boundaryHierarchyMapping.get(boundaryHierarchy); + } + + plan.setJurisdictionMapping(jurisdictionMapping); + } + } + + /** + * Converts the boundaryAncestral path from a pipe separated string to an array of boundary codes. + * + * @param boundaryAncestralPath pipe separated boundaryAncestralPath. + * @return a list of boundary codes. + */ + private List getBoundaryCodeFromAncestralPath(String boundaryAncestralPath) { + if (ObjectUtils.isEmpty(boundaryAncestralPath)) { + return Collections.emptyList(); + } + return Arrays.asList(boundaryAncestralPath.split("\\|")); } } diff --git a/health-services/plan-service/src/main/java/digit/service/PlanFacilityService.java b/health-services/plan-service/src/main/java/digit/service/PlanFacilityService.java new file mode 100644 index 00000000000..8282a0fea74 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/PlanFacilityService.java @@ -0,0 +1,98 @@ +package digit.service; + +import digit.repository.PlanFacilityRepository; +import digit.service.enrichment.PlanFacilityEnricher; +import digit.service.validator.PlanFacilityValidator; +import digit.util.ResponseInfoFactory; +import digit.web.models.PlanFacility; +import digit.web.models.PlanFacilityRequest; +import digit.web.models.PlanFacilityResponse; +import org.egov.common.utils.ResponseInfoUtil; +import digit.web.models.PlanFacilitySearchRequest; +import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.List; + +@Service +public class PlanFacilityService { + + private PlanFacilityValidator planFacilityValidator; + private ResponseInfoFactory responseInfoFactory; + private PlanFacilityEnricher planFacilityEnricher; + private PlanFacilityRepository planFacilityRepository; + + public PlanFacilityService(PlanFacilityValidator planFacilityValidator, ResponseInfoFactory responseInfoFactory, PlanFacilityEnricher planFacilityEnricher, PlanFacilityRepository planFacilityRepository) { + this.planFacilityValidator = planFacilityValidator; + this.responseInfoFactory = responseInfoFactory; + this.planFacilityEnricher = planFacilityEnricher; + this.planFacilityRepository = planFacilityRepository; + } + + /** + * This method processes the requests that come for creating plan facilities. + * + * @param planFacilityRequest The PlanFacilityRequest containing the plan facility details for creation. + * @return PlanFacilityResponse containing the created plan facility and response information. + */ + public PlanFacilityResponse createPlanFacility(PlanFacilityRequest planFacilityRequest) { + // Validate plan facility create request + planFacilityValidator.validatePlanFacilityCreate(planFacilityRequest); + + // Enrich plan facility create request + planFacilityEnricher.enrichPlanFacilityCreate(planFacilityRequest); + + // Delegate creation request to repository + planFacilityRepository.create(planFacilityRequest); + + // Build and return response back to controller + return PlanFacilityResponse.builder() + .planFacility(Collections.singletonList(planFacilityRequest.getPlanFacility())) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(planFacilityRequest.getRequestInfo(), Boolean.TRUE)) + .build(); + } + + /** + * This method processes the requests that come for searching plan facilities. + * + * @param planFacilitySearchRequest containing the search criteria and request information. + * @return PlanFacilityResponse object containing the search results and response information. + */ + public PlanFacilityResponse searchPlanFacility(PlanFacilitySearchRequest planFacilitySearchRequest) { + // Enrich search request + planFacilityEnricher.enrichSearchRequest(planFacilitySearchRequest); + + // Delegate request to repository + List planFacilityList = planFacilityRepository.search(planFacilitySearchRequest.getPlanFacilitySearchCriteria()); + + // Build and return response back to controller + return PlanFacilityResponse.builder() + .responseInfo(ResponseInfoUtil.createResponseInfoFromRequestInfo(planFacilitySearchRequest.getRequestInfo(), Boolean.TRUE)) + .planFacility(planFacilityList) + .totalCount(planFacilityRepository.count(planFacilitySearchRequest.getPlanFacilitySearchCriteria())) + .build(); + } + + /** + * Processes requests for updating plan facilities. + * + * @param planFacilityRequest The PlanFacilityRequest containing the update information. + * @return PlanFacilityResponse containing the updated plan facility and response information. + */ + public PlanFacilityResponse updatePlanFacility(PlanFacilityRequest planFacilityRequest) { + //validate plan facility request + planFacilityValidator.validatePlanFacilityUpdate(planFacilityRequest); + + //enrich plan facility request + planFacilityEnricher.enrichPlanFacilityUpdate(planFacilityRequest); + + //delegate update request to repository + planFacilityRepository.update(planFacilityRequest); + + //Build and return response back to controller + return PlanFacilityResponse.builder(). + responseInfo(ResponseInfoUtil.createResponseInfoFromRequestInfo(planFacilityRequest.getRequestInfo(), Boolean.TRUE)). + planFacility(Collections.singletonList(planFacilityRequest.getPlanFacility())). + build(); + } + +} diff --git a/health-services/plan-service/src/main/java/digit/service/PlanService.java b/health-services/plan-service/src/main/java/digit/service/PlanService.java index c0c97a30eaf..4acaf58218b 100644 --- a/health-services/plan-service/src/main/java/digit/service/PlanService.java +++ b/health-services/plan-service/src/main/java/digit/service/PlanService.java @@ -1,16 +1,16 @@ package digit.service; import digit.repository.PlanRepository; -import digit.web.models.Plan; -import digit.web.models.PlanRequest; -import digit.web.models.PlanResponse; -import digit.web.models.PlanSearchRequest; +import digit.service.workflow.WorkflowService; +import digit.web.models.*; +import org.egov.common.contract.response.ResponseInfo; import org.egov.common.utils.ResponseInfoUtil; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; @Service public class PlanService { @@ -21,10 +21,13 @@ public class PlanService { private PlanRepository planRepository; - public PlanService(PlanValidator planValidator, PlanEnricher planEnricher, PlanRepository planRepository) { + private WorkflowService workflowService; + + public PlanService(PlanValidator planValidator, PlanEnricher planEnricher, PlanRepository planRepository, WorkflowService workflowService) { this.planValidator = planValidator; this.planEnricher = planEnricher; this.planRepository = planRepository; + this.workflowService = workflowService; } /** @@ -39,6 +42,9 @@ public PlanResponse createPlan(PlanRequest body) { // Enrich plan create request planEnricher.enrichPlanCreate(body); + // Call workflow transition API for status update + workflowService.invokeWorkflowForStatusUpdate(body); + // Delegate creation request to repository planRepository.create(body); @@ -58,10 +64,18 @@ public PlanResponse searchPlan(PlanSearchRequest body) { // Delegate search request to repository List planList = planRepository.search(body.getPlanSearchCriteria()); + // Get the total count of plans for given search criteria + Integer count = planRepository.count(body.getPlanSearchCriteria()); + + // Get the status count of plans for given search criteria + Map statusCountMap = planRepository.statusCount(body); + // Build and return response back to controller return PlanResponse.builder() .responseInfo(ResponseInfoUtil.createResponseInfoFromRequestInfo(body.getRequestInfo(), Boolean.TRUE)) .plan(planList) + .totalCount(count) + .statusCount(statusCountMap) .build(); } @@ -77,6 +91,9 @@ public PlanResponse updatePlan(PlanRequest body) { // Enrich plan update request planEnricher.enrichPlanUpdate(body); + // Call workflow transition API for status update + workflowService.invokeWorkflowForStatusUpdate(body); + // Delegate update request to repository planRepository.update(body); @@ -86,4 +103,25 @@ public PlanResponse updatePlan(PlanRequest body) { .plan(Collections.singletonList(body.getPlan())) .build(); } + + /** + * This method processes bulk update requests for plan. + * @param bulkPlanRequest + * @return + */ + public PlanResponse bulkUpdate(BulkPlanRequest bulkPlanRequest) { + // Validate bulk plan update request + planValidator.validateBulkPlanUpdate(bulkPlanRequest); + + // Call workflow transition for updating status and assignee + workflowService.invokeWorkflowForStatusUpdate(bulkPlanRequest); + + // Delegate bulk update request to repository + planRepository.bulkUpdate(bulkPlanRequest); + + // Build and return response back to controller + return PlanResponse.builder().responseInfo(ResponseInfoUtil.createResponseInfoFromRequestInfo(bulkPlanRequest.getRequestInfo(), Boolean.TRUE)) + .plan(bulkPlanRequest.getPlans()) + .build(); + } } diff --git a/health-services/plan-service/src/main/java/digit/service/PlanValidator.java b/health-services/plan-service/src/main/java/digit/service/PlanValidator.java index 40958a9e0d3..8f495a271f8 100644 --- a/health-services/plan-service/src/main/java/digit/service/PlanValidator.java +++ b/health-services/plan-service/src/main/java/digit/service/PlanValidator.java @@ -1,10 +1,18 @@ package digit.service; import com.jayway.jsonpath.JsonPath; +import digit.config.Configuration; import digit.repository.PlanConfigurationRepository; import digit.repository.PlanRepository; +import digit.util.BoundaryUtil; +import digit.util.CampaignUtil; +import digit.util.CommonUtil; import digit.util.MdmsUtil; import digit.web.models.*; +import digit.web.models.boundary.BoundarySearchResponse; +import digit.web.models.boundary.BoundaryTypeHierarchyResponse; +import digit.web.models.boundary.HierarchyRelation; +import digit.web.models.projectFactory.CampaignResponse; import org.egov.common.utils.MultiStateInstanceUtil; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; @@ -27,20 +35,47 @@ public class PlanValidator { private MultiStateInstanceUtil centralInstanceUtil; - public PlanValidator(PlanRepository planRepository, PlanConfigurationRepository planConfigurationRepository, MdmsUtil mdmsUtil, MultiStateInstanceUtil centralInstanceUtil) { + private CommonUtil commonUtil; + + private CampaignUtil campaignUtil; + + private PlanEmployeeService planEmployeeService; + + private Configuration config; + + private PlanEnricher planEnricher; + + private BoundaryUtil boundaryUtil; + + public PlanValidator(PlanRepository planRepository, PlanConfigurationRepository planConfigurationRepository, MdmsUtil mdmsUtil, MultiStateInstanceUtil centralInstanceUtil, CommonUtil commonUtil, CampaignUtil campaignUtil, PlanEmployeeService planEmployeeService, Configuration config, PlanEnricher planEnricher, BoundaryUtil boundaryUtil) { this.planRepository = planRepository; this.planConfigurationRepository = planConfigurationRepository; this.mdmsUtil = mdmsUtil; this.centralInstanceUtil = centralInstanceUtil; + this.commonUtil = commonUtil; + this.campaignUtil = campaignUtil; + this.planEmployeeService = planEmployeeService; + this.config = config; + this.planEnricher = planEnricher; + this.boundaryUtil = boundaryUtil; } /** * This method performs business validations on plan create requests + * * @param request */ public void validatePlanCreate(PlanRequest request) { String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlan().getTenantId()); Object mdmsData = mdmsUtil.fetchMdmsData(request.getRequestInfo(), rootTenantId); + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(request.getRequestInfo(), request.getPlan().getCampaignId(), rootTenantId); + BoundarySearchResponse boundarySearchResponse = boundaryUtil.fetchBoundaryData(request.getRequestInfo(), request.getPlan().getLocality(), request.getPlan().getTenantId(), campaignResponse.getCampaignDetails().get(0).getHierarchyType(), Boolean.TRUE, Boolean.FALSE); + + //TODO: remove after setting the flag in consumer + request.getPlan().setRequestFromResourceEstimationConsumer(Boolean.TRUE); + + // Validate locality against boundary service + validateBoundaryCode(boundarySearchResponse, request.getPlan()); // Validate activities validateActivities(request); @@ -65,10 +100,32 @@ public void validatePlanCreate(PlanRequest request) { // Validate Metric Detail's Unit against MDMS validateMetricDetailUnit(request, mdmsData); + + // Validate if campaign id exists against project factory + validateCampaignId(campaignResponse); + + // Validate the user information in the request + commonUtil.validateUserInfo(request.getRequestInfo()); + + // Validate plan-employee assignment and jurisdiction is request is from Resource Estimation Consumer + if(!request.getPlan().isRequestFromResourceEstimationConsumer()) + validatePlanEmployeeAssignmentAndJurisdiction(request); + } + + /** + * Validates campaign ID from request against project factory + * + * @param campaignResponse The campaign details response from project factory + */ + private void validateCampaignId(CampaignResponse campaignResponse) { + if (CollectionUtils.isEmpty(campaignResponse.getCampaignDetails())) { + throw new CustomException(NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_CODE, NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_MESSAGE); + } } /** * This validation method validates if the dependent activities are valid and if they form a cycle + * * @param request */ private void validateActivityDependencies(PlanRequest request) { @@ -81,6 +138,7 @@ private void validateActivityDependencies(PlanRequest request) { /** * This method checks if the activity dependencies form a cycle + * * @param request */ private void checkForCycleInActivityDependencies(PlanRequest request) { @@ -90,7 +148,7 @@ private void checkForCycleInActivityDependencies(PlanRequest request) { activityCodeVsDependenciesMap.keySet().forEach(activityCode -> { activityCodeVsDependenciesMap.get(activityCode).forEach(dependency -> { - if(activityCodeVsDependenciesMap.get(dependency).contains(activityCode)) + if (activityCodeVsDependenciesMap.get(dependency).contains(activityCode)) throw new CustomException(CYCLIC_ACTIVITY_DEPENDENCY_CODE, CYCLIC_ACTIVITY_DEPENDENCY_MESSAGE); }); }); @@ -98,6 +156,7 @@ private void checkForCycleInActivityDependencies(PlanRequest request) { /** * This method validates if the dependent activity codes are valid + * * @param request */ private void validateDependentActivityCodes(PlanRequest request) { @@ -108,9 +167,9 @@ private void validateDependentActivityCodes(PlanRequest request) { // Check if the dependent activity codes are valid request.getPlan().getActivities().forEach(activity -> { - if(!CollectionUtils.isEmpty(activity.getDependencies())) { + if (!CollectionUtils.isEmpty(activity.getDependencies())) { activity.getDependencies().forEach(dependency -> { - if(!activityCodes.contains(dependency)) + if (!activityCodes.contains(dependency)) throw new CustomException(INVALID_ACTIVITY_DEPENDENCY_CODE, INVALID_ACTIVITY_DEPENDENCY_MESSAGE); }); } @@ -120,11 +179,12 @@ private void validateDependentActivityCodes(PlanRequest request) { /** * This method validates the activities provided in the request + * * @param request */ private void validateActivities(PlanRequest request) { // Collect all activity codes - if(request.getPlan().getActivities() == null) + if (request.getPlan().getActivities() == null) throw new CustomException(ACTIVITIES_CANNOT_BE_NULL_CODE, ACTIVITIES_CANNOT_BE_NULL_MESSAGE); Set activityCodes = request.getPlan().getActivities().stream() @@ -132,26 +192,26 @@ private void validateActivities(PlanRequest request) { .collect(Collectors.toSet()); // If activity codes are not unique, throw an exception - if(activityCodes.size() != request.getPlan().getActivities().size()) { + if (activityCodes.size() != request.getPlan().getActivities().size()) { throw new CustomException(DUPLICATE_ACTIVITY_CODES, DUPLICATE_ACTIVITY_CODES_MESSAGE); } // If execution plan id is not provided, providing activities is mandatory - if(ObjectUtils.isEmpty(request.getPlan().getExecutionPlanId()) + if (ObjectUtils.isEmpty(request.getPlan().getCampaignId()) && CollectionUtils.isEmpty(request.getPlan().getActivities())) { throw new CustomException(PLAN_ACTIVITIES_MANDATORY_CODE, PLAN_ACTIVITIES_MANDATORY_MESSAGE); } // If execution plan id is provided, providing activities is not allowed - if(!ObjectUtils.isEmpty(request.getPlan().getExecutionPlanId()) + if (!ObjectUtils.isEmpty(request.getPlan().getCampaignId()) && !CollectionUtils.isEmpty(request.getPlan().getActivities())) { throw new CustomException(PLAN_ACTIVITIES_NOT_ALLOWED_CODE, PLAN_ACTIVITIES_NOT_ALLOWED_MESSAGE); } // Validate activity dates - if(!CollectionUtils.isEmpty(request.getPlan().getActivities())) { + if (!CollectionUtils.isEmpty(request.getPlan().getActivities())) { request.getPlan().getActivities().forEach(activity -> { - if(activity.getPlannedEndDate() < activity.getPlannedStartDate()) + if (activity.getPlannedEndDate() < activity.getPlannedStartDate()) throw new CustomException(INVALID_ACTIVITY_DATES_CODE, INVALID_ACTIVITY_DATES_MESSAGE); }); } @@ -159,49 +219,37 @@ private void validateActivities(PlanRequest request) { /** * This method validates if the plan configuration id provided in the request exists + * * @param request */ private void validatePlanConfigurationExistence(PlanRequest request) { // If plan id provided is invalid, throw an exception - if(!ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) && CollectionUtils.isEmpty(planConfigurationRepository.search(PlanConfigurationSearchCriteria.builder() - .id(request.getPlan().getPlanConfigurationId()) - .tenantId(request.getPlan().getTenantId()) - .build()))) { + if (!ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) && + CollectionUtils.isEmpty(commonUtil.searchPlanConfigId(request.getPlan().getPlanConfigurationId(), request.getPlan().getTenantId()))) { throw new CustomException(INVALID_PLAN_CONFIG_ID_CODE, INVALID_PLAN_CONFIG_ID_MESSAGE); } } /** * This method validates the resources provided in the request + * * @param request */ private void validateResources(PlanRequest request) { // If plan configuration id is not provided, providing resources is mandatory - if(ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) + if (ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) && CollectionUtils.isEmpty(request.getPlan().getResources())) { throw new CustomException(PLAN_RESOURCES_MANDATORY_CODE, PLAN_RESOURCES_MANDATORY_MESSAGE); } - - // If plan configuration id is provided, providing resources is not allowed - if(!ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) - && !CollectionUtils.isEmpty(request.getPlan().getResources())) { - throw new CustomException(PLAN_RESOURCES_NOT_ALLOWED_CODE, PLAN_RESOURCES_NOT_ALLOWED_MESSAGE); - } - - // Validate resource type existence - if(!CollectionUtils.isEmpty(request.getPlan().getResources())) { - request.getPlan().getResources().forEach(resource -> { - // Validate resource type existence - }); - } } /** * This method validates the linkage between resources and activities + * * @param request */ private void validateResourceActivityLinkage(PlanRequest request) { - if(ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) + if (ObjectUtils.isEmpty(request.getPlan().getPlanConfigurationId()) && !CollectionUtils.isEmpty(request.getPlan().getActivities())) { // Collect all activity codes Set activityCodes = request.getPlan().getActivities().stream() @@ -210,7 +258,7 @@ private void validateResourceActivityLinkage(PlanRequest request) { // Validate resource-activity linkage request.getPlan().getResources().forEach(resource -> { - if(!activityCodes.contains(resource.getActivityCode())) + if (!activityCodes.contains(resource.getActivityCode())) throw new CustomException(INVALID_RESOURCE_ACTIVITY_LINKAGE_CODE, INVALID_RESOURCE_ACTIVITY_LINKAGE_MESSAGE); }); } @@ -218,10 +266,11 @@ private void validateResourceActivityLinkage(PlanRequest request) { /** * This method validates the linkage between targets and activities + * * @param request */ private void validateTargetActivityLinkage(PlanRequest request) { - if(!CollectionUtils.isEmpty(request.getPlan().getActivities())) { + if (!CollectionUtils.isEmpty(request.getPlan().getActivities())) { // Collect all activity codes Set activityCodes = request.getPlan().getActivities().stream() .map(Activity::getCode) @@ -229,7 +278,7 @@ private void validateTargetActivityLinkage(PlanRequest request) { // Validate target-activity linkage request.getPlan().getTargets().forEach(target -> { - if(!activityCodes.contains(target.getActivityCode())) + if (!activityCodes.contains(target.getActivityCode())) throw new CustomException(INVALID_TARGET_ACTIVITY_LINKAGE_CODE, INVALID_TARGET_ACTIVITY_LINKAGE_MESSAGE); }); } @@ -237,6 +286,7 @@ private void validateTargetActivityLinkage(PlanRequest request) { /** * This method performs business validations on plan update requests + * * @param request */ public void validatePlanUpdate(PlanRequest request) { @@ -245,6 +295,12 @@ public void validatePlanUpdate(PlanRequest request) { String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlan().getTenantId()); Object mdmsData = mdmsUtil.fetchMdmsData(request.getRequestInfo(), rootTenantId); + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(request.getRequestInfo(), request.getPlan().getCampaignId(), rootTenantId); + BoundaryTypeHierarchyResponse boundaryTypeHierarchyResponse = boundaryUtil.fetchBoundaryHierarchy(request.getRequestInfo(), request.getPlan().getTenantId(), campaignResponse.getCampaignDetails().get(0).getHierarchyType()); + + //TODO: remove after setting the flag in consumer + request.getPlan().setRequestFromResourceEstimationConsumer(Boolean.TRUE); + // Validate activities validateActivities(request); @@ -279,6 +335,14 @@ public void validatePlanUpdate(PlanRequest request) { // Validate Metric Detail's Unit against MDMS validateMetricDetailUnit(request, mdmsData); + // Validate the user information in the request + commonUtil.validateUserInfo(request.getRequestInfo()); + + // Validate plan-employee assignment and jurisdiction + validatePlanEmployeeAssignmentAndJurisdiction(request); + + // Enrich jurisdiction mapping in plan + planEnricher.enrichJurisdictionMapping(request.getPlan(), boundaryTypeHierarchyResponse.getBoundaryHierarchy().get(0)); } /** @@ -338,15 +402,19 @@ private void validateActivitiesUuidUniqueness(PlanRequest request) { /** * This method validates if the plan id provided in the update request exists - * @param request + * + * @param request the PlanRequest containing the plan */ private void validatePlanExistence(PlanRequest request) { // If plan id provided is invalid, throw an exception - if(CollectionUtils.isEmpty(planRepository.search(PlanSearchCriteria.builder() + List planFromDatabase = planRepository.search(PlanSearchCriteria.builder() .ids(Collections.singleton(request.getPlan().getId())) - .build()))) { + .build()); + if (CollectionUtils.isEmpty(planFromDatabase)) { throw new CustomException(INVALID_PLAN_ID_CODE, INVALID_PLAN_ID_MESSAGE); } + // enriching boundary ancestral path for incoming plan request from the database + request.getPlan().setBoundaryAncestralPath(planFromDatabase.get(0).getBoundaryAncestralPath()); } /** @@ -355,7 +423,7 @@ private void validatePlanExistence(PlanRequest request) { * This method checks each target metric in the plan to ensure it exists in the MDMS data. * If a metric is not found, it throws a CustomException. * - * @param request the PlanRequest containing the plan and target metrics to be validated + * @param request the PlanRequest containing the plan and target metrics to be validated * @param mdmsData the MDMS data against which the target metrics are validated * @throws CustomException if there is an error reading the MDMS data using JsonPath * or if any target metric is not found in the MDMS data @@ -386,7 +454,7 @@ public void validateTargetMetrics(PlanRequest request, Object mdmsData) { * This method extracts metric details from the plan and checks if each metric unit * is present in the MDMS data. If a metric unit is not found, it throws a CustomException. * - * @param request the PlanRequest containing the plan and metric details to be validated + * @param request the PlanRequest containing the plan and metric details to be validated * @param mdmsData the MDMS data against which the metric units are validated * @throws CustomException if there is an error reading the MDMS data using JsonPath * or if any metric unit is not found in the MDMS data @@ -415,4 +483,195 @@ public void validateMetricDetailUnit(PlanRequest request, Object mdmsData) { } + /** + * Validates the plan's employee assignment and ensures the jurisdiction is valid based on tenant, employee, role, and plan configuration. + * If no assignment is found, throws a custom exception. + * + * @param planRequest the request containing the plan and workflow details + * @throws CustomException if no employee assignment is found or jurisdiction is invalid + */ + public void validatePlanEmployeeAssignmentAndJurisdiction(PlanRequest planRequest) { + PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria = PlanEmployeeAssignmentSearchCriteria + .builder() + .tenantId(planRequest.getPlan().getTenantId()) + .employeeId(Collections.singletonList(planRequest.getRequestInfo().getUserInfo().getUuid())) + .planConfigurationId(planRequest.getPlan().getPlanConfigurationId()) + .role(config.getPlanEstimationApproverRoles()) + .build(); + + PlanEmployeeAssignmentResponse planEmployeeAssignmentResponse = planEmployeeService.search(PlanEmployeeAssignmentSearchRequest.builder() + .planEmployeeAssignmentSearchCriteria(planEmployeeAssignmentSearchCriteria) + .requestInfo(planRequest.getRequestInfo()).build()); + + if(CollectionUtils.isEmpty(planEmployeeAssignmentResponse.getPlanEmployeeAssignment())) + throw new CustomException(PLAN_EMPLOYEE_ASSIGNMENT_NOT_FOUND_CODE, PLAN_EMPLOYEE_ASSIGNMENT_NOT_FOUND_MESSAGE + planRequest.getPlan().getLocality()); + + validateJurisdiction(planRequest.getPlan(), + planEmployeeAssignmentResponse.getPlanEmployeeAssignment().get(0).getJurisdiction()); + } + + /** + * Validates that at least one jurisdiction exists within the hierarchy's boundary codes. + * If no jurisdiction is found in the boundary set, throws a custom exception. + * + * @param plan the plan containing the boundary ancestral path + * @param jurisdictions the list of jurisdictions to check against the boundary set + * @throws CustomException if none of the jurisdictions are present in the boundary codes + */ + public void validateJurisdiction(Plan plan, Set jurisdictions) { + Set boundarySet = new HashSet<>(Arrays.asList(plan.getBoundaryAncestralPath() + .split(PIPE_REGEX))); + + // Check if any jurisdiction is present in the boundary set + if (jurisdictions.stream().noneMatch(boundarySet::contains)) + throw new CustomException(JURISDICTION_NOT_FOUND_CODE, JURISDICTION_NOT_FOUND_MESSAGE); + + // Enrich jurisdiction of current assignee + plan.setAssigneeJurisdiction(new ArrayList<>(jurisdictions)); + + } + + /** + * Validates the boundary code provided in plan request against boundary service. + * + * @param boundarySearchResponse response from the boundary service. + * @param plan Plan record whose loclality is to be validated. + */ + private void validateBoundaryCode(BoundarySearchResponse boundarySearchResponse, Plan plan) { + HierarchyRelation tenantBoundary = boundarySearchResponse.getTenantBoundary().get(0); + + if (CollectionUtils.isEmpty(tenantBoundary.getBoundary())) { + throw new CustomException(NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_CODE, NO_BOUNDARY_DATA_FOUND_FOR_GIVEN_BOUNDARY_CODE_MESSAGE); + } + + //TODO: change to if(!plan.isRequestFromResourceEstimationConsumer()) after triggering from consumer + + // Enrich the boundary ancestral path and jurisdiction mapping for the provided boundary code + if(plan.isRequestFromResourceEstimationConsumer()) + planEnricher.enrichBoundaryAncestralPath(plan, tenantBoundary); + } + + /** + * TODO - 1. plan existence 2. plan employee assignment 3. uniqueness check across records + * @param bulkPlanRequest + */ + public void validateBulkPlanUpdate(BulkPlanRequest bulkPlanRequest) { + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(bulkPlanRequest.getRequestInfo(), bulkPlanRequest.getPlans().get(0).getCampaignId(), bulkPlanRequest.getPlans().get(0).getTenantId()); + BoundaryTypeHierarchyResponse boundaryTypeHierarchyResponse = boundaryUtil.fetchBoundaryHierarchy(bulkPlanRequest.getRequestInfo(), bulkPlanRequest.getPlans().get(0).getTenantId(), campaignResponse.getCampaignDetails().get(0).getHierarchyType()); + + // Validate attributes across each plan in the bulk request + validatePlanAttributes(bulkPlanRequest); + + // Validate if plans provided in the request body exist + validatePlanExistence(bulkPlanRequest); + + // Validate plan employee assignment and jurisdiction + validatePlanEmployeeAssignmentAndJurisdiction(bulkPlanRequest); + + // Enrich jurisdiction mapping in plan + planEnricher.enrichJurisdictionMapping(bulkPlanRequest.getPlans(), boundaryTypeHierarchyResponse.getBoundaryHierarchy().get(0)); + } + + /** + * + * @param bulkPlanRequest + */ + private void validatePlanEmployeeAssignmentAndJurisdiction(BulkPlanRequest bulkPlanRequest) { + // Prepare plan employee assignment search criteria + PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria = PlanEmployeeAssignmentSearchCriteria + .builder() + .tenantId(bulkPlanRequest.getPlans().get(0).getTenantId()) + .employeeId(Collections.singletonList(bulkPlanRequest.getRequestInfo().getUserInfo().getUuid())) + .planConfigurationId(bulkPlanRequest.getPlans().get(0).getPlanConfigurationId()) + .role(config.getPlanEstimationApproverRoles()) + .build(); + + // Fetch plan employee assignment + PlanEmployeeAssignmentResponse planEmployeeAssignmentResponse = planEmployeeService.search(PlanEmployeeAssignmentSearchRequest.builder() + .planEmployeeAssignmentSearchCriteria(planEmployeeAssignmentSearchCriteria) + .requestInfo(bulkPlanRequest.getRequestInfo()) + .build()); + + // Throw exception if the employee taking action is not a part of plan + if(CollectionUtils.isEmpty(planEmployeeAssignmentResponse.getPlanEmployeeAssignment())) { + throw new CustomException(PLAN_EMPLOYEE_ASSIGNMENT_NOT_FOUND_CODE, + PLAN_EMPLOYEE_ASSIGNMENT_NOT_FOUND_MESSAGE); + } + + // Validate jurisdiction for each plan + bulkPlanRequest.getPlans().forEach(plan -> validateJurisdiction(plan, + planEmployeeAssignmentResponse.getPlanEmployeeAssignment().get(0).getJurisdiction())); + + } + + /** + * + * @param bulkPlanRequest + */ + private void validatePlanAttributes(BulkPlanRequest bulkPlanRequest) { + if(bulkPlanRequest.getPlans().stream().map(Plan :: getId).collect(Collectors.toSet()).size() + != bulkPlanRequest.getPlans().size()) { + throw new CustomException("BULK_UPDATE_ERROR", + "Plans provided in the bulk update request are not unique."); + } + + if(!bulkPlanRequest.getPlans().stream().allMatch(plan -> + plan.getTenantId().equals(bulkPlanRequest.getPlans().get(0).getTenantId()) && + plan.getPlanConfigurationId().equals(bulkPlanRequest.getPlans().get(0).getPlanConfigurationId()))) { + throw new CustomException("BULK_UPDATE_ERROR", + "Tenant id and plan configuration ids should be same across all entries for bulk update."); + } + + bulkPlanRequest.getPlans().forEach(plan -> { + if(ObjectUtils.isEmpty(plan.getWorkflow())) { + throw new CustomException("BULK_UPDATE_ERROR", + "Workflow information is mandatory for each entry for bulk update"); + } + }); + + if(!bulkPlanRequest.getPlans().stream().allMatch(plan -> + plan.getStatus().equals(bulkPlanRequest.getPlans().get(0).getStatus()) && + plan.getWorkflow().getAction().equals(bulkPlanRequest.getPlans().get(0).getWorkflow().getAction()))) { + throw new CustomException("BULK_UPDATE_ERROR", + "All entries should be in the same state for bulk transitioning plan records."); + } + + } + + /** + * + * @param bulkPlanRequest + */ + private void validatePlanExistence(BulkPlanRequest bulkPlanRequest) { + // Get all plan ids to validate existence + List planListFromDatabase = planRepository.search(PlanSearchCriteria.builder() + .ids(bulkPlanRequest.getPlans().stream().map(Plan :: getId).collect(Collectors.toSet())) + .offset(0) + .limit(bulkPlanRequest.getPlans().size()) + .build()); + + // If plan id provided is invalid, throw an exception + if (planListFromDatabase.size() != bulkPlanRequest.getPlans().size()) { + throw new CustomException(INVALID_PLAN_ID_CODE, INVALID_PLAN_ID_MESSAGE); + } + + // Enrich ancestral materialized path for each plan object being passed in the request + enrichAncestralMaterializedPath(bulkPlanRequest, planListFromDatabase); + + } + + /** + * + * @param bulkPlanRequest + * @param planListFromDatabase + */ + private void enrichAncestralMaterializedPath(BulkPlanRequest bulkPlanRequest, List planListFromDatabase) { + Map planIdVsAncestralMaterializedPathMap = planListFromDatabase.stream() + .collect(Collectors.toMap(Plan :: getId, Plan :: getBoundaryAncestralPath)); + + bulkPlanRequest.getPlans().forEach(plan -> + plan.setBoundaryAncestralPath(planIdVsAncestralMaterializedPathMap + .get(plan.getId())) + ); + } } diff --git a/health-services/plan-service/src/main/java/digit/service/enrichment/EnrichmentService.java b/health-services/plan-service/src/main/java/digit/service/enrichment/EnrichmentService.java index 86c1fdb4b81..7dfb66f4a64 100644 --- a/health-services/plan-service/src/main/java/digit/service/enrichment/EnrichmentService.java +++ b/health-services/plan-service/src/main/java/digit/service/enrichment/EnrichmentService.java @@ -1,33 +1,37 @@ package digit.service.enrichment; import digit.config.Configuration; -import digit.web.models.File; -import digit.web.models.PlanConfiguration; -import digit.web.models.PlanConfigurationRequest; -import digit.web.models.ResourceMapping; +import digit.util.CommonUtil; +import digit.web.models.*; import lombok.extern.slf4j.Slf4j; import org.egov.common.utils.UUIDEnrichmentUtil; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; import java.util.List; -import static org.egov.common.utils.AuditDetailsEnrichmentUtil.prepareAuditDetails; -import org.springframework.util.ObjectUtils; +import static digit.config.ServiceConstants.DRAFT_STATUS; +import static org.egov.common.utils.AuditDetailsEnrichmentUtil.prepareAuditDetails; @Component @Slf4j public class EnrichmentService { private Configuration config; - public EnrichmentService(Configuration config) { + private CommonUtil commonUtil; + + public EnrichmentService(Configuration config, CommonUtil commonUtil) { this.config = config; + this.commonUtil = commonUtil; } /** * Enriches the PlanConfigurationRequest for creating a new plan configuration. * Enriches the given plan configuration with generated IDs for plan, files, assumptions, operations, and resource mappings, * validates user information, and enriches audit details for create operation. + * * @param request The PlanConfigurationRequest to be enriched. * @throws CustomException if user information is missing in the request. */ @@ -35,24 +39,35 @@ public void enrichCreate(PlanConfigurationRequest request) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); log.info("Enriching plan config with generated IDs"); + //set Draft status on create + planConfiguration.setStatus(DRAFT_STATUS); + // Generate id for plan configuration UUIDEnrichmentUtil.enrichRandomUuid(planConfiguration, "id"); // Generate id for files - planConfiguration.getFiles().forEach(file -> { - UUIDEnrichmentUtil.enrichRandomUuid(file, "id"); - enrichActiveForResourceMapping(file, planConfiguration.getResourceMapping()); - }); - + if (!CollectionUtils.isEmpty(planConfiguration.getFiles())) { + planConfiguration.getFiles().forEach(file -> { + UUIDEnrichmentUtil.enrichRandomUuid(file, "id"); + enrichActiveForResourceMapping(file, planConfiguration.getResourceMapping()); + }); + } // Generate id for assumptions - planConfiguration.getAssumptions().forEach(assumption -> UUIDEnrichmentUtil.enrichRandomUuid(assumption, "id")); + if (!CollectionUtils.isEmpty(planConfiguration.getAssumptions())) { + planConfiguration.getAssumptions().forEach(assumption -> UUIDEnrichmentUtil.enrichRandomUuid(assumption, "id")); + } + // Generate id for operations - planConfiguration.getOperations().forEach(operation -> UUIDEnrichmentUtil.enrichRandomUuid(operation, "id")); + if (!CollectionUtils.isEmpty(planConfiguration.getOperations())) { + planConfiguration.getOperations().forEach(operation -> UUIDEnrichmentUtil.enrichRandomUuid(operation, "id")); + } // Generate id for resource mappings - planConfiguration.getResourceMapping().forEach(resourceMapping -> UUIDEnrichmentUtil.enrichRandomUuid(resourceMapping, "id")); + if (!CollectionUtils.isEmpty(planConfiguration.getResourceMapping())) { + planConfiguration.getResourceMapping().forEach(resourceMapping -> UUIDEnrichmentUtil.enrichRandomUuid(resourceMapping, "id")); + } planConfiguration.setAuditDetails(prepareAuditDetails(planConfiguration.getAuditDetails(), request.getRequestInfo(), Boolean.TRUE)); } @@ -60,6 +75,7 @@ public void enrichCreate(PlanConfigurationRequest request) { /** * Enriches the PlanConfigurationRequest for updating an existing plan configuration. * This method enriches the plan configuration for update, validates user information, and enriches audit details for update operation. + * * @param request The PlanConfigurationRequest to be enriched. * @throws CustomException if user information is missing in the request. */ @@ -67,41 +83,54 @@ public void enrichUpdate(PlanConfigurationRequest request) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); // Generate id for Files - planConfiguration.getFiles().forEach(file -> { - if (ObjectUtils.isEmpty(file.getId())) { - UUIDEnrichmentUtil.enrichRandomUuid(file, "id"); - } - enrichActiveForResourceMapping(file, request.getPlanConfiguration().getResourceMapping()); - }); + if (!CollectionUtils.isEmpty(planConfiguration.getFiles())) { + planConfiguration.getFiles().forEach(file -> { + if (ObjectUtils.isEmpty(file.getId())) { + UUIDEnrichmentUtil.enrichRandomUuid(file, "id"); + } + enrichActiveForResourceMapping(file, request.getPlanConfiguration().getResourceMapping()); + }); + } // Generate id for Assumptions - planConfiguration.getAssumptions().forEach(assumption -> { - if (ObjectUtils.isEmpty(assumption.getId())) { - UUIDEnrichmentUtil.enrichRandomUuid(assumption, "id"); - } - }); + if (!CollectionUtils.isEmpty(planConfiguration.getAssumptions())) { + planConfiguration.getAssumptions().forEach(assumption -> { + if (ObjectUtils.isEmpty(assumption.getId())) { + UUIDEnrichmentUtil.enrichRandomUuid(assumption, "id"); + } + }); + } // Generate id for Operations - planConfiguration.getOperations().forEach(operation -> { - if (ObjectUtils.isEmpty(operation.getId())) { - UUIDEnrichmentUtil.enrichRandomUuid(operation, "id"); - } - }); + if (!CollectionUtils.isEmpty(planConfiguration.getOperations())) { + planConfiguration.getOperations().forEach(operation -> { + if (ObjectUtils.isEmpty(operation.getId())) { + UUIDEnrichmentUtil.enrichRandomUuid(operation, "id"); + } + }); + } // Generate id for ResourceMappings - planConfiguration.getResourceMapping().forEach(resourceMapping -> { - if (ObjectUtils.isEmpty(resourceMapping.getId())) { - UUIDEnrichmentUtil.enrichRandomUuid(resourceMapping, "id"); - } - }); + if (!CollectionUtils.isEmpty(planConfiguration.getResourceMapping())) { + planConfiguration.getResourceMapping().forEach(resourceMapping -> { + if (ObjectUtils.isEmpty(resourceMapping.getId())) { + UUIDEnrichmentUtil.enrichRandomUuid(resourceMapping, "id"); + } + }); + } planConfiguration.setAuditDetails(prepareAuditDetails(request.getPlanConfiguration().getAuditDetails(), request.getRequestInfo(), Boolean.FALSE)); + + //enrich execution order for operations on setup complete + if (commonUtil.checkForEmptyOperationsOrAssumptions(planConfiguration)) { + enrichExecutionOrderForOperations(planConfiguration); + } } /** * Sets all corresponding resource mappings to inactive if the given file is inactive. * - * @param file the file object which may be inactive + * @param file the file object which may be inactive * @param resourceMappings the list of resource mappings to update */ public void enrichActiveForResourceMapping(File file, List resourceMappings) { @@ -120,34 +149,55 @@ public void enrichActiveForResourceMapping(File file, List reso * * @param request the plan configuration request containing the plan configuration to be enriched */ - public void enrichPlanConfigurationBeforeValidation(PlanConfigurationRequest request) - { + public void enrichPlanConfigurationBeforeValidation(PlanConfigurationRequest request) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); // For Files, Operations, Assumptions and Resource Mappings override active to be True - planConfiguration.getFiles().forEach(file -> { - if (ObjectUtils.isEmpty(file.getId())) { - file.setActive(Boolean.TRUE); - } - }); - - planConfiguration.getOperations().forEach(operation -> { - if (ObjectUtils.isEmpty(operation.getId())) { - operation.setActive(Boolean.TRUE); - } - }); - - planConfiguration.getAssumptions().forEach(assumption -> { - if (ObjectUtils.isEmpty(assumption.getId())) { - assumption.setActive(Boolean.TRUE); - } - }); - - planConfiguration.getResourceMapping().forEach(resourceMapping -> { - if (ObjectUtils.isEmpty(resourceMapping.getId())) { - resourceMapping.setActive(Boolean.TRUE); - } - }); + if (!CollectionUtils.isEmpty(planConfiguration.getFiles())) { + planConfiguration.getFiles().forEach(file -> { + if (ObjectUtils.isEmpty(file.getId())) { + file.setActive(Boolean.TRUE); + } + }); + } + + if (!CollectionUtils.isEmpty(planConfiguration.getOperations())) { + planConfiguration.getOperations().forEach(operation -> { + if (ObjectUtils.isEmpty(operation.getId())) { + operation.setActive(Boolean.TRUE); + } + }); + } + + if (!CollectionUtils.isEmpty(planConfiguration.getAssumptions())) { + planConfiguration.getAssumptions().forEach(assumption -> { + if (ObjectUtils.isEmpty(assumption.getId())) { + assumption.setActive(Boolean.TRUE); + } + }); + } + + if (!CollectionUtils.isEmpty(planConfiguration.getResourceMapping())) { + planConfiguration.getResourceMapping().forEach(resourceMapping -> { + if (ObjectUtils.isEmpty(resourceMapping.getId())) { + resourceMapping.setActive(Boolean.TRUE); + } + }); + } + } + + /** + * Sets a sequential execution order for each operation in the given PlanConfiguration. + * + * @param planConfiguration the configuration containing operations to order + */ + public void enrichExecutionOrderForOperations(PlanConfiguration planConfiguration) { + int executionOrderCounter = 1; + + for (Operation operation : planConfiguration.getOperations()) { + if(operation.getActive()) + operation.setExecutionOrder(executionOrderCounter++); + } } } diff --git a/health-services/plan-service/src/main/java/digit/service/enrichment/PlanEmployeeAssignmentEnricher.java b/health-services/plan-service/src/main/java/digit/service/enrichment/PlanEmployeeAssignmentEnricher.java new file mode 100644 index 00000000000..c4072ca637e --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/enrichment/PlanEmployeeAssignmentEnricher.java @@ -0,0 +1,71 @@ +package digit.service.enrichment; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import digit.repository.PlanEmployeeAssignmentRepository; +import digit.util.CommonUtil; +import digit.web.models.*; +import org.egov.common.utils.UUIDEnrichmentUtil; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static digit.config.ServiceConstants.*; +import static org.egov.common.utils.AuditDetailsEnrichmentUtil.prepareAuditDetails; + +@Component +public class PlanEmployeeAssignmentEnricher { + + private ObjectMapper objectMapper; + + private PlanEmployeeAssignmentRepository repository; + + private CommonUtil commonUtil; + + public PlanEmployeeAssignmentEnricher(ObjectMapper objectMapper, CommonUtil commonUtil, PlanEmployeeAssignmentRepository repository) { + this.objectMapper = objectMapper; + this.commonUtil = commonUtil; + this.repository = repository; + } + + /** + * Enriches the PlanEmployeeAssignmentRequest with id and audit details and sets active as true. + * + * @param request The PlanEmployeeAssignmentRequest body to be enriched + */ + public void enrichCreate(PlanEmployeeAssignmentRequest request) { + PlanEmployeeAssignment planEmployeeAssignment = request.getPlanEmployeeAssignment(); + + // Generate id for Plan employee assignment body + UUIDEnrichmentUtil.enrichRandomUuid(planEmployeeAssignment, "id"); + + // Set active true + planEmployeeAssignment.setActive(Boolean.TRUE); + + // Set Audit Details for Plan employee assignment + planEmployeeAssignment.setAuditDetails(prepareAuditDetails(planEmployeeAssignment.getAuditDetails(), + request.getRequestInfo(), + Boolean.TRUE)); + + // Add plan config name to which the employee is mapped + planEmployeeAssignment.setPlanConfigurationName(commonUtil.getPlanConfigName(planEmployeeAssignment.getTenantId(), planEmployeeAssignment.getPlanConfigurationId())); + } + + /** + * Enriches the PlanEmployeeAssignmentRequest for updating an existing plan employee assignment with audit details. + * + * @param request The PlanEmployeeAssignmentRequest body to be enriched + */ + public void enrichUpdate(PlanEmployeeAssignmentRequest request) { + PlanEmployeeAssignment planEmployeeAssignment = request.getPlanEmployeeAssignment(); + + // Set Audit Details for Plan employee assignment update + planEmployeeAssignment.setAuditDetails(prepareAuditDetails(planEmployeeAssignment.getAuditDetails(), + request.getRequestInfo(), + Boolean.FALSE)); + } +} diff --git a/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java b/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java new file mode 100644 index 00000000000..a07530dbfd2 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java @@ -0,0 +1,213 @@ +package digit.service.enrichment; + +import digit.util.BoundaryUtil; +import digit.util.CensusUtil; +import digit.util.CommonUtil; +import digit.web.models.PlanFacility; +import digit.web.models.PlanFacilityRequest; +import digit.web.models.PlanFacilitySearchCriteria; +import digit.web.models.PlanFacilitySearchRequest; +import digit.web.models.boundary.BoundarySearchResponse; +import digit.web.models.boundary.EnrichedBoundary; +import digit.web.models.census.*; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.utils.AuditDetailsEnrichmentUtil; +import org.egov.common.utils.UUIDEnrichmentUtil; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +import static digit.config.ServiceConstants.*; +import static org.egov.common.utils.AuditDetailsEnrichmentUtil.prepareAuditDetails; + +@Component +@Slf4j +public class PlanFacilityEnricher { + + private CommonUtil commonUtil; + + private CensusUtil censusUtil; + + private BoundaryUtil boundaryUtil; + + public PlanFacilityEnricher(CommonUtil commonUtil, CensusUtil censusUtil, BoundaryUtil boundaryUtil) { + this.commonUtil = commonUtil; + this.censusUtil = censusUtil; + this.boundaryUtil = boundaryUtil; + } + + /** + * Enriches the plan facility create request + * + * @param planFacilityRequest + */ + public void enrichPlanFacilityCreate(@Valid PlanFacilityRequest planFacilityRequest) { + // Generate id for plan facility + UUIDEnrichmentUtil.enrichRandomUuid(planFacilityRequest.getPlanFacility(), "id"); + + // Enrich audit details + planFacilityRequest.getPlanFacility().setAuditDetails(AuditDetailsEnrichmentUtil + .prepareAuditDetails(planFacilityRequest.getPlanFacility().getAuditDetails(), + planFacilityRequest.getRequestInfo(), Boolean.TRUE)); + + //Set Active + planFacilityRequest.getPlanFacility().setActive(Boolean.TRUE); + + // Add plan config name to which the facility is mapped + planFacilityRequest.getPlanFacility().setPlanConfigurationName(commonUtil.getPlanConfigName(planFacilityRequest.getPlanFacility().getTenantId(), planFacilityRequest.getPlanFacility().getPlanConfigurationId())); + } + + public void enrichJurisdictionMapping(PlanFacilityRequest request, String hierarchyType) { + BoundarySearchResponse boundarySearchResponse = boundaryUtil.fetchBoundaryData(request.getRequestInfo(), request.getPlanFacility().getResidingBoundary(), request.getPlanFacility().getTenantId(), hierarchyType, Boolean.TRUE, Boolean.FALSE); + + EnrichedBoundary boundary = boundarySearchResponse.getTenantBoundary().get(0).getBoundary().get(0); + Map jurisdictionMapping = new LinkedHashMap<>(); + + jurisdictionMapping.put(boundary.getBoundaryType(), boundary.getCode()); + StringBuilder boundaryAncestralPath = new StringBuilder(boundary.getCode()); + + // Iterate through the child boundary until there are no more + while (!CollectionUtils.isEmpty(boundary.getChildren())) { + boundary = boundary.getChildren().get(0); + + boundaryAncestralPath.append("|").append(boundary.getCode()); + jurisdictionMapping.put(boundary.getBoundaryType(), boundary.getCode()); + } + + // Setting the boundary ancestral path for the provided boundary + request.getPlanFacility().setBoundaryAncestralPath(boundaryAncestralPath.toString()); + + // Setting jurisdiction mapping for the provided boundary + request.getPlanFacility().setJurisdictionMapping(jurisdictionMapping); + } + + /** + * Enriches the plan facility update request + * + * @param planFacilityRequest The PlanFacilityRequest object contains the plan facility to be enriched. + */ + public void enrichPlanFacilityUpdate(PlanFacilityRequest planFacilityRequest) { + PlanFacility planFacility = planFacilityRequest.getPlanFacility(); + + //enrich audit details + planFacility.setAuditDetails(prepareAuditDetails(planFacilityRequest.getPlanFacility().getAuditDetails(), planFacilityRequest.getRequestInfo(), Boolean.FALSE)); + + // enrich serving population + enrichServingPopulation(planFacilityRequest); + } + + /** + * Enriches serving population based on the serving boundaries provided. + * + * @param planFacilityRequest plan facility request whose serving population is to be enriched. + */ + private void enrichServingPopulation(PlanFacilityRequest planFacilityRequest) { + PlanFacility planFacility = planFacilityRequest.getPlanFacility(); + + // Prepare list of boundaries whose census records are to be fetched + Set boundariesToBeSearched = new HashSet<>(planFacility.getServiceBoundaries()); + boundariesToBeSearched.addAll(planFacility.getInitiallySetServiceBoundaries()); + + if(!CollectionUtils.isEmpty(boundariesToBeSearched)) { + CensusSearchCriteria censusSearchCriteria = CensusSearchCriteria.builder() + .tenantId(planFacility.getTenantId()) + .source(planFacility.getPlanConfigurationId()) + .areaCodes(new ArrayList<>(boundariesToBeSearched)) + .limit(boundariesToBeSearched.size()) + .build(); + + CensusResponse censusResponse = censusUtil.fetchCensusRecords(CensusSearchRequest.builder() + .requestInfo(planFacilityRequest.getRequestInfo()) + .censusSearchCriteria(censusSearchCriteria) + .build()); + + // Creates a population map based on the confirmed target population of the boundary + Map boundaryToPopMap = getPopulationMap(censusResponse.getCensus()); + + // Get existing servingPopulation or default to 0 + BigDecimal servingPopulation = (BigDecimal) commonUtil.extractFieldsFromJsonObject(planFacility.getAdditionalDetails(), SERVING_POPULATION_CODE); + + updateServingPopulation(boundariesToBeSearched, planFacility, boundaryToPopMap, servingPopulation); + } + } + + /** + * Creates a mapping of boundary with it's confirmed target population. + * + * @param censusList Census records for the given list of serving boundaries. + * @return returns a map of boundary with its confirmed target population. + */ + private Map getPopulationMap(List censusList) { + Map boundaryToPopMap = new HashMap<>(); + + for (Census census : censusList) { + Map additionalFieldsMap = census.getAdditionalFields().stream() + .collect(Collectors.toMap(AdditionalField::getKey, AdditionalField::getValue)); + + Long confirmedTargetPopulation = 0L; + + // Get confirmed target population based on campaign type. + if (additionalFieldsMap.containsKey(CONFIRMED_TARGET_POPULATION_AGE_3TO11)) { + confirmedTargetPopulation = additionalFieldsMap.get(CONFIRMED_TARGET_POPULATION_AGE_3TO11) + .add(additionalFieldsMap.get(CONFIRMED_TARGET_POPULATION_AGE_12TO59)) + .longValue(); + } else if(additionalFieldsMap.containsKey(CONFIRMED_TARGET_POPULATION)){ + confirmedTargetPopulation = additionalFieldsMap.get(CONFIRMED_TARGET_POPULATION).longValue(); + } + + // Map the boundary code with it's confirmed target population. + boundaryToPopMap.put(census.getBoundaryCode(), confirmedTargetPopulation); + } + + return boundaryToPopMap; + } + + private void updateServingPopulation(Set boundariesToBeSearched, PlanFacility planFacility, Map boundaryToPopMap, BigDecimal servingPopulation) { + Set currentServiceBoundaries = new HashSet<>(planFacility.getServiceBoundaries()); + Set initialServiceBoundaries = new HashSet<>(planFacility.getInitiallySetServiceBoundaries()); + + for(String boundary : boundariesToBeSearched) { + Long totalPopulation = boundaryToPopMap.get(boundary); + + if (!currentServiceBoundaries.contains(boundary)) { + servingPopulation = servingPopulation.subtract(BigDecimal.valueOf(totalPopulation)); + } else if (!initialServiceBoundaries.contains(boundary)) { + servingPopulation = servingPopulation.add(BigDecimal.valueOf(totalPopulation)); + } + } + Map fieldToUpdate = new HashMap<>(); + fieldToUpdate.put(SERVING_POPULATION_CODE, servingPopulation); + + planFacility.setAdditionalDetails(commonUtil.updateFieldInAdditionalDetails(planFacility.getAdditionalDetails(), fieldToUpdate)); + } + + /** + * Enriches plan facility search request + * + * @param planFacilitySearchRequest + */ + public void enrichSearchRequest(PlanFacilitySearchRequest planFacilitySearchRequest) { + PlanFacilitySearchCriteria planFacilitySearchCriteria = planFacilitySearchRequest.getPlanFacilitySearchCriteria(); + + // Filter map for filtering facility meta data present in additional details + Map filtersMap = new LinkedHashMap<>(); + + // Add facility status as a filter if present in search criteria + if(!ObjectUtils.isEmpty(planFacilitySearchCriteria.getFacilityStatus())) { + filtersMap.put(FACILITY_STATUS_SEARCH_PARAMETER_KEY, planFacilitySearchCriteria.getFacilityStatus()); + } + + // Add facility type as a filter if present in search criteria + if(!ObjectUtils.isEmpty(planFacilitySearchCriteria.getFacilityType())) { + filtersMap.put(FACILITY_TYPE_SEARCH_PARAMETER_KEY, planFacilitySearchCriteria.getFacilityType()); + } + + if(!CollectionUtils.isEmpty(filtersMap)) + planFacilitySearchCriteria.setFiltersMap(filtersMap); + } +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/service/validator/PlanConfigurationValidator.java b/health-services/plan-service/src/main/java/digit/service/validator/PlanConfigurationValidator.java index 22d316b707e..58041c3324d 100644 --- a/health-services/plan-service/src/main/java/digit/service/validator/PlanConfigurationValidator.java +++ b/health-services/plan-service/src/main/java/digit/service/validator/PlanConfigurationValidator.java @@ -1,25 +1,24 @@ package digit.service.validator; +import com.fasterxml.jackson.databind.JsonNode; import com.jayway.jsonpath.JsonPath; -import digit.config.ServiceConstants; import digit.repository.PlanConfigurationRepository; +import digit.util.CampaignUtil; +import digit.util.CommonUtil; import digit.util.MdmsUtil; -import digit.util.ServiceUtil; +import digit.util.MdmsV2Util; import digit.web.models.*; - -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import digit.web.models.mdmsV2.Mdms; +import digit.web.models.projectFactory.CampaignResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.egov.common.utils.MultiStateInstanceUtil; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.stream.Collectors; import static digit.config.ServiceConstants.*; @@ -27,60 +26,96 @@ @Slf4j public class PlanConfigurationValidator { - private MdmsUtil mdmsUtil; + private MdmsV2Util mdmsV2Util; + private PlanConfigurationRepository planConfigRepository; + private CommonUtil commonUtil; - private ServiceUtil serviceUtil; private MultiStateInstanceUtil centralInstanceUtil; - public PlanConfigurationValidator(MdmsUtil mdmsUtil, PlanConfigurationRepository planConfigRepository, ServiceUtil serviceUtil, MultiStateInstanceUtil centralInstanceUtil) { + private CampaignUtil campaignUtil; + + public PlanConfigurationValidator(MdmsUtil mdmsUtil, MdmsV2Util mdmsV2Util, PlanConfigurationRepository planConfigRepository, CommonUtil commonUtil, MultiStateInstanceUtil centralInstanceUtil, CampaignUtil campaignUtil) { this.mdmsUtil = mdmsUtil; + this.mdmsV2Util = mdmsV2Util; this.planConfigRepository = planConfigRepository; - this.serviceUtil = serviceUtil; - this.centralInstanceUtil = centralInstanceUtil; + this.commonUtil = commonUtil; + this.centralInstanceUtil = centralInstanceUtil; + this.campaignUtil = campaignUtil; } /** * Validates the create request for plan configuration, including assumptions against MDMS data. + * * @param request The create request for plan configuration. */ public void validateCreate(PlanConfigurationRequest request) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); String rootTenantId = centralInstanceUtil.getStateLevelTenant(planConfiguration.getTenantId()); Object mdmsData = mdmsUtil.fetchMdmsData(request.getRequestInfo(), rootTenantId); + List mdmsV2Data = mdmsV2Util.fetchMdmsV2Data(request.getRequestInfo(), rootTenantId, MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_SCHEMA_VEHICLE_DETAILS, null); + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(request.getRequestInfo(), request.getPlanConfiguration().getCampaignId(), rootTenantId); + + // Validate if the plan configuration for the provided name and campaign id already exists + validateDuplicateRecord(planConfiguration); + + // Validate if campaign id exists against project factory + validateCampaignId(campaignResponse); // Validate that the assumption keys in the request are present in the MDMS data validateAssumptionKeyAgainstMDMS(request, mdmsData); - // Validate that the assumption values in the plan configuration are correct - validateAssumptionValue(planConfiguration); - - // Validate the filestore ID in the plan configuration's request mappings - validateFilestoreId(planConfiguration); + // Validate that the assumption keys in the request are unique + validateAssumptionUniqueness(planConfiguration); // Validate that the template identifiers in the request match those in the MDMS data validateTemplateIdentifierAgainstMDMS(request, mdmsData); - // Validate that the inputs for operations in the request match those in the MDMS data - validateOperationsInputAgainstMDMS(request, mdmsData); - - // Validate that the resource mappings in the request match those in the MDMS data - validateResourceMappingAgainstMDMS(request, mdmsData); - - // Validate the uniqueness of the 'mappedTo' fields in the resource mappings - validateMappedToUniqueness(planConfiguration.getResourceMapping()); + //Validating operation's input and assumptionValue fields + validateOperations(request, campaignResponse); //Validating plan config name against MDMS data validatePlanConfigName(request, mdmsData); // Validate the user information in the request - validateUserInfo(request); + commonUtil.validateUserInfo(request.getRequestInfo()); + + // Validates the vehicle id from additional details object against the data from mdms v2 + validateVehicleIdsFromAdditionalDetailsAgainstMDMS(request, mdmsV2Data); } + /** + * Validates if plan configuration for the provided name and campaign id already exists + * + * @param planConfiguration the plan configuration from the create request + */ + private void validateDuplicateRecord(PlanConfiguration planConfiguration) { + List planConfigurationsFromSearch = planConfigRepository.search(PlanConfigurationSearchCriteria.builder() + .tenantId(planConfiguration.getTenantId()) + .campaignId(planConfiguration.getCampaignId()) + .name(planConfiguration.getName()) + .build()); + + if (!CollectionUtils.isEmpty(planConfigurationsFromSearch)) { + throw new CustomException(PLAN_CONFIGURATION_ALREADY_EXISTS_CODE, PLAN_CONFIGURATION_ALREADY_EXISTS_MESSAGE); + } + } + + /** + * Validates campaign ID from request against project factory + * + * @param campaignResponse The campaign details response from project factory + */ + private void validateCampaignId(CampaignResponse campaignResponse) { + if (CollectionUtils.isEmpty(campaignResponse.getCampaignDetails())) { + throw new CustomException(NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_CODE, NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_MESSAGE); + } + } + /** * Validates the name of the plan configuration against a regex pattern retrieved from MDMS data. * @@ -89,12 +124,10 @@ public void validateCreate(PlanConfigurationRequest request) { * @throws CustomException if the JSONPath evaluation fails, the name validation list from MDMS is empty, * or the plan configuration name validation fails. */ - public void validatePlanConfigName(PlanConfigurationRequest request, Object mdmsData) - { + public void validatePlanConfigName(PlanConfigurationRequest request, Object mdmsData) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); final String jsonPathForNameValidation = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_NAME_VALIDATION + "[*].data"; - List nameValidationListFromMDMS = null; try { nameValidationListFromMDMS = JsonPath.read(mdmsData, jsonPathForNameValidation); @@ -108,10 +141,9 @@ public void validatePlanConfigName(PlanConfigurationRequest request, Object mdms } String regexPattern = (String) nameValidationListFromMDMS.get(0); - if (!serviceUtil.validateStringAgainstRegex(regexPattern, planConfiguration.getName())) { + if (!commonUtil.validateStringAgainstRegex(regexPattern, planConfiguration.getName())) { throw new CustomException(NAME_VALIDATION_FAILED_CODE, NAME_VALIDATION_FAILED_MESSAGE); } - } @@ -121,226 +153,129 @@ public void validatePlanConfigName(PlanConfigurationRequest request, Object mdms * * @param planConfiguration The plan configuration to validate. */ - public void validateAssumptionValue(PlanConfiguration planConfiguration) { - // Collect all active assumption keys - Set activeAssumptionKeys = planConfiguration.getAssumptions().stream() - .filter(Assumption::getActive) - .map(Assumption::getKey) - .collect(Collectors.toSet()); - - planConfiguration.getOperations().stream() - .filter(Operation::getActive) - .forEach(operation -> { - if (!activeAssumptionKeys.contains(operation.getAssumptionValue())) { - log.error("Assumption Value " + operation.getAssumptionValue() + " is not present in the list of active Assumption Keys"); - throw new CustomException(ASSUMPTION_VALUE_NOT_FOUND_CODE, ASSUMPTION_VALUE_NOT_FOUND_MESSAGE + " - " + operation.getAssumptionValue()); - } - }); - - } /** * Validates the assumption keys against MDMS data. - * @param request The request containing the plan configuration and the MDMS data. + * + * @param request The request containing the plan configuration and the MDMS data. * @param mdmsData The MDMS data. */ public void validateAssumptionKeyAgainstMDMS(PlanConfigurationRequest request, Object mdmsData) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); - final String jsonPathForAssumption = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_ASSUMPTION + "[*].assumptions[*]"; + if (!CollectionUtils.isEmpty(planConfiguration.getAssumptions())) { - List assumptionListFromMDMS = null; - try { - log.info(jsonPathForAssumption); - assumptionListFromMDMS = JsonPath.read(mdmsData, jsonPathForAssumption); - } catch (Exception e) { - log.error(e.getMessage()); - throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); - } + Object additionalDetails = request.getPlanConfiguration().getAdditionalDetails(); + if (additionalDetails == null) { + throw new CustomException(ADDITIONAL_DETAILS_MISSING_CODE, ADDITIONAL_DETAILS_MISSING_MESSAGE); + } - HashSet assumptionSetFromMDMS = new HashSet<>(assumptionListFromMDMS); - planConfiguration.getAssumptions().forEach(assumption -> { - if (!assumptionSetFromMDMS.contains(assumption.getKey())) { + String jsonPathForAssumption = commonUtil.createJsonPathForAssumption((String) commonUtil.extractFieldsFromJsonObject(additionalDetails, JSON_FIELD_CAMPAIGN_TYPE), + (String) commonUtil.extractFieldsFromJsonObject(additionalDetails, JSON_FIELD_DISTRIBUTION_PROCESS), + (String) commonUtil.extractFieldsFromJsonObject(additionalDetails, JSON_FIELD_REGISTRATION_PROCESS), + (String) commonUtil.extractFieldsFromJsonObject(additionalDetails, JSON_FIELD_RESOURCE_DISTRIBUTION_STRATEGY_CODE), + (String) commonUtil.extractFieldsFromJsonObject(additionalDetails, JSON_FIELD_IS_REGISTRATION_AND_DISTRIBUTION_TOGETHER)); + List assumptionListFromMDMS = null; + try { + log.info(jsonPathForAssumption); + assumptionListFromMDMS = JsonPath.read(mdmsData, jsonPathForAssumption); + } catch (Exception e) { + log.error(e.getMessage()); + throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); + } + + Set assumptionSetFromMDMS = new HashSet<>(assumptionListFromMDMS); + planConfiguration.getAssumptions().forEach(assumption -> { + if (assumption.getActive() && assumption.getSource() == Source.MDMS && !assumptionSetFromMDMS.contains(assumption.getKey())) { log.error(ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_MESSAGE + assumption.getKey()); - throw new CustomException(ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_CODE, ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_MESSAGE + " at JSONPath: " + jsonPathForAssumption); + throw new CustomException(ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_CODE, ASSUMPTION_KEY_NOT_FOUND_IN_MDMS_MESSAGE + assumption.getKey() + " at JSONPath: " + jsonPathForAssumption); } - } - ); - + }); + } } /** - * Validates the file store IDs in the provided PlanConfiguration's Resource Mapping list. - * @param planConfiguration The PlanConfiguration to validate. + * Validates the uniqueness of assumption keys in the provided PlanConfiguration. + * If any duplicate keys are found, a CustomException is thrown. + * + * @param planConfig the PlanConfiguration object containing a list of Assumptions to validate + * @throws CustomException if a duplicate assumption key is found */ - public void validateFilestoreId(PlanConfiguration planConfiguration) { - Set fileStoreIds = planConfiguration.getFiles().stream() - .map(File::getFilestoreId) - .collect(Collectors.toSet()); + public void validateAssumptionUniqueness(PlanConfiguration planConfig) { + Set assumptionKeys = new HashSet<>(); - planConfiguration.getResourceMapping().stream().forEach(mapping -> { - if (!fileStoreIds.contains(mapping.getFilestoreId())) { - log.error("Resource Mapping " + mapping.getMappedTo() + " does not have valid fileStoreId " + mapping.getFilestoreId()); - throw new CustomException(FILESTORE_ID_INVALID_CODE, FILESTORE_ID_INVALID_MESSAGE); + for (Assumption assumption : planConfig.getAssumptions()) { + if (assumption.getActive() != Boolean.FALSE) { + if (assumptionKeys.contains(assumption.getKey())) { + throw new CustomException(DUPLICATE_ASSUMPTION_KEY_CODE, DUPLICATE_ASSUMPTION_KEY_MESSAGE + assumption.getKey()); + } + assumptionKeys.add(assumption.getKey()); } - }); - + } } /** * Validates the template identifiers of files in the PlanConfigurationRequest against the list of template identifiers * obtained from MDMS (Master Data Management System) data. * - * @param request The PlanConfigurationRequest containing the PlanConfiguration to validate. - * @param mdmsData The MDMS data containing template identifiers to validate against. + * @param request The PlanConfigurationRequest containing the PlanConfiguration to validate. + * @param mdmsData The MDMS data containing template identifiers to validate against. */ public void validateTemplateIdentifierAgainstMDMS(PlanConfigurationRequest request, Object mdmsData) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); - final String jsonPathForTemplateIdentifier = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_UPLOAD_CONFIGURATION + ".*.id"; - final String jsonPathForTemplateIdentifierIsRequired = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_UPLOAD_CONFIGURATION + "[?(@.required == true)].id"; - - List templateIdentifierListFromMDMS = null; - List requiredTemplateIdentifierFromMDMS = null; - Set activeRequiredTemplates = new HashSet<>(); - - try { - log.info(jsonPathForTemplateIdentifier); - templateIdentifierListFromMDMS = JsonPath.read(mdmsData, jsonPathForTemplateIdentifier); - requiredTemplateIdentifierFromMDMS = JsonPath.read(mdmsData, jsonPathForTemplateIdentifierIsRequired); - } catch (Exception e) { - log.error(e.getMessage()); - throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); - } + if (!CollectionUtils.isEmpty(planConfiguration.getFiles())) { + final String jsonPathForTemplateIdentifier = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_UPLOAD_CONFIGURATION + ".*.id"; + final String jsonPathForTemplateIdentifierIsRequired = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_UPLOAD_CONFIGURATION + "[?(@.required == true)].id"; + + List templateIdentifierListFromMDMS = null; + List requiredTemplateIdentifierFromMDMS = null; + Set activeRequiredTemplates = new HashSet<>(); + + try { + log.info(jsonPathForTemplateIdentifier); + templateIdentifierListFromMDMS = JsonPath.read(mdmsData, jsonPathForTemplateIdentifier); + requiredTemplateIdentifierFromMDMS = JsonPath.read(mdmsData, jsonPathForTemplateIdentifierIsRequired); + } catch (Exception e) { + log.error(e.getMessage()); + throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); + } - HashSet templateIdentifierSetFromMDMS = new HashSet<>(templateIdentifierListFromMDMS); - HashSet requiredTemplateIdentifierSetFromMDMS = new HashSet<>(requiredTemplateIdentifierFromMDMS); + HashSet templateIdentifierSetFromMDMS = new HashSet<>(templateIdentifierListFromMDMS); + HashSet requiredTemplateIdentifierSetFromMDMS = new HashSet<>(requiredTemplateIdentifierFromMDMS); - for(File file : planConfiguration.getFiles()) - { - if(!templateIdentifierSetFromMDMS.contains(file.getTemplateIdentifier())) - { - log.error(TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_MESSAGE + file.getTemplateIdentifier()); - throw new CustomException(TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_CODE, TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_MESSAGE); - } + for (File file : planConfiguration.getFiles()) { + if (!templateIdentifierSetFromMDMS.contains(file.getTemplateIdentifier())) { + log.error(TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_MESSAGE + file.getTemplateIdentifier()); + throw new CustomException(TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_CODE, TEMPLATE_IDENTIFIER_NOT_FOUND_IN_MDMS_MESSAGE); + } - if (file.getActive()) { // Check if the file is active - String templateIdentifier = file.getTemplateIdentifier(); - if (requiredTemplateIdentifierSetFromMDMS.contains(templateIdentifier)) { // Check if the template identifier is required - if (!activeRequiredTemplates.add(templateIdentifier)) { // Ensure only one active file per required template identifier - log.error(ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_MESSAGE + file.getTemplateIdentifier()); - throw new CustomException(ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_CODE, ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_MESSAGE); + if (file.getActive()) { // Check if the file is active + String templateIdentifier = file.getTemplateIdentifier(); + if (requiredTemplateIdentifierSetFromMDMS.contains(templateIdentifier)) { // Check if the template identifier is required + if (!activeRequiredTemplates.add(templateIdentifier)) { // Ensure only one active file per required template identifier + log.error(ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_MESSAGE + file.getTemplateIdentifier()); + throw new CustomException(ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_CODE, ONLY_ONE_FILE_OF_REQUIRED_TEMPLATE_IDENTIFIER_MESSAGE); + } } } } - } - // Ensure at least one active file for each required template identifier - requiredTemplateIdentifierSetFromMDMS - .stream() - .forEach(requiredTemplate -> { + // Ensure at least one active file for each required template identifier + if(commonUtil.isSetupCompleted(planConfiguration)){ + requiredTemplateIdentifierSetFromMDMS.forEach(requiredTemplate -> { if (!activeRequiredTemplates.contains(requiredTemplate)) { log.error("Required Template Identifier " + requiredTemplate + " does not have any active file."); throw new CustomException(REQUIRED_TEMPLATE_IDENTIFIER_NOT_FOUND_CODE, REQUIRED_TEMPLATE_IDENTIFIER_NOT_FOUND_MESSAGE); } }); - - } - - - /** - * Validates the operations input against the Master Data Management System (MDMS) data. - * - * @param request The PlanConfigurationRequest containing the plan configuration and other details. - * @param mdmsData The MDMS data containing the master rule configure inputs. - */ - public void validateOperationsInputAgainstMDMS(PlanConfigurationRequest request, Object mdmsData) { - PlanConfiguration planConfiguration = request.getPlanConfiguration(); - List files = planConfiguration.getFiles(); - List templateIds = files.stream() - .map(File::getTemplateIdentifier) - .collect(Collectors.toList()); - List inputFileTypes = files.stream() - .map(File::getInputFileType) - .map(File.InputFileTypeEnum::toString) - .collect(Collectors.toList()); - - final String jsonPathForRuleInputs = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_SCHEMAS; - List ruleInputsListFromMDMS = null; - try { - log.info(jsonPathForRuleInputs); - ruleInputsListFromMDMS = JsonPath.read(mdmsData, jsonPathForRuleInputs); - } catch (Exception e) { - log.error(e.getMessage()); - throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); - } - - HashSet ruleInputsSetFromMDMS = new HashSet<>(ruleInputsListFromMDMS); - - HashSet allowedColumns = getColumnsFromSchemaThatAreRuleInputs(ruleInputsSetFromMDMS, templateIds, inputFileTypes); - planConfiguration.getOperations().stream() - .map(Operation::getOutput) - .forEach(allowedColumns::add); - - planConfiguration.getOperations().stream().forEach(operation -> { - if (!allowedColumns.contains(operation.getInput())) { - log.error("Input Value " + operation.getInput() + " is not present in MDMS Input List"); - throw new CustomException(INPUT_KEY_NOT_FOUND_CODE, INPUT_KEY_NOT_FOUND_MESSAGE); } - }); - - } - /** - * Filters the Schema MDMS data by type and section - * returns the list of columns which have the property 'isRuleConfigureInputs' as true - * - * @param schemas List of schemas from MDMS - * @param templateIds The list of template identifiers from request object - * @param inputFileTypes The list of input file type from request object - */ - public static HashSet getColumnsFromSchemaThatAreRuleInputs(HashSet schemas, List templateIds, List inputFileTypes) { - if (schemas == null) { - return new HashSet<>(); } - HashSet finalData = new HashSet<>(); - for (Object item : schemas) { - LinkedHashMap schemaEntity = (LinkedHashMap) item; - if(!templateIds.contains(schemaEntity.get(MDMS_SCHEMA_SECTION)) || !inputFileTypes.contains(schemaEntity.get(MDMS_SCHEMA_TYPE))) continue; - LinkedHashMap columns = (LinkedHashMap)((LinkedHashMap) schemaEntity.get(MDMS_SCHEMA_SCHEMA)).get(MDMS_SCHEMA_PROPERTIES); - if(columns == null) return new HashSet<>(); - columns.entrySet().stream() - .forEach(column -> { - LinkedHashMap data = column.getValue(); - if(data.get(MDMS_SCHEMA_PROPERTIES_IS_RULE_CONFIGURE_INPUT)){ - finalData.add(column.getKey()); - } - }); // Add the keys to finalData - } - return finalData; } - - /** - * Validates that the 'mappedTo' values in the list of 'resourceMappings' are unique. - * If a duplicate 'mappedTo' value is found, it logs an error and throws a CustomException. - * - * @param resourceMappings The list of 'ResourceMapping' objects to validate. - * @throws CustomException If a duplicate 'mappedTo' value is found. - */ - public static void validateMappedToUniqueness(List resourceMappings) { - Set uniqueMappedToSet = new HashSet<>(); - resourceMappings.stream().forEach(mapping -> { - String uniqueKey = mapping.getFilestoreId() + "-" + mapping.getMappedTo(); - if (!uniqueMappedToSet.add(uniqueKey)) { - log.error("Duplicate MappedTo " + mapping.getMappedTo() + " for FilestoreId " + mapping.getFilestoreId()); - throw new CustomException(DUPLICATE_MAPPED_TO_VALIDATION_ERROR_CODE, DUPLICATE_MAPPED_TO_VALIDATION_ERROR_MESSAGE + " - " + mapping.getMappedTo() + " for FilestoreId " + mapping.getFilestoreId()); - } - }); - } - - /** * Validates the search request for plan configurations. + * * @param planConfigurationSearchRequest The search request for plan configurations. */ public void validateSearchRequest(PlanConfigurationSearchRequest planConfigurationSearchRequest) { @@ -360,62 +295,60 @@ private void validateSearchCriteria(PlanConfigurationSearchRequest planConfigura /** * Validates the update request for plan configuration, including assumptions against MDMS data. + * * @param request The update request for plan configuration. */ public void validateUpdateRequest(PlanConfigurationRequest request) { PlanConfiguration planConfiguration = request.getPlanConfiguration(); String rootTenantId = centralInstanceUtil.getStateLevelTenant(planConfiguration.getTenantId()); Object mdmsData = mdmsUtil.fetchMdmsData(request.getRequestInfo(), rootTenantId); + List mdmsV2Data = mdmsV2Util.fetchMdmsV2Data(request.getRequestInfo(), rootTenantId, MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_SCHEMA_VEHICLE_DETAILS, null); + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(request.getRequestInfo(), request.getPlanConfiguration().getCampaignId(), rootTenantId); // Validate the existence of the plan configuration in the request validatePlanConfigExistence(request); + // Validate if campaign id exists against project factory + validateCampaignId(campaignResponse); + // Validate that the assumption keys in the request are present in the MDMS data validateAssumptionKeyAgainstMDMS(request, mdmsData); - // Validate that the assumption values in the plan configuration are correct - validateAssumptionValue(planConfiguration); + // Validate that the assumption keys in the request are unique + validateAssumptionUniqueness(planConfiguration); - // Validate the filestore ID in the plan configuration's request mappings - validateFilestoreId(planConfiguration); + //Validating operation's input and assumptionValue fields + validateOperations(request, campaignResponse); // Validate that the template identifiers in the request match those in the MDMS data validateTemplateIdentifierAgainstMDMS(request, mdmsData); - // Validate that the inputs for operations in the request match those in the MDMS data - validateOperationsInputAgainstMDMS(request, mdmsData); - - // Validate the dependencies between operations in the plan configuration - validateOperationDependencies(planConfiguration); - - // Validate that the resource mappings in the request match those in the MDMS data - validateResourceMappingAgainstMDMS(request, mdmsData); - - // Validate the uniqueness of the 'mappedTo' fields in the resource mappings - validateMappedToUniqueness(planConfiguration.getResourceMapping()); - //Validating plan config name against MDMS data validatePlanConfigName(request, mdmsData); // Validate the user information in the request - validateUserInfo(request); + commonUtil.validateUserInfo(request.getRequestInfo()); + + // Validates the vehicle id from additional details object against the data from mdms v2 + validateVehicleIdsFromAdditionalDetailsAgainstMDMS(request, mdmsV2Data); + } /** * Validates the existence of the plan configuration in the repository. + * * @param request The request containing the plan configuration to validate. */ - public PlanConfiguration validatePlanConfigExistence(PlanConfigurationRequest request) { + public void validatePlanConfigExistence(PlanConfigurationRequest request) { // If plan id provided is invalid, throw an exception List planConfigurationList = planConfigRepository.search(PlanConfigurationSearchCriteria.builder() .id(request.getPlanConfiguration().getId()) .build()); - if(CollectionUtils.isEmpty(planConfigurationList)) { + if (CollectionUtils.isEmpty(planConfigurationList)) { throw new CustomException(INVALID_PLAN_CONFIG_ID_CODE, INVALID_PLAN_CONFIG_ID_MESSAGE); } - return planConfigurationList.get(0); } /** @@ -426,103 +359,231 @@ public PlanConfiguration validatePlanConfigExistence(PlanConfigurationRequest re * @throws CustomException If an inactive operation's output is used as input in any other active operation. */ public static void validateOperationDependencies(PlanConfiguration planConfiguration) { - // Collect all active operations' inputs - Set activeInputs = planConfiguration.getOperations().stream() - .filter(Operation::getActive) - .map(Operation::getInput) - .collect(Collectors.toSet()); + if (!CollectionUtils.isEmpty(planConfiguration.getOperations())) { + // Collect all active operations' inputs + Set activeInputs = planConfiguration.getOperations().stream() + .filter(Operation::getActive) + .map(Operation::getInput) + .collect(Collectors.toSet()); + + // Check for each inactive operation + planConfiguration.getOperations().forEach(operation -> { + if (!operation.getActive() && activeInputs.contains(operation.getOutput())) { + log.error(INACTIVE_OPERATION_USED_AS_INPUT_MESSAGE + operation.getOutput()); + throw new CustomException(INACTIVE_OPERATION_USED_AS_INPUT_CODE, INACTIVE_OPERATION_USED_AS_INPUT_MESSAGE + operation.getOutput()); + } + }); + } + } - // Check for each inactive operation - planConfiguration.getOperations().stream().forEach(operation -> { - if (!operation.getActive() && activeInputs.contains(operation.getOutput())) { - log.error(INACTIVE_OPERATION_USED_AS_INPUT_MESSAGE + operation.getOutput()); - throw new CustomException(INACTIVE_OPERATION_USED_AS_INPUT_CODE, INACTIVE_OPERATION_USED_AS_INPUT_MESSAGE + operation.getOutput()); - } - }); + + /** + * Validates Vehicle ids from additional details against MDMS V2 + * + * @param request plan configuration request + * @param mdmsV2Data mdms v2 data object + */ + public void validateVehicleIdsFromAdditionalDetailsAgainstMDMS(PlanConfigurationRequest request, List mdmsV2Data) { + List vehicleIdsfromAdditionalDetails = commonUtil.extractFieldsFromJsonObject(request.getPlanConfiguration().getAdditionalDetails(), JSON_FIELD_VEHICLE_ID, List.class); + if (!CollectionUtils.isEmpty(vehicleIdsfromAdditionalDetails)) { + List vehicleIdsFromMdms = mdmsV2Data.stream() + .map(Mdms::getId) + .toList(); + + vehicleIdsfromAdditionalDetails.forEach(vehicleId -> { + if (!vehicleIdsFromMdms.contains(vehicleId)) { + log.error("Vehicle Id " + vehicleId + " is not present in MDMS"); + throw new CustomException(VEHICLE_ID_NOT_FOUND_IN_MDMS_CODE, VEHICLE_ID_NOT_FOUND_IN_MDMS_MESSAGE); + } + }); + } } - /** - * Validate input (BCode) against MDMS data. - * @param request plan configauration request. - * @param mdmsData MDMS data object. - */ - public void validateResourceMappingAgainstMDMS(PlanConfigurationRequest request, Object mdmsData) { - PlanConfiguration planConfiguration = request.getPlanConfiguration(); - List files = planConfiguration.getFiles(); - List templateIds = files.stream() - .map(File::getTemplateIdentifier) - .toList(); - List inputFileTypes = files.stream() - .map(File::getInputFileType) - .map(File.InputFileTypeEnum::toString) - .toList(); - - final String jsonPathForRuleInputs = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_SCHEMAS; - List ruleInputsListFromMDMS = null; - try { - log.info(jsonPathForRuleInputs); - ruleInputsListFromMDMS = JsonPath.read(mdmsData, jsonPathForRuleInputs); - } catch (Exception e) { - log.error(e.getMessage()); - throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); + /** + * Checks if files are empty or null when the setup action is marked as completed. + * + * @param planConfiguration The plan configuration to check. + */ + private void checkForEmptyFiles(PlanConfiguration planConfiguration) { + if (CollectionUtils.isEmpty(planConfiguration.getFiles())) { + log.error("Files cannot be empty at action = " + SETUP_COMPLETED_ACTION); + throw new CustomException(FILES_NOT_FOUND_CODE, FILES_NOT_FOUND_MESSAGE); } - HashSet ruleInputsSetFromMDMS = new HashSet<>(ruleInputsListFromMDMS); - HashSet requiredColumns = getRequiredColumnsFromSchema(ruleInputsSetFromMDMS, templateIds, inputFileTypes); - List resourceMappings = planConfiguration.getResourceMapping(); + } - // Throw a custom exception if no active mappings with BOUNDARY_CODE are found - if(requiredColumns.contains(ServiceConstants.BOUNDARY_CODE)) { - boolean exists = resourceMappings.stream() - .anyMatch(mapping -> mapping.getActive() && mapping.getMappedTo().equals(ServiceConstants.BOUNDARY_CODE)); + /** + * Checks if assumptions are empty or null when the setup action is marked as completed. + * + * @param planConfiguration The plan configuration to check. + */ + private void checkForEmptyAssumption(PlanConfiguration planConfiguration) { + if (CollectionUtils.isEmpty(planConfiguration.getAssumptions())) { + log.error("Assumptions cannot be empty at action = " + SETUP_COMPLETED_ACTION); + throw new CustomException(ASSUMPTIONS_NOT_FOUND_CODE, ASSUMPTIONS_NOT_FOUND_MESSAGE); + } + } - if (!exists) { - throw new CustomException(BOUNDARY_CODE_MAPPING_NOT_FOUND_CODE, BOUNDARY_CODE_MAPPING_NOT_FOUND_MESSAGE); - } + /** + * Checks if operations are empty or null when the setup action is marked as completed. + * + * @param planConfiguration The plan configuration to check. + */ + private void checkForEmptyOperation(PlanConfiguration planConfiguration) { + if (CollectionUtils.isEmpty(planConfiguration.getOperations())) { + log.error("Operations cannot be empty at action = " + SETUP_COMPLETED_ACTION); + throw new CustomException(OPERATIONS_NOT_FOUND_CODE, OPERATIONS_NOT_FOUND_MESSAGE); } + } + + /** + * Validates the inputs and assumption values in the plan configuration after verifying the setup action is completed. + * + * @param request The plan configuration request to validate. + * @param campaignResponse The campaign response containing details for validation. + */ + public void validateOperations(PlanConfigurationRequest request, CampaignResponse campaignResponse) { + PlanConfiguration planConfiguration = request.getPlanConfiguration(); + + if (commonUtil.isSetupCompleted(planConfiguration)) { + performEmptyChecks(planConfiguration); + + // Get shared data upfront + HashSet allowedColumns = getAllowedColumnsFromMDMS( + request, campaignResponse.getCampaignDetails().get(0).getProjectType() + ); + Set activeAssumptionKeys = getActiveAssumptionKeys(planConfiguration); + validateOperationInputs(planConfiguration, allowedColumns, activeAssumptionKeys); + validateOperationAssumptionValues(planConfiguration, allowedColumns, activeAssumptionKeys); } + } + + /** + * Performs checks for empty files, assumptions, and operations in the plan configuration. + * + * @param planConfiguration The plan configuration to check. + */ + private void performEmptyChecks(PlanConfiguration planConfiguration) { + checkForEmptyFiles(planConfiguration); + checkForEmptyOperation(planConfiguration); + checkForEmptyAssumption(planConfiguration); + } /** - * Filters the Schema MDMS data by type and section - * returns the list of columns which have the property 'isRequired' as true + * Retrieves allowed columns based on MDMS data and campaign type. * - * @param schemas List of schemas from MDMS - * @param templateIds The list of template identifiers from request object - * @param inputFileTypes The list of input file type from request object - * @return List of Columns that are required + * @param request The plan configuration request containing tenant information. + * @param campaignType The type of campaign for which allowed columns are fetched. + * @return A set of allowed column names. */ - public static HashSet getRequiredColumnsFromSchema(HashSet schemas, List templateIds, List inputFileTypes) { - if (CollectionUtils.isEmpty(schemas)) { - return new HashSet<>(); + private HashSet getAllowedColumnsFromMDMS(PlanConfigurationRequest request, String campaignType) { + String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlanConfiguration().getTenantId()); + String uniqueIndentifier = BOUNDARY + DOT_SEPARATOR + MICROPLAN_PREFIX + campaignType; + List mdmsV2Data = mdmsV2Util.fetchMdmsV2Data(request.getRequestInfo(), rootTenantId, MDMS_ADMIN_CONSOLE_MODULE_NAME + DOT_SEPARATOR + MDMS_SCHEMA_ADMIN_SCHEMA, uniqueIndentifier); + List columnNameList = extractPropertyNamesFromAdminSchema(mdmsV2Data.get(0).getData()); + return new HashSet<>(columnNameList); + } + + /** + * Extracts the names of properties defined within the "numberProperties" and "stringProperties" arrays from admin schema. + * + * @param rootNode The root JSON node from which to extract property names. + * @return A list of property names found in "numberProperties" and "stringProperties". + */ + public List extractPropertyNamesFromAdminSchema(JsonNode rootNode) { + List names = new ArrayList<>(); + + // Access the "properties" node directly from the root node + JsonNode propertiesNode = rootNode.path(PROPERTIES); + + // Extract names from "numberProperties" + JsonNode numberProperties = propertiesNode.path(NUMBER_PROPERTIES); + if (numberProperties.isArray()) { + for (JsonNode property : numberProperties) { + String name = property.path(NAME).asText(null); + if (name != null) { + names.add(name); + } + } } - HashSet finalData = new HashSet<>(); - for (Object item : schemas) { - LinkedHashMap schemaEntity = (LinkedHashMap) item; - if(!templateIds.contains(schemaEntity.get(MDMS_SCHEMA_SECTION)) || !inputFileTypes.contains(schemaEntity.get(MDMS_SCHEMA_TYPE))) continue; - LinkedHashMap columns = (LinkedHashMap)((LinkedHashMap) schemaEntity.get(MDMS_SCHEMA_SCHEMA)).get(MDMS_SCHEMA_PROPERTIES); - if (columns == null) return new HashSet<>(); - columns.entrySet().stream().forEach(column -> { - LinkedHashMap data = column.getValue(); - if (data.get(MDMS_SCHEMA_PROPERTIES_IS_REQUIRED)) { - finalData.add(column.getKey()); + + // Extract names from "stringProperties" + JsonNode stringProperties = propertiesNode.path(STRING_PROPERTIES); + if (stringProperties.isArray()) { + for (JsonNode property : stringProperties) { + String name = property.path(NAME).asText(null); + if (name != null) { + names.add(name); } - }); + } + } + + return names; + } + + /** + * Gets keys of active assumptions in the plan configuration. + * + * @param planConfiguration The plan configuration containing assumptions. + * @return A set of keys of active assumptions. + */ + private Set getActiveAssumptionKeys(PlanConfiguration planConfiguration) { + return planConfiguration.getAssumptions().stream() + .filter(Assumption::getActive) + .map(Assumption::getKey) + .collect(Collectors.toSet()); + } + + /** + * Validates the input values of operations against allowed columns and previous outputs. + * + * @param planConfiguration The plan configuration containing operations. + * @param allowedColumns The allowed column names for input validation. + */ + private void validateOperationInputs(PlanConfiguration planConfiguration, HashSet allowedColumns, Set activeAssumptionKeys) { + // Set to keep track of previous outputs + Set previousOutputs = new HashSet<>(); + + for (Operation operation : planConfiguration.getOperations()) { + // Validate input + if (operation.getActive() && !allowedColumns.contains(operation.getInput()) && !activeAssumptionKeys.contains(operation.getInput()) && !previousOutputs.contains(operation.getInput()) && operation.getSource() == Source.MDMS) { + log.error("Input Value " + operation.getInput() + " is not present in allowed columns or previous outputs"); + throw new CustomException(INPUT_KEY_NOT_FOUND_CODE, INPUT_KEY_NOT_FOUND_MESSAGE + operation.getInput()); + } + + // Add current operation's output to previousOutputs if it's active + if (operation.getActive()) { + previousOutputs.add(operation.getOutput()); + } } - return finalData; } /** - * Validates the user information within the provided PlanConfigurationRequest. + * Validates the assumption values of operations against allowed columns, active keys, and previous outputs. * - * @param request the PlanConfigurationRequest containing the user information to be validated - * @throws CustomException if the user information is missing in the request + * @param planConfiguration The plan configuration containing operations. + * @param allowedColumns The allowed column names for assumption validation. + * @param activeAssumptionKeys The set of active assumption keys. */ - public void validateUserInfo(PlanConfigurationRequest request) - { - if (ObjectUtils.isEmpty(request.getRequestInfo().getUserInfo())) { - log.error(USERINFO_MISSING_MESSAGE); - throw new CustomException(USERINFO_MISSING_CODE, USERINFO_MISSING_MESSAGE); + private void validateOperationAssumptionValues(PlanConfiguration planConfiguration, HashSet allowedColumns, Set activeAssumptionKeys) { + // Set to keep track of previous outputs + Set previousOutputs = new HashSet<>(); + + for (Operation operation : planConfiguration.getOperations()) { + String assumptionValue = operation.getAssumptionValue(); + + // Validate assumption value + if (operation.getActive() && !allowedColumns.contains(assumptionValue) && !activeAssumptionKeys.contains(assumptionValue) && !previousOutputs.contains(assumptionValue) && operation.getSource() == Source.MDMS) { + log.error("Assumption Value " + assumptionValue + " is not present in allowed columns, previous outputs, or active Assumption Keys"); + throw new CustomException(ASSUMPTION_VALUE_NOT_FOUND_CODE, ASSUMPTION_VALUE_NOT_FOUND_MESSAGE + " - " + assumptionValue); + } + + // Add current operation's output to previousOutputs if it's active + if (operation.getActive() && operation.getSource() == Source.MDMS) { + previousOutputs.add(operation.getOutput()); + } } } + } diff --git a/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java b/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java new file mode 100644 index 00000000000..772ec6dd133 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java @@ -0,0 +1,349 @@ +package digit.service.validator; + +import digit.config.Configuration; +import digit.repository.PlanEmployeeAssignmentRepository; +import digit.util.*; +import digit.web.models.*; +import digit.web.models.projectFactory.Boundary; +import digit.web.models.projectFactory.CampaignDetail; +import digit.web.models.projectFactory.CampaignResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.request.Role; +import org.egov.common.contract.user.UserDetailResponse; +import org.egov.common.utils.MultiStateInstanceUtil; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class PlanEmployeeAssignmentValidator { + + private MultiStateInstanceUtil centralInstanceUtil; + + private MdmsUtil mdmsUtil; + + private UserUtil userUtil; + + private CommonUtil commonUtil; + + private CampaignUtil campaignUtil; + + private PlanEmployeeAssignmentRepository repository; + + private Configuration config; + + public PlanEmployeeAssignmentValidator(MultiStateInstanceUtil centralInstanceUtil, MdmsUtil mdmsUtil, UserUtil userUtil, CommonUtil commonUtil, CampaignUtil campaignUtil, PlanEmployeeAssignmentRepository repository, Configuration config) { + this.centralInstanceUtil = centralInstanceUtil; + this.mdmsUtil = mdmsUtil; + this.userUtil = userUtil; + this.commonUtil = commonUtil; + this.campaignUtil = campaignUtil; + this.repository = repository; + this.config = config; + } + + /** + * This method validates the create request for plan employee assignment. + * + * @param request The create request for plan employee assignment + */ + public void validateCreate(PlanEmployeeAssignmentRequest request) { + PlanEmployeeAssignment planEmployeeAssignment = request.getPlanEmployeeAssignment(); + String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlanEmployeeAssignment().getTenantId()); + List planConfigurations = commonUtil.searchPlanConfigId(planEmployeeAssignment.getPlanConfigurationId(), rootTenantId); + UserDetailResponse userDetailResponse = userUtil.fetchUserDetail(userUtil.getUserSearchReq(request.getRequestInfo(), planEmployeeAssignment.getEmployeeId(), planEmployeeAssignment.getTenantId())); + + // Validate if a same assignment already exists + validateDuplicateRecord(request); + + // Validate if plan config id exists + validatePlanConfigId(planConfigurations); + + // Validate role of employee against User Service + validateRoleAgainstUserService(planEmployeeAssignment, userDetailResponse); + + // Validate if role of employee is a conflicting role + validateRoleConflict(planEmployeeAssignment); + + // Validate campaign id, employee jurisdiction and highest root jurisdiction in case of Root role + validateCampaignDetails(planConfigurations.get(0).getCampaignId(), rootTenantId, request); + + } + + /** + * This method validates the provided roles of the employee against User Service + * + * @param planEmployeeAssignment The plan employee assignment provided in request + * @param userDetailResponse The user detail response from user service for the provided employeeId + */ + private void validateRoleAgainstUserService(PlanEmployeeAssignment planEmployeeAssignment, UserDetailResponse userDetailResponse) { + + // Validate if employee exists against User Service + if (CollectionUtils.isEmpty(userDetailResponse.getUser())) { + throw new CustomException(INVALID_EMPLOYEE_ID_CODE, INVALID_EMPLOYEE_ID_MESSAGE); + } + + List userRolesFromUserService = userDetailResponse.getUser().get(0).getRoles().stream() + .map(Role::getCode) + .toList(); + + if (!userRolesFromUserService.contains(planEmployeeAssignment.getRole())) { + throw new CustomException(INVALID_EMPLOYEE_ROLE_CODE, INVALID_EMPLOYEE_ROLE_MESSAGE); + } + } + + /** + * Validates if the plan employee assignment for the provided details already exists + * + * @param request the employee assignment create request + */ + private void validateDuplicateRecord(PlanEmployeeAssignmentRequest request) { + PlanEmployeeAssignment employeeAssignment = request.getPlanEmployeeAssignment(); + + List planEmployeeAssignmentsFromSearch = repository.search(PlanEmployeeAssignmentSearchCriteria.builder() + .tenantId(employeeAssignment.getTenantId()) + .planConfigurationId(employeeAssignment.getPlanConfigurationId()) + .employeeId(Collections.singletonList(employeeAssignment.getEmployeeId())) + .role(Collections.singletonList(employeeAssignment.getRole())) + .build()); + + if (!CollectionUtils.isEmpty(planEmployeeAssignmentsFromSearch)) { + throw new CustomException(PLAN_EMPLOYEE_ASSIGNMENT_ALREADY_EXISTS_CODE, PLAN_EMPLOYEE_ASSIGNMENT_ALREADY_EXISTS_MESSAGE); + } + } + + /** + * Validates that employee with National role is assigned to the highest root jurisdiction only against MDMS + * + * @param planEmployeeAssignment The plan employee assignment provided in request + * @param mdmsData mdms data from mdms v2 + * @param campaignDetail the campaign details for the corresponding campaign id + */ + private void validateRootEmployeeJurisdiction(PlanEmployeeAssignment planEmployeeAssignment, Object mdmsData, CampaignDetail campaignDetail) { + if (planEmployeeAssignment.getRole().contains(ROOT_PREFIX)) { + Set jurisdiction = planEmployeeAssignment.getJurisdiction(); + + // Validate that National role employee should not have more than one jurisdiction assigned + if (jurisdiction.size() > 1) { + throw new CustomException(INVALID_ROOT_EMPLOYEE_JURISDICTION_CODE, INVALID_ROOT_EMPLOYEE_JURISDICTION_MESSAGE); + } + + String rootLevelJurisdiction = jurisdiction.stream().findFirst().orElse(null); + + // Fetch the highest hierarchy for Microplan from MDMS + String highestHierarchy = commonUtil.getMicroplanHierarchy(mdmsData).get(HIGHEST_HIERARCHY_FIELD_FOR_MICROPLAN); + + // Filter out the boundary details for the jurisdiction assigned to employee + // Throw exception if jurisdiction assigned to Root role employee is not the highest hierarchy + campaignDetail.getBoundaries().stream() + .filter(boundary -> boundary.getCode().equals(rootLevelJurisdiction)) + .forEach(boundary -> { + if (!boundary.getType().toLowerCase().equals(highestHierarchy)) { + throw new CustomException(INVALID_ROOT_EMPLOYEE_JURISDICTION_CODE, INVALID_ROOT_EMPLOYEE_JURISDICTION_MESSAGE); + } + }); + } + } + + /** + * This method checks if the employee's role provided is a conflicting role against the role map. + * + * @param planEmployeeAssignment The plan employee assignment provided in request + */ + private void validateRoleConflict(PlanEmployeeAssignment planEmployeeAssignment) { + + // Fetch the role mappings from the configuration + Map roleMap = config.getRoleMap(); + + // Check if the role of the employee exists in the role map + if (roleMap.containsKey(planEmployeeAssignment.getRole())) { + + // Fetch existing role assignments for the employee based on their tenant, planConfig Id, and employee ID + // The search is conducted using the conflicting role + List response = repository.search(PlanEmployeeAssignmentSearchCriteria.builder() + .tenantId(planEmployeeAssignment.getTenantId()) + .planConfigurationId(planEmployeeAssignment.getPlanConfigurationId()) + .employeeId(Collections.singletonList(planEmployeeAssignment.getEmployeeId())) + .role(Collections.singletonList(roleMap.get(planEmployeeAssignment.getRole()))).build()); + + // If there are any conflicting assignments found, throw a custom exception + if (!CollectionUtils.isEmpty(response)) { + throw new CustomException(INVALID_EMPLOYEE_ROLE_CODE, INVALID_EMPLOYEE_ROLE_MESSAGE); + } + } + } + + /** + * This method validates campaign id and employee's jurisdiction against project factory + * If the employee has a national role, it validates that the employee has the highest root jurisdiction only + * + * @param campaignId the campaign id corresponding to the plan config id provided in the request + * @param tenantId the tenant id provided in the request + * @param planEmployeeAssignmentRequest the plan employee assignment request provided + */ + private void validateCampaignDetails(String campaignId, String tenantId, PlanEmployeeAssignmentRequest planEmployeeAssignmentRequest) { + PlanEmployeeAssignment planEmployeeAssignment = planEmployeeAssignmentRequest.getPlanEmployeeAssignment(); + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(planEmployeeAssignmentRequest.getRequestInfo(), campaignId, tenantId); + Object mdmsData = mdmsUtil.fetchMdmsData(planEmployeeAssignmentRequest.getRequestInfo(), tenantId); + + // Validate if campaign id exists against project factory + validateCampaignId(campaignResponse); + + // Validate the provided jurisdiction for the plan employee assignment + validateEmployeeAssignmentJurisdiction(campaignResponse.getCampaignDetails().get(0), planEmployeeAssignment); + + // Validates the jurisdiction assigned to Root role employee against MDMS + validateRootEmployeeJurisdiction(planEmployeeAssignment, mdmsData, campaignResponse.getCampaignDetails().get(0)); + + // Validates the jurisdiction assigned to a non-Root employee against MDMS + validateEmployeeJurisdiction(planEmployeeAssignment, mdmsData, campaignResponse.getCampaignDetails().get(0)); + } + + /** + * Validates that a non-Root role employee is not assigned to the highest or lowest hierarchy against MDMS + * + * @param planEmployeeAssignment The plan employee assignment provided in request + * @param mdmsData mdms data from mdms v2 + * @param campaignDetail the campaign details for the corresponding campaign id + */ + private void validateEmployeeJurisdiction(PlanEmployeeAssignment planEmployeeAssignment, Object mdmsData, CampaignDetail campaignDetail) { + if (!planEmployeeAssignment.getRole().contains(ROOT_PREFIX)) { + Set jurisdiction = planEmployeeAssignment.getJurisdiction(); + + // Fetch the highest and lowest hierarchy for Microplan from MDMS + Map hierarchyMap = commonUtil.getMicroplanHierarchy(mdmsData); + String lowestHierarchy = hierarchyMap.get(LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN); + String highestHierarchy = hierarchyMap.get(HIGHEST_HIERARCHY_FIELD_FOR_MICROPLAN); + + // Filter out the boundary details for the jurisdiction assigned to employee + // Simultaneously validating if employee is assigned to lowest or highest hierarchy + campaignDetail.getBoundaries().stream() + .filter(boundary -> jurisdiction.contains(boundary.getCode())) + .forEach(boundary -> { + if (boundary.getType().toLowerCase().equals(lowestHierarchy) || + boundary.getType().toLowerCase().equals(highestHierarchy)) { + throw new CustomException(INVALID_EMPLOYEE_JURISDICTION_CODE, INVALID_EMPLOYEE_JURISDICTION_MESSAGE); + } + }); + } + } + + /** + * This method validates if employee's jurisdiction exist in campaign details + * + * @param campaignDetail the campaign details for the corresponding campaign id + * @param planEmployeeAssignment the plan employee assignment provided in request + */ + private void validateEmployeeAssignmentJurisdiction(CampaignDetail campaignDetail, PlanEmployeeAssignment planEmployeeAssignment) { + + // Collect all boundary code for the campaign + Set boundaryCode = campaignDetail.getBoundaries().stream() + .filter(boundary -> planEmployeeAssignment.getHierarchyLevel().equals(boundary.getType())) + .map(Boundary::getCode) + .collect(Collectors.toSet()); + + if(CollectionUtils.isEmpty(boundaryCode)) { + throw new CustomException(INVALID_HIERARCHY_LEVEL_CODE, INVALID_HIERARCHY_LEVEL_MESSAGE); + } + + planEmployeeAssignment.getJurisdiction() + .forEach(jurisdiction -> { + if (!boundaryCode.contains(jurisdiction)) { + throw new CustomException(INVALID_JURISDICTION_CODE, INVALID_JURISDICTION_MESSAGE); + } + }); + + } + + /** + * This method validates if the campaign id provided in the request exists + * + * @param campaignResponse The campaign details response from project factory + */ + private void validateCampaignId(CampaignResponse campaignResponse) { + if (CollectionUtils.isEmpty(campaignResponse.getCampaignDetails())) { + throw new CustomException(NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_CODE, NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_MESSAGE); + } + } + + /** + * This method validates if the plan configuration id provided in the request exists + * + * @param planConfigurations The list of plan configuration for the provided plan config id + */ + private void validatePlanConfigId(List planConfigurations) { + if (CollectionUtils.isEmpty(planConfigurations)) { + throw new CustomException(INVALID_PLAN_CONFIG_ID_CODE, INVALID_PLAN_CONFIG_ID_MESSAGE); + } + } + + /** + * Validates the search request for plan employee assignment + * + * @param request the request to search plan employee assignment + */ + public void validateSearch(PlanEmployeeAssignmentSearchRequest request) { + PlanEmployeeAssignmentSearchCriteria searchCriteria = request.getPlanEmployeeAssignmentSearchCriteria(); + if (Objects.isNull(searchCriteria)) { + throw new CustomException(SEARCH_CRITERIA_EMPTY_CODE, SEARCH_CRITERIA_EMPTY_MESSAGE); + } + + if (StringUtils.isEmpty(searchCriteria.getTenantId())) { + throw new CustomException(TENANT_ID_EMPTY_CODE, TENANT_ID_EMPTY_MESSAGE); + } + + if (StringUtils.isEmpty(searchCriteria.getPlanConfigurationId())) { + throw new CustomException(PLAN_CONFIG_ID_EMPTY_CODE, PLAN_CONFIG_ID_EMPTY_MESSAGE); + } + } + + /** + * This method validates the update request for plan employee assignment. + * + * @param request The update request for plan employee assignment. + */ + public void validateUpdate(PlanEmployeeAssignmentRequest request) { + PlanEmployeeAssignment planEmployeeAssignment = request.getPlanEmployeeAssignment(); + String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlanEmployeeAssignment().getTenantId()); + List planConfigurations = commonUtil.searchPlanConfigId(planEmployeeAssignment.getPlanConfigurationId(), rootTenantId); + + // Validate if Plan employee assignment exists + validatePlanEmployeeAssignmentExistance(planEmployeeAssignment); + + // Validate campaign id and employee jurisdiction + validateCampaignDetails(planConfigurations.get(0).getCampaignId(), rootTenantId, request); + + } + + /** + * This method validates if the plan employee assignment provided in the update request exists + * + * @param planEmployeeAssignment The plan employee assignment details from the request + */ + private void validatePlanEmployeeAssignmentExistance(PlanEmployeeAssignment planEmployeeAssignment) { + if (ObjectUtils.isEmpty(planEmployeeAssignment.getId())) { + throw new CustomException(PLAN_EMPLOYEE_ASSIGNMENT_ID_EMPTY_CODE, PLAN_EMPLOYEE_ASSIGNMENT_ID_EMPTY_MESSAGE); + } + + // Validates the existence of plan employee assignment + List planEmployeeAssignments = repository.search(PlanEmployeeAssignmentSearchCriteria.builder() + .tenantId(planEmployeeAssignment.getTenantId()) + .id(planEmployeeAssignment.getId()) + .role(Collections.singletonList(planEmployeeAssignment.getRole())) + .planConfigurationId(planEmployeeAssignment.getPlanConfigurationId()) + .employeeId(Collections.singletonList(planEmployeeAssignment.getEmployeeId())) + .build()); + + if (CollectionUtils.isEmpty(planEmployeeAssignments)) { + throw new CustomException(INVALID_PLAN_EMPLOYEE_ASSIGNMENT_CODE, INVALID_PLAN_EMPLOYEE_ASSIGNMENT_MESSAGE); + } + } + +} diff --git a/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java b/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java new file mode 100644 index 00000000000..dd7b251eaf1 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java @@ -0,0 +1,301 @@ +package digit.service.validator; + +import com.jayway.jsonpath.JsonPath; +import digit.repository.PlanConfigurationRepository; +import digit.repository.PlanFacilityRepository; +import digit.service.enrichment.PlanFacilityEnricher; +import digit.util.*; +import digit.web.models.*; +import digit.web.models.facility.Facility; +import digit.web.models.facility.FacilityResponse; +import digit.web.models.projectFactory.CampaignDetail; +import digit.web.models.projectFactory.CampaignResponse; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.utils.MultiStateInstanceUtil; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import static digit.config.ServiceConstants.*; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +import digit.web.models.projectFactory.Boundary; + +@Component +@Slf4j +public class PlanFacilityValidator { + private PlanFacilityRepository planFacilityRepository; + private PlanConfigurationRepository planConfigurationRepository; + private CampaignUtil campaignUtil; + private MultiStateInstanceUtil centralInstanceUtil; + private MdmsUtil mdmsUtil; + private FacilityUtil facilityUtil; + private CommonUtil commonUtil; + private PlanFacilityEnricher enrichment; + + public PlanFacilityValidator(PlanFacilityRepository planFacilityRepository, PlanConfigurationRepository planConfigurationRepository, CampaignUtil campaignUtil, MultiStateInstanceUtil centralInstanceUtil, MdmsUtil mdmsUtil, FacilityUtil facilityUtil, CommonUtil commonUtil, PlanFacilityEnricher enrichment) { + this.planFacilityRepository = planFacilityRepository; + this.planConfigurationRepository = planConfigurationRepository; + this.campaignUtil = campaignUtil; + this.centralInstanceUtil = centralInstanceUtil; + this.mdmsUtil = mdmsUtil; + this.facilityUtil = facilityUtil; + this.commonUtil = commonUtil; + this.enrichment = enrichment; + } + + /** + * This method validates the Plan Facility Create request. + * It performs multiple validations such as plan configuration, facility existence, + * and campaign-related validations. + * + * @param planFacilityRequest + */ + public void validatePlanFacilityCreate(@Valid PlanFacilityRequest planFacilityRequest) { + // Retrieve the root-level tenant ID (state-level) based on the facility's tenant ID + String rootTenantId = centralInstanceUtil.getStateLevelTenant(planFacilityRequest.getPlanFacility().getTenantId()); + + // Validate duplicate records for plan facility + validateDuplicateRecords(planFacilityRequest); + + // Validate PlanConfiguration Existence and fetch the plan configuration details using the PlanConfigurationId + List planConfigurations = fetchPlanConfigurationById(planFacilityRequest.getPlanFacility().getPlanConfigurationId(), rootTenantId); + + // Validate facility existence + validateFacilityExistence(planFacilityRequest); + + // Validate service boundaries and residing boundaries with campaign id + validateCampaignDetails(planConfigurations.get(0).getCampaignId(), rootTenantId, planFacilityRequest); + } + + /** + * Validates if plan facility linkage for the provided planConfiguration id and facility id already exists + * + * @param planFacilityRequest The plan facility linkage create request + */ + private void validateDuplicateRecords(@Valid PlanFacilityRequest planFacilityRequest) { + PlanFacility planFacility = planFacilityRequest.getPlanFacility(); + + PlanFacilitySearchCriteria searchCriteria = PlanFacilitySearchCriteria.builder().planConfigurationId(planFacility.getPlanConfigurationId()).facilityId(planFacility.getFacilityId()).build(); + + List planFacilityList = planFacilityRepository.search(searchCriteria); + + if (!CollectionUtils.isEmpty(planFacilityList)) { + throw new CustomException(PLAN_FACILITY_LINKAGE_ALREADY_EXISTS_CODE, PLAN_FACILITY_LINKAGE_ALREADY_EXISTS_MESSAGE); + } + } + + /** + * This method validates the Plan Facility Update request. + * It performs multiple validations such as plan facility existence + * and campaign-related validations. + * + * @param planFacilityRequest + */ + public void validatePlanFacilityUpdate(PlanFacilityRequest planFacilityRequest) { + String rootTenantId = centralInstanceUtil.getStateLevelTenant(planFacilityRequest.getPlanFacility().getTenantId()); + + //validate plan facility existence + validatePlanFacilityExistence(planFacilityRequest); + + List planConfigurations = fetchPlanConfigurationById(planFacilityRequest.getPlanFacility().getPlanConfigurationId(), rootTenantId); + + //validate service boundaries and residing boundaries with campaign id + validateCampaignDetails(planConfigurations.get(0).getCampaignId(), rootTenantId, planFacilityRequest); + } + + /** + * This method validates campaign id and service boundaries against project factory + * + * @param campaignId the campaign id corresponding to the plan config id provided in the request + * @param rootTenantId the tenant id provided in the request + * @param planFacilityRequest the plan facility request provided + */ + private void validateCampaignDetails(String campaignId, String rootTenantId, PlanFacilityRequest planFacilityRequest) { + PlanFacility planFacility = planFacilityRequest.getPlanFacility(); + Object mdmsData = mdmsUtil.fetchMdmsData(planFacilityRequest.getRequestInfo(), rootTenantId); + CampaignResponse campaignResponse = campaignUtil.fetchCampaignData(planFacilityRequest.getRequestInfo(), campaignId, rootTenantId); + + // Validate hierarchy type for campaign + Map hierarchyMap = commonUtil.getMicroplanHierarchy(mdmsData); + String lowestHierarchy = hierarchyMap.get(LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN); + + // Collect all boundary code for the campaign + Set boundaryCodes = fetchBoundaryCodes(campaignResponse.getCampaignDetails().get(0), lowestHierarchy); + + // Validate residing boundaries + validateResidingBoundaries(boundaryCodes, planFacility); + + // Validate service boundaries + validateServiceBoundaries(boundaryCodes, planFacility); + + //Enrich jurisdiction mapping and boundary ancestral path + enrichment.enrichJurisdictionMapping(planFacilityRequest, campaignResponse.getCampaignDetails().get(0).getHierarchyType()); + } + + /** + * This method returns boundary code for the campaign + * + * @param campaignDetail + * @param lowestHierarchy + */ + private Set fetchBoundaryCodes(CampaignDetail campaignDetail, String lowestHierarchy) { + Set boundaryCodes = campaignDetail.getBoundaries().stream() + .filter(boundary -> lowestHierarchy.equals(boundary.getType().toLowerCase())) + .map(Boundary::getCode) + .collect(Collectors.toSet()); + + return boundaryCodes; + } + + /** + * This method validates if residing boundaries exist in campaign details + * + * @param boundaryCodes + * @param planFacility + */ + private void validateResidingBoundaries(Set boundaryCodes, PlanFacility planFacility) { + String residingBoundary = planFacility.getResidingBoundary(); + if (residingBoundary != null && !boundaryCodes.contains(residingBoundary)) { + throw new CustomException(INVALID_RESIDING_BOUNDARY_CODE, INVALID_RESIDING_BOUNDARY_MESSAGE); + } + } + + /** + * This method validates if service boundaries exist in campaign details + * + * @param boundaryCodes + * @param planFacility + */ + private void validateServiceBoundaries(Set boundaryCodes, PlanFacility planFacility) { + List serviceBoundaries = planFacility.getServiceBoundaries(); + + // Check for duplicate service boundaries + Set uniqueBoundaries = new HashSet<>(serviceBoundaries); + if (uniqueBoundaries.size() != serviceBoundaries.size()) { + throw new CustomException(INVALID_SERVICE_BOUNDARY_CODE, "Duplicate service boundaries are not allowed"); + } + + planFacility.getServiceBoundaries().forEach(serviceBoundary -> { + if (!boundaryCodes.contains(serviceBoundary)) { + throw new CustomException(INVALID_SERVICE_BOUNDARY_CODE, INVALID_SERVICE_BOUNDARY_MESSAGE); + } + }); + } + + /** + * This method validates if the hierarchy type provided in the request exists + * + * @param campaignResponse + * @param mdmsData + */ + private String validateHierarchyType(CampaignResponse campaignResponse, Object mdmsData) { + // Get the hierarchy type from the campaign response + String hierarchyType = campaignResponse.getCampaignDetails().get(0).getHierarchyType(); + + // Define the JSON path to fetch hierarchy configurations from MDMS data + final String jsonPathForHierarchy = JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_HIERARCHY_CONFIG + "[*]"; + + List> hierarchyConfigList = null; + try { + hierarchyConfigList = JsonPath.read(mdmsData, jsonPathForHierarchy); + } catch (Exception e) { + throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); + } + + // Iterate through the hierarchy configuration list + for (Map hierarchyConfig : hierarchyConfigList) { + if (hierarchyType.equals(hierarchyConfig.get(MDMS_MASTER_HIERARCHY))) { + // Return the lowest hierarchy value from the configuration + return (String) hierarchyConfig.get(LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN); + } + } + // Throw exception if no matching hierarchy is found + throw new CustomException(HIERARCHY_NOT_FOUND_IN_MDMS_CODE, HIERARCHY_NOT_FOUND_IN_MDMS_MESSAGE); + } + + /** + * This method validates if the plan facility id provided in the update request exists + * + * @param planFacilityRequest + */ + private void validatePlanFacilityExistence(PlanFacilityRequest planFacilityRequest) { + List planFacilityListFromSearch = planFacilityRepository.search(PlanFacilitySearchCriteria.builder() + .ids(Collections.singleton(planFacilityRequest.getPlanFacility().getId())) + .build()); + + // If plan facility id provided is invalid, throw an exception + if (CollectionUtils.isEmpty(planFacilityListFromSearch)) { + throw new CustomException(INVALID_PLAN_FACILITY_ID_CODE, INVALID_PLAN_FACILITY_ID_MESSAGE); + } + + enrichInitialServiceBoundaries(planFacilityListFromSearch, planFacilityRequest); + } + + private void enrichInitialServiceBoundaries(List planFacilityListFromSearch, PlanFacilityRequest planFacilityRequest) { + + List initiallySetServiceBoundaries = planFacilityListFromSearch.get(0).getServiceBoundaries(); + planFacilityRequest.getPlanFacility().setInitiallySetServiceBoundaries(initiallySetServiceBoundaries); + } + + /** + * Searches the plan config based on the plan config id provided + * + * @param planConfigurationId + * @param tenantId + * @return + */ + public List fetchPlanConfigurationById(String planConfigurationId, String tenantId) { + List planConfigurations = planConfigurationRepository.search(PlanConfigurationSearchCriteria.builder() + .id(planConfigurationId) + .tenantId(tenantId) + .build()); + log.info("planConfigurations: " + planConfigurations); + + // Validate planConfiguration exists + if (CollectionUtils.isEmpty(planConfigurations)) { + throw new CustomException(INVALID_PLAN_CONFIG_ID_CODE, INVALID_PLAN_CONFIG_ID_MESSAGE); + } + + return planConfigurations; + } + + /** + * Validates if the facility with the provided ID exists in the system. + * + * @param planFacilityRequest + */ + private void validateFacilityExistence(PlanFacilityRequest planFacilityRequest) { + FacilityResponse facilityResponse = facilityUtil.fetchFacilityData(planFacilityRequest); + + // Use ObjectUtils and CollectionUtils to handle null or empty checks + if (ObjectUtils.isEmpty(facilityResponse) || CollectionUtils.isEmpty(facilityResponse.getFacilities())) { + throw new CustomException("FACILITY_NOT_FOUND", "Facility with ID " + planFacilityRequest.getPlanFacility().getFacilityId() + " not found in the system."); + } + + enrichFacilityDetails(facilityResponse.getFacilities().get(0), planFacilityRequest); + } + + private void enrichFacilityDetails(Facility facility, PlanFacilityRequest planFacilityRequest) { + String facilityName = facility.getName(); + planFacilityRequest.getPlanFacility().setFacilityName(facilityName); + BigDecimal initialServingPop = BigDecimal.ZERO; + + Map fieldsToBeAdded = new HashMap<>(); + fieldsToBeAdded.put("facilityUsage", facility.getUsage()); + fieldsToBeAdded.put("capacity", facility.getStorageCapacity()); + fieldsToBeAdded.put("facilityStatus", facility.getAddress().getType()); + fieldsToBeAdded.put("facilityType", facility.getUsage()); + fieldsToBeAdded.put("isPermanent", facility.isPermanent()); + fieldsToBeAdded.put("servingPopulation", initialServingPop); + + planFacilityRequest.getPlanFacility().setAdditionalDetails( + commonUtil.updateFieldInAdditionalDetails(planFacilityRequest.getPlanFacility().getAdditionalDetails(), fieldsToBeAdded)); + } + +} diff --git a/health-services/plan-service/src/main/java/digit/service/validator/WorkflowValidator.java b/health-services/plan-service/src/main/java/digit/service/validator/WorkflowValidator.java new file mode 100644 index 00000000000..321cdb36ecc --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/validator/WorkflowValidator.java @@ -0,0 +1,141 @@ +package digit.service.validator; + +import digit.service.PlanService; +import digit.util.CensusUtil; +import digit.web.models.*; +import digit.web.models.census.CensusResponse; +import digit.web.models.census.CensusSearchCriteria; +import digit.web.models.census.CensusSearchRequest; +import org.egov.common.contract.request.RequestInfo; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.util.Map; + +import static digit.config.ServiceConstants.*; +import static digit.config.ServiceConstants.CANNOT_APPROVE_ESTIMATIONS_MESSAGE; + +@Component +public class WorkflowValidator { + + private CensusUtil censusUtil; + + private PlanService planService; + + public WorkflowValidator(CensusUtil censusUtil, PlanService planService) { + this.censusUtil = censusUtil; + this.planService = planService; + } + + public void validateWorkflow(PlanConfigurationRequest planConfigurationRequest) { + if (ObjectUtils.isEmpty(planConfigurationRequest.getPlanConfiguration().getWorkflow())) + return; + + String workflowAction = planConfigurationRequest.getPlanConfiguration().getWorkflow().getAction(); + + if(workflowAction.equals(APPROVE_CENSUS_DATA_ACTION)) { + validateCensusData(planConfigurationRequest); + } else if(workflowAction.equals(FINALIZE_CATCHMENT_MAPPING_ACTION)) { + validateCatchmentMapping(planConfigurationRequest); + } else if(workflowAction.equals(APPROVE_ESTIMATIONS_ACTION)) { + validateResourceEstimations(planConfigurationRequest); + } + } + + /** + * Validates if all the census records are validated before approving census data for the given planConfigId. + * + * @param planConfigurationRequest request with plan config id. + */ + private void validateCensusData(PlanConfigurationRequest planConfigurationRequest) { + PlanConfiguration planConfiguration = planConfigurationRequest.getPlanConfiguration(); + + CensusSearchRequest censusSearchRequest = getCensusSearchRequest(planConfiguration.getTenantId(), planConfiguration.getId(), planConfigurationRequest.getRequestInfo()); + + // Fetches census records for given planConfigId + CensusResponse censusResponse = censusUtil.fetchCensusRecords(censusSearchRequest); + + Map statusCount = censusResponse.getStatusCount(); + Integer totalCount = censusResponse.getTotalCount(); + + // Throws exception if all census records are not validated + if (!statusCount.get(VALIDATED_STATUS).equals(totalCount)) { + throw new CustomException(CANNOT_APPROVE_CENSUS_DATA_CODE, CANNOT_APPROVE_CENSUS_DATA_MESSAGE); + } + } + + /** + * Validates if all boundaries have facility assigned before finalizing catchment mapping for a given planConfigID. + * + * @param planConfigurationRequest request with plan config id. + */ + private void validateCatchmentMapping(PlanConfigurationRequest planConfigurationRequest) { + PlanConfiguration planConfiguration = planConfigurationRequest.getPlanConfiguration(); + + CensusSearchRequest censusSearchRequest = getCensusSearchRequest(planConfiguration.getTenantId(), planConfiguration.getId(), planConfigurationRequest.getRequestInfo()); + + // Fetches all census records for given planConfigId + CensusResponse censusResponse = censusUtil.fetchCensusRecords(censusSearchRequest); + Integer totalCensusCount = censusResponse.getTotalCount(); + + censusSearchRequest.getCensusSearchCriteria().setFacilityAssigned(Boolean.TRUE); + + // Fetches all census records for given planConfigId where facility is assigned + CensusResponse censusWithFacilityAssigned = censusUtil.fetchCensusRecords(censusSearchRequest); + Integer totalCensusWithFacilityAssigned = censusWithFacilityAssigned.getTotalCount(); + + if (!totalCensusCount.equals(totalCensusWithFacilityAssigned)) { + throw new CustomException(CANNOT_FINALIZE_CATCHMENT_MAPPING_CODE, CANNOT_FINALIZE_CATCHMENT_MAPPING_MESSAGE); + } + } + + /** + * Validates if all the plan estimations are validated before approving estimations for the given planConfigId. + * + * @param planConfigurationRequest request with plan config id. + */ + private void validateResourceEstimations(PlanConfigurationRequest planConfigurationRequest) { + PlanConfiguration planConfiguration = planConfigurationRequest.getPlanConfiguration(); + + PlanSearchRequest searchRequest = getPlanSearchRequest(planConfiguration.getTenantId(), planConfiguration.getId(), planConfigurationRequest.getRequestInfo()); + + // Fetches plans for given planConfigId + PlanResponse planResponse = planService.searchPlan(searchRequest); + + Map statusCount = planResponse.getStatusCount(); + Integer totalCount = planResponse.getTotalCount(); + + // Throws exception if all plans are not validated + if (!statusCount.get(VALIDATED_STATUS).equals(totalCount)) { + throw new CustomException(CANNOT_APPROVE_ESTIMATIONS_CODE, CANNOT_APPROVE_ESTIMATIONS_MESSAGE); + } + } + + // Prepares Census search request for given planConfigId + private CensusSearchRequest getCensusSearchRequest(String tenantId, String planConfigId, RequestInfo requestInfo) { + CensusSearchCriteria searchCriteria = CensusSearchCriteria.builder() + .tenantId(tenantId) + .source(planConfigId) + .build(); + + return CensusSearchRequest.builder() + .requestInfo(requestInfo) + .censusSearchCriteria(searchCriteria) + .build(); + } + + // Prepares Plan search request for given planConfigId + private PlanSearchRequest getPlanSearchRequest(String tenantId, String planConfigId, RequestInfo requestInfo) { + PlanSearchCriteria searchCriteria = PlanSearchCriteria.builder() + .tenantId(tenantId) + .planConfigurationId(planConfigId) + .build(); + + return PlanSearchRequest.builder() + .requestInfo(requestInfo) + .planSearchCriteria(searchCriteria) + .build(); + } + +} diff --git a/health-services/plan-service/src/main/java/digit/service/workflow/WorkflowService.java b/health-services/plan-service/src/main/java/digit/service/workflow/WorkflowService.java new file mode 100644 index 00000000000..24932950df8 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/service/workflow/WorkflowService.java @@ -0,0 +1,432 @@ +package digit.service.workflow; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.config.Configuration; +import digit.repository.ServiceRequestRepository; +import digit.service.PlanEmployeeService; +import digit.service.validator.PlanConfigurationValidator; +import digit.util.CommonUtil; +import digit.web.models.*; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.models.Workflow; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.request.User; +import org.egov.common.contract.workflow.*; +import org.egov.common.utils.AuditDetailsEnrichmentUtil; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.client.RestTemplate; + +import java.util.*; +import java.util.stream.Collectors; + +import static digit.config.ServiceConstants.*; +import static digit.config.ServiceConstants.NO_BUSINESS_SERVICE_DATA_FOUND_MESSAGE; + +@Service +@Slf4j +public class WorkflowService { + + private ServiceRequestRepository serviceRequestRepository; + + private Configuration config; + + private ObjectMapper mapper; + + private CommonUtil commonUtil; + + private PlanEmployeeService planEmployeeService; + + private PlanConfigurationValidator planConfigurationValidator; + + private RestTemplate restTemplate; + + public WorkflowService(ServiceRequestRepository serviceRequestRepository, Configuration config, ObjectMapper mapper, CommonUtil commonUtil, PlanEmployeeService planEmployeeService, PlanConfigurationValidator planConfigurationValidator, RestTemplate restTemplate) { + this.serviceRequestRepository = serviceRequestRepository; + this.config = config; + this.mapper = mapper; + this.commonUtil = commonUtil; + this.planEmployeeService = planEmployeeService; + this.planConfigurationValidator = planConfigurationValidator; + this.restTemplate = restTemplate; + } + + /** + * Integrates with the workflow for the given plan configuration request. + * If the action is null, it does not proceed with integration. + * + * @param planConfigurationRequest The request containing the plan configuration to integrate with the workflow. + */ + public void invokeWorkflowForStatusUpdate(PlanConfigurationRequest planConfigurationRequest) { + if (ObjectUtils.isEmpty(planConfigurationRequest.getPlanConfiguration().getWorkflow())) + return; + + ProcessInstanceRequest processInstanceRequest = createWorkflowRequest(planConfigurationRequest); + ProcessInstanceResponse processInstanceResponse = callWorkflowTransition(processInstanceRequest); + + // Setting the status back to the plan configuration object from workflow response + planConfigurationRequest.getPlanConfiguration().setStatus(processInstanceResponse.getProcessInstances().get(0).getState().getState()); + } + + /** + * Integrates with the workflow for the given plan request. + * If the action is null, it does not proceed with integration. + * + * @param planRequest The request containing the plan estimate to integrate with the workflow. + */ + public void invokeWorkflowForStatusUpdate(PlanRequest planRequest) { + if (ObjectUtils.isEmpty(planRequest.getPlan().getWorkflow())) + return; + + ProcessInstanceRequest processInstanceRequest = createWorkflowRequest(planRequest); + ProcessInstanceResponse processInstanceResponse = callWorkflowTransition(processInstanceRequest); + + // Setting the status back to the plan configuration object from workflow response + planRequest.getPlan().setStatus(processInstanceResponse.getProcessInstances().get(0).getState().getState()); + + // Enrich audit details after auto assignment is complete + planRequest.getPlan().setAuditDetails(AuditDetailsEnrichmentUtil + .prepareAuditDetails( planRequest.getPlan().getAuditDetails(), planRequest.getRequestInfo(), Boolean.FALSE)); + + } + + /** + * Calls the workflow transition service and retrieves the process instance response. + * + * @param processInstanceRequest The request containing process instance details for the workflow transition. + * @return The response containing details of the process instances after the transition. + * @throws CustomException if there is an error during the workflow integration. + */ + public ProcessInstanceResponse callWorkflowTransition(ProcessInstanceRequest processInstanceRequest) { + ProcessInstanceResponse processInstanceResponse; + try { + Object response = serviceRequestRepository.fetchResult(getWorkflowTransitionUri(), processInstanceRequest); + processInstanceResponse = mapper.convertValue(response, ProcessInstanceResponse.class); + } catch (Exception e) { + throw new CustomException(WORKFLOW_INTEGRATION_ERROR_CODE, WORKFLOW_INTEGRATION_ERROR_MESSAGE + e.getMessage()); + } + + return processInstanceResponse; + } + + /** + * Creates a workflow request from the given plan configuration request. + * + * @param planConfigurationRequest The request containing the plan configuration to create a workflow request. + * @return The constructed process instance request for the workflow. + */ + public ProcessInstanceRequest createWorkflowRequest(PlanConfigurationRequest planConfigurationRequest) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + ProcessInstance processInstance = ProcessInstance.builder() + .businessId(planConfig.getId()) + .tenantId(planConfig.getTenantId()) + .businessService(PLAN_CONFIGURATION_BUSINESS_SERVICE) + .moduleName(MODULE_NAME_VALUE) + .action(planConfig.getWorkflow().getAction()) + .comment(planConfig.getWorkflow().getComments()) + .documents(planConfig.getWorkflow().getDocuments()) + .build(); + + enrichAssignesInProcessInstance(processInstance, planConfig.getWorkflow()); + + return ProcessInstanceRequest.builder() + .requestInfo(planConfigurationRequest.getRequestInfo()) + .processInstances(Collections.singletonList(processInstance)) + .build(); + } + + /** + * Creates a workflow request from the given plan configuration request. + * + * @param planRequest The request containing the plan to create a workflow request. + * @return The constructed process instance request for the workflow. + */ + public ProcessInstanceRequest createWorkflowRequest(PlanRequest planRequest) { + Plan plan = planRequest.getPlan(); + ProcessInstance processInstance = ProcessInstance.builder() + .businessId(plan.getId()) + .tenantId(plan.getTenantId()) + .businessService(PLAN_ESTIMATION_BUSINESS_SERVICE) + .moduleName(MODULE_NAME_VALUE) + .action(plan.getWorkflow().getAction()) + .comment(plan.getWorkflow().getComments()) + .documents(plan.getWorkflow().getDocuments()) + .build(); + + List assignee = getAssigneeForAutoAssignment(plan, planRequest.getRequestInfo()); + + // Set assignees for send back actions + if (config.getWfSendBackActions().contains(plan.getWorkflow().getAction())) { + assignee = Collections.singletonList(plan.getAuditDetails().getLastModifiedBy()); + } + + // Set Assignee + if(!ObjectUtils.isEmpty(assignee)) + plan.getWorkflow().setAssignes(assignee); + + plan.setAssignee(assignee); + + enrichAssignesInProcessInstance(processInstance, plan.getWorkflow()); + + log.info("Process Instance assignes - " + processInstance.getAssignes()); + return ProcessInstanceRequest.builder() + .requestInfo(planRequest.getRequestInfo()) + .processInstances(Collections.singletonList(processInstance)) + .build(); + } + + /** + * Enriches the process instance with assignees from the given workflow. + * + * @param processInstance The process instance to enrich with assignees. + * @param workflow The workflow containing assignees to be added to the process instance. + */ + public void enrichAssignesInProcessInstance(ProcessInstance processInstance, Workflow workflow) { + List userList = CollectionUtils.isEmpty(workflow.getAssignes()) + ? new LinkedList<>() + : workflow.getAssignes().stream() + .map(assignee -> User.builder().uuid(assignee).build()) + .toList(); + + processInstance.setAssignes(userList); + } + + /** + * Constructs the URI for the workflow service transition API. + * + * @return The StringBuilder containing the constructed workflow transition URI. + */ + private StringBuilder getWorkflowTransitionUri() { + return new StringBuilder().append(config.getWfHost()).append(config.getWfTransitionPath()); + } + + /** + * Automatically assigns a list of assignee based on the workflow action and jurisdiction hierarchy. + * Retrieves jurisdiction boundaries from the plan request and searches for matching employee assignments. + * + * For INITIATE actions, assigns the employee from the lowest boundary. + * For INTERMEDIATE actions (non-ROOT_APPROVER), assigns an employee from a higher-level boundary. + * For SEND_BACK actions, assigns the last modified user. + * + * The assignee is set in both the workflow and the plan request. + * + * @param requestInfo auth details for making internal calls + * @param plan the plan object containing workflow and jurisdiction details + */ + private List getAssigneeForAutoAssignment(Plan plan, RequestInfo requestInfo) { + String[] allheirarchysBoundaryCodes = plan.getBoundaryAncestralPath().split(PIPE_REGEX); + String[] heirarchysBoundaryCodes = Arrays.copyOf(allheirarchysBoundaryCodes, allheirarchysBoundaryCodes.length - 1); + + PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria = + PlanEmployeeAssignmentSearchCriteria.builder() + .tenantId(plan.getTenantId()) + .jurisdiction(Arrays.stream(heirarchysBoundaryCodes).toList()) + .planConfigurationId(plan.getPlanConfigurationId()) + .role(config.getPlanEstimationApproverRoles()) + .build(); + + //search for plan-employee assignments for the ancestral heirarchy codes. + PlanEmployeeAssignmentResponse planEmployeeAssignmentResponse = planEmployeeService.search(PlanEmployeeAssignmentSearchRequest.builder() + .planEmployeeAssignmentSearchCriteria(planEmployeeAssignmentSearchCriteria) + .requestInfo(requestInfo).build()); + + // Create a map of jurisdiction to list of employeeIds + Map> jurisdictionToEmployeeMap = planEmployeeAssignmentResponse.getPlanEmployeeAssignment().stream() + .filter(assignment -> assignment.getJurisdiction() != null && !assignment.getJurisdiction().isEmpty()) + .flatMap(assignment -> { + String employeeId = assignment.getEmployeeId(); + return assignment.getJurisdiction().stream() + .filter(jurisdiction -> Arrays.asList(heirarchysBoundaryCodes).contains(jurisdiction)) + .map(jurisdiction -> new AbstractMap.SimpleEntry<>(jurisdiction, employeeId)); + }) + .collect(Collectors.groupingBy( + Map.Entry::getKey, // jurisdiction as the key + LinkedHashMap::new, // Preserve insertion order + Collectors.mapping( + Map.Entry::getValue, // employee IDs as values + Collectors.toList() // Collect employee IDs into a List + ) + )); + + List assignee = null; //assignee will remain null in case terminate actions are being taken + + String action = plan.getWorkflow().getAction(); + if (config.getWfInitiateActions().contains(action)) { + for (int i = heirarchysBoundaryCodes.length - 1; i >= 0; i--) { + assignee = jurisdictionToEmployeeMap.get(heirarchysBoundaryCodes[i]); + if (assignee != null) + break; // Stop iterating once an assignee is found + } + } else if (config.getWfIntermediateActions().contains(action)) { + assignee = assignToHigherBoundaryLevel(heirarchysBoundaryCodes, plan, jurisdictionToEmployeeMap); + } + + return assignee; + } + + /** + * Assigns a list of employees from a higher-level jurisdiction in the hierarchy. + * Iterates through boundary codes, checking if they match the assignee's jurisdiction. + * If a higher-level boundary has an assigned employee, returns that employee's ID. + * + * @param heirarchysBoundaryCodes boundary codes representing the hierarchy + * @param plan the object with plan and jurisdiction details + * @param jurisdictionToEmployeeMap map of jurisdiction codes to employee IDs + * @return the employee ID from the higher boundary, or null if + */ + public List assignToHigherBoundaryLevel(String[] heirarchysBoundaryCodes, Plan plan, Map> jurisdictionToEmployeeMap) { + for (int i = heirarchysBoundaryCodes.length - 1; i >= 0; i--) { + String boundaryCode = heirarchysBoundaryCodes[i]; + + // Check if this boundary code is present in assigneeJurisdiction + if (plan.getAssigneeJurisdiction().contains(boundaryCode)) { + + for (int j = i - 1; j >= 0; j--) { + // Check the next higher level in the hierarchy (one index above the match) + String higherBoundaryCode = heirarchysBoundaryCodes[j]; + + // Fetch the employeeId from the map for the higher boundary code + List employeeId = jurisdictionToEmployeeMap.get(higherBoundaryCode); + + // If an employee is found, set them as the assignee and break the loop + if (employeeId != null) { + return employeeId; + } + } + } + } + return null; + } + + public void invokeWorkflowForStatusUpdate(BulkPlanRequest bulkPlanRequest) { + ProcessInstanceRequest processInstanceRequest = createWorkflowRequest(bulkPlanRequest); + ProcessInstanceResponse processInstanceResponse = callWorkflowTransition(processInstanceRequest); + + enrichPlansPostTransition(processInstanceResponse, bulkPlanRequest); + } + + private void enrichPlansPostTransition(ProcessInstanceResponse processInstanceResponse, BulkPlanRequest bulkPlanRequest) { + // Update status and audit information post transition + bulkPlanRequest.getPlans().forEach(plan -> { + // Update status of plan + plan.setStatus(processInstanceResponse.getProcessInstances().get(0).getState().getState()); + + // Update audit information of plan + plan.setAuditDetails(AuditDetailsEnrichmentUtil + .prepareAuditDetails(plan.getAuditDetails(), bulkPlanRequest.getRequestInfo(), Boolean.FALSE)); + }); + } + + private ProcessInstanceRequest createWorkflowRequest(BulkPlanRequest bulkPlanRequest) { + List processInstanceList = new ArrayList<>(); + + // Perform auto assignment + List assignee = getAssigneeForAutoAssignment(bulkPlanRequest.getPlans().get(0), + bulkPlanRequest.getRequestInfo()); + + for(Plan plan: bulkPlanRequest.getPlans()) { + + // Setting assignee for send back actions + if (config.getWfSendBackActions().contains(plan.getWorkflow().getAction())) { + assignee = Collections.singletonList(plan.getAuditDetails().getLastModifiedBy()); + } + + // Set assignee + if(!ObjectUtils.isEmpty(assignee)) + plan.getWorkflow().setAssignes(assignee); + + plan.setAssignee(assignee); + + // Create process instance object from plan + ProcessInstance processInstance = ProcessInstance.builder() + .businessId(plan.getId()) + .tenantId(plan.getTenantId()) + .businessService(PLAN_ESTIMATION_BUSINESS_SERVICE) + .moduleName(MODULE_NAME_VALUE) + .action(plan.getWorkflow().getAction()) + .comment(plan.getWorkflow().getComments()) + .documents(plan.getWorkflow().getDocuments()) + .build(); + + // Enrich user list for process instance + enrichAssignesInProcessInstance(processInstance, plan.getWorkflow()); + + // Add entry for bulk transition + processInstanceList.add(processInstance); + } + + return ProcessInstanceRequest.builder() + .requestInfo(bulkPlanRequest.getRequestInfo()) + .processInstances(processInstanceList) + .build(); + } + + /** + * Creates a list of all the workflow states for the provided business service. + * @param requestInfo + * @param businessService + * @param tenantId + * @return + */ + public List getStatusFromBusinessService(RequestInfo requestInfo, String businessService, String tenantId) { + BusinessService businessServices = fetchBusinessService(requestInfo, businessService, tenantId); + + return businessServices.getStates().stream() + .map(State::getState) + .filter(state -> !ObjectUtils.isEmpty(state)) + .toList(); + } + + /** + * This method fetches business service details for the given tenant id and business service. + * + * @param requestInfo the request info from request. + * @param businessService businessService whose details are to be searched. + * @param tenantId tenantId from request. + * @return returns the business service response for the given tenant id and business service. + */ + public BusinessService fetchBusinessService(RequestInfo requestInfo, String businessService, String tenantId) { + + // Get business service uri + Map uriParameters = new HashMap<>(); + String uri = getBusinessServiceUri(businessService, tenantId, uriParameters); + + // Create request body + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + BusinessServiceResponse businessServiceResponse = new BusinessServiceResponse(); + + try { + businessServiceResponse = restTemplate.postForObject(uri, requestInfoWrapper, BusinessServiceResponse.class, uriParameters); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_BUSINESS_SERVICE_DETAILS, e); + } + + if (CollectionUtils.isEmpty(businessServiceResponse.getBusinessServices())) { + throw new CustomException(NO_BUSINESS_SERVICE_DATA_FOUND_CODE, NO_BUSINESS_SERVICE_DATA_FOUND_MESSAGE); + } + + return businessServiceResponse.getBusinessServices().get(0); + } + + /** + * This method creates business service uri with query parameters + * + * @param businessService businessService whose details are to be searched. + * @param tenantId tenant id from the request. + * @param uriParameters map that stores values corresponding to the placeholder in uri + * @return + */ + private String getBusinessServiceUri(String businessService, String tenantId, Map uriParameters) { + + StringBuilder uri = new StringBuilder(); + uri.append(config.getWfHost()).append(config.getBusinessServiceSearchEndpoint()).append(URI_BUSINESS_SERVICE_QUERY_TEMPLATE); + + uriParameters.put(URI_TENANT_ID_PARAM, tenantId); + uriParameters.put(URI_BUSINESS_SERVICE_PARAM, businessService); + + return uri.toString(); + } + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/util/BoundaryUtil.java b/health-services/plan-service/src/main/java/digit/util/BoundaryUtil.java new file mode 100644 index 00000000000..ec43e0f0447 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/BoundaryUtil.java @@ -0,0 +1,111 @@ +package digit.util; + +import digit.config.Configuration; +import digit.web.models.RequestInfoWrapper; +import digit.web.models.boundary.BoundarySearchResponse; +import digit.web.models.boundary.BoundaryTypeHierarchyResponse; +import digit.web.models.boundary.BoundaryTypeHierarchySearchCriteria; +import digit.web.models.boundary.BoundaryTypeHierarchySearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_BOUNDARY_DETAILS; +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_BOUNDARY_HIERARCHY_DETAILS; + + +@Slf4j +@Component +public class BoundaryUtil { + + private RestTemplate restTemplate; + + private Configuration configs; + + public BoundaryUtil(RestTemplate restTemplate, Configuration configs) { + this.restTemplate = restTemplate; + this.configs = configs; + } + + /** + * This method fetches boundary relationships from Boundary service for the provided boundaryCode and hierarchyType. + * + * @param requestInfo request info from the request. + * @param boundaryCode boundary code from the request. + * @param tenantId tenant id from the request. + * @param hierarchyType hierarchy type from the request. + * @param includeParents is true if you want to include parent boundary. + * @param includeChildren is true if you want to include child boundary. + * @return returns the response from boundary service + */ + public BoundarySearchResponse fetchBoundaryData(RequestInfo requestInfo, String boundaryCode, String tenantId, String hierarchyType, Boolean includeParents, Boolean includeChildren) { + + // Create Boundary Relationship search uri + Map uriParameters = new HashMap<>(); + StringBuilder uri = getBoundaryRelationshipSearchUri(uriParameters, boundaryCode, tenantId, hierarchyType, includeParents, includeChildren); + + // Create request body + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + BoundarySearchResponse boundarySearchResponse = new BoundarySearchResponse(); + + try { + boundarySearchResponse = restTemplate.postForObject(uri.toString(), requestInfoWrapper, BoundarySearchResponse.class, uriParameters); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_BOUNDARY_DETAILS, e); + } + + return boundarySearchResponse; + } + + /** + * This method creates Boundary service uri with query parameters + * + * @param uriParameters map that stores values corresponding to the placeholder in uri + * @param boundaryCode boundary code from the request. + * @param tenantId tenant id from the request. + * @param hierarchyType hierarchy type from the request. + * @param includeParents is true if you want to include parent boundary. + * @param includeChildren is true if you want to include child boundary. + * @return a complete boundary service uri + */ + private StringBuilder getBoundaryRelationshipSearchUri(Map uriParameters, String boundaryCode, String tenantId, String hierarchyType, Boolean includeParents, Boolean includeChildren) { + StringBuilder uri = new StringBuilder(); + uri.append(configs.getBoundaryServiceHost()).append(configs.getBoundaryRelationshipSearchEndpoint()).append("?codes={boundaryCode}&includeParents={includeParents}&includeChildren={includeChildren}&tenantId={tenantId}&hierarchyType={hierarchyType}"); + + uriParameters.put("boundaryCode", boundaryCode); + uriParameters.put("tenantId", tenantId); + uriParameters.put("includeParents", includeParents.toString()); + uriParameters.put("includeChildren", includeChildren.toString()); + uriParameters.put("hierarchyType", hierarchyType); + + return uri; + } + + public BoundaryTypeHierarchyResponse fetchBoundaryHierarchy(RequestInfo requestInfo, String tenantId, String hierarchyType) { + + // Create Boundary hierarchy search uri + String uri = getBoundaryHierarchySearchUri(); + + // Create request body + BoundaryTypeHierarchySearchCriteria searchCriteria = BoundaryTypeHierarchySearchCriteria.builder().tenantId(tenantId).hierarchyType(hierarchyType).build(); + BoundaryTypeHierarchySearchRequest searchRequest = BoundaryTypeHierarchySearchRequest.builder().requestInfo(requestInfo).boundaryTypeHierarchySearchCriteria(searchCriteria).build(); + BoundaryTypeHierarchyResponse searchResponse = new BoundaryTypeHierarchyResponse(); + + try { + searchResponse = restTemplate.postForObject(uri, searchRequest, BoundaryTypeHierarchyResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_BOUNDARY_HIERARCHY_DETAILS, e); + } + + return searchResponse; + } + + private String getBoundaryHierarchySearchUri() { + StringBuilder uri = new StringBuilder(); + uri.append(configs.getBoundaryServiceHost()).append(configs.getBoundaryHierarchySearchEndpoint()); + return uri.toString(); + } +} diff --git a/health-services/plan-service/src/main/java/digit/util/CampaignUtil.java b/health-services/plan-service/src/main/java/digit/util/CampaignUtil.java new file mode 100644 index 00000000000..5f1632e6366 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/CampaignUtil.java @@ -0,0 +1,91 @@ +package digit.util; + +import digit.config.Configuration; + +import digit.web.models.projectFactory.*; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class CampaignUtil { + + private RestTemplate restTemplate; + + private Configuration configs; + + public CampaignUtil(RestTemplate restTemplate, Configuration configs) { + this.restTemplate = restTemplate; + this.configs = configs; + } + + /** + * This method fetches data from project factory for provided campaignId and service boundaries + * + * @param requestInfo request info from the request + * @param campaignId campaign id provided in the request + * @param tenantId tenant id from the request + */ + public CampaignResponse fetchCampaignData(RequestInfo requestInfo, String campaignId, String tenantId) { + + // Build the URI for calling the Project Factory service + StringBuilder uri = buildCampaignSearchUri(); + + // Prepare the search request object with required campaign ID, tenant ID, and request information + CampaignSearchReq campaignSearchReq = getCampaignSearchRequest(requestInfo, campaignId, tenantId); + CampaignResponse campaignResponse = null; + + try { + campaignResponse = restTemplate.postForObject(uri.toString(), campaignSearchReq, CampaignResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_PROJECT_FACTORY, e); + } + + // Validate that the response contains campaign details, otherwise throw an exception + if (CollectionUtils.isEmpty(campaignResponse.getCampaignDetails())) { + throw new CustomException(NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_CODE, NO_CAMPAIGN_DETAILS_FOUND_FOR_GIVEN_CAMPAIGN_ID_MESSAGE); + } + + return campaignResponse; + } + + /** + * This method create the uri for project factory to fetch campaign data. + * + * @return The complete URI. + */ + private StringBuilder buildCampaignSearchUri() { + return new StringBuilder().append(configs.getProjectFactoryHost()).append(configs.getProjectFactorySearchEndPoint()); + } + + /** + * Creates the request object for fetching campaign data. + * + * @param requestInfo Information about the request such as user details and correlation ID. + * @param campaignId The ID of the campaign to be searched. + * @param tenantId The tenant identifier (for multi-tenant support). + * @return The request object containing the search criteria and request info. + */ + private CampaignSearchReq getCampaignSearchRequest(RequestInfo requestInfo, String campaignId, String tenantId) { + Pagination pagination = Pagination.builder() + .limit(configs.getDefaultLimit()) + .offset(configs.getDefaultOffset()) + .build(); + + CampaignSearchCriteria searchCriteria = CampaignSearchCriteria.builder() + .ids(Collections.singletonList(campaignId)) + .tenantId(tenantId) + .pagination(pagination) + .build(); + + return CampaignSearchReq.builder().requestInfo(requestInfo).campaignSearchCriteria(searchCriteria).build(); + } +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/util/CensusUtil.java b/health-services/plan-service/src/main/java/digit/util/CensusUtil.java new file mode 100644 index 00000000000..fa45518c44a --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/CensusUtil.java @@ -0,0 +1,60 @@ +package digit.util; + +import digit.config.Configuration; +import digit.web.models.census.CensusResponse; +import digit.web.models.census.CensusSearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestTemplate; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class CensusUtil { + + private RestTemplate restTemplate; + + private Configuration config; + + public CensusUtil(RestTemplate restTemplate, Configuration config) { + this.restTemplate = restTemplate; + this.config = config; + } + + /** + * This method fetches data from Census based on the given census search request. + * + * @param searchRequest The census search request containing the search criteria. + * @return returns the census response. + */ + public CensusResponse fetchCensusRecords(CensusSearchRequest searchRequest) { + + // Get census search uri + String uri = getCensusUri().toString(); + + CensusResponse censusResponse = null; + try { + censusResponse = restTemplate.postForObject(uri, searchRequest, CensusResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_CENSUS, e); + } + + if (CollectionUtils.isEmpty(censusResponse.getCensus())) { + throw new CustomException(NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_CODE, NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_MESSAGE); + } + + return censusResponse; + } + + /** + * Builds the census search uri. + * + * @return returns the complete uri for census search. + */ + private StringBuilder getCensusUri() { + return new StringBuilder().append(config.getCensusHost()).append(config.getCensusSearchEndPoint()); + } +} diff --git a/health-services/plan-service/src/main/java/digit/util/CommonUtil.java b/health-services/plan-service/src/main/java/digit/util/CommonUtil.java new file mode 100644 index 00000000000..6635294fd87 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/CommonUtil.java @@ -0,0 +1,275 @@ +package digit.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jayway.jsonpath.JsonPath; +import digit.repository.PlanConfigurationRepository; +import digit.web.models.Operation; +import digit.web.models.PlanConfiguration; +import digit.web.models.PlanConfigurationSearchCriteria; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.text.StringEscapeUtils; +import org.egov.common.contract.request.RequestInfo; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static digit.config.ServiceConstants.*; + +@Component +@Slf4j +public class CommonUtil { + + private PlanConfigurationRepository planConfigurationRepository; + + private ObjectMapper objectMapper; + + public CommonUtil(PlanConfigurationRepository planConfigurationRepository, ObjectMapper objectMapper) { + this.planConfigurationRepository = planConfigurationRepository; + this.objectMapper = objectMapper; + } + + /** + * Validates the given input string against the provided regex pattern. + * + * @param patternString the regex pattern to validate against + * @param inputString the input string to be validated + * @return true if the input string matches the regex pattern, false otherwise + */ + public Boolean validateStringAgainstRegex(String patternString, String inputString) { + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(inputString); + return matcher.matches(); + } + + + /** + * Extracts provided field from the additional details object + * + * @param additionalDetails the additionalDetails object from PlanConfigurationRequest + * @param fieldToExtract the name of the field to be extracted from the additional details + * @return the value of the specified field as a string + * @throws CustomException if the field does not exist + */ + public Object extractFieldsFromJsonObject(Object additionalDetails, String fieldToExtract) { + try { + String jsonString = objectMapper.writeValueAsString(additionalDetails); + JsonNode rootNode = objectMapper.readTree(jsonString); + + JsonNode node = rootNode.get(fieldToExtract); + if (node != null && !node.isNull()) { + + // Check for different types of JSON nodes + if (node.isDouble() || node.isFloat()) { + return BigDecimal.valueOf(node.asDouble()); // Convert Double to BigDecimal + } else if (node.isLong() || node.isInt()) { + return BigDecimal.valueOf(node.asLong()); // Convert Long to BigDecimal + } else if (node.isBoolean()) { + return node.asBoolean(); + } else if (node.isTextual()) { + return node.asText(); + } + } + return null; + } catch (Exception e) { + log.error(e.getMessage() + fieldToExtract); + throw new CustomException(PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_CODE, PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_MESSAGE + fieldToExtract); + } + } + + /** + * Extracts provided field from the additional details object + * + * @param additionalDetails the additionalDetails object from PlanConfigurationRequest + * @param fieldToExtract the name of the field to be extracted from the additional details + * @return the value of the specified field as a list of string + * @throws CustomException if the field does not exist + */ + public List extractFieldsFromJsonObject(Object additionalDetails, String fieldToExtract, Class valueType) { + try { + String jsonString = objectMapper.writeValueAsString(additionalDetails); + JsonNode rootNode = objectMapper.readTree(jsonString); + + JsonNode node = rootNode.get(fieldToExtract); + List list = new ArrayList<>(); + if (node != null && node.isArray()) { + for (JsonNode idNode : node) { + list.add(idNode.asText()); + } + } + return list; + } catch (Exception e) { + log.error(e.getMessage() + fieldToExtract); + throw new CustomException(PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_CODE, PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_MESSAGE + fieldToExtract); + } + } + + /** + * Constructs a JSONPath expression used to filter assumptions based on the given parameters - + * campaign type, distribution process, registration process, resource distribution strategy, + * and whether registration and distribution are together match the provided values. + * + * @param campaignType The type of campaign to filter by (e.g., "Health", "Education"). + * @param distributionProcess The process of distribution to filter by (e.g., "Central", "Decentralized"). + * @param registrationProcess The registration process to filter by (e.g., "Online", "In-Person"). + * @param resourceDistributionStrategyCode The strategy code for resource distribution to filter by (e.g., "Strategy1"). + * @param isRegistrationAndDistributionTogether Whether registration and distribution are combined, to filter by ("true"/"false"). + * @return A JSONPath expression string that filters assumptions based on the given criteria. + */ + public String createJsonPathForAssumption( + String campaignType, + String distributionProcess, + String registrationProcess, + String resourceDistributionStrategyCode, + String isRegistrationAndDistributionTogether + ) { + + StringBuilder jsonPathFilters = new StringBuilder(JSONPATH_FILTER_PREFIX); + jsonPathFilters.append(JSON_PATH_FILTER_CAMPAIGN_TYPE).append(EQUALS).append(SINGLE_QUOTE).append(StringEscapeUtils.escapeJson(campaignType)).append(SINGLE_QUOTE) + .append(AND).append(JSON_PATH_FILTER_DISTRIBUTION_PROCESS).append(EQUALS).append(SINGLE_QUOTE).append(StringEscapeUtils.escapeJson(distributionProcess)).append(SINGLE_QUOTE) + .append(AND).append(JSON_PATH_FILTER_REGISTRATION_PROCESS).append(EQUALS).append(SINGLE_QUOTE).append(StringEscapeUtils.escapeJson(registrationProcess)).append(SINGLE_QUOTE) + .append(AND).append(JSON_PATH_FILTER_RESOURCE_DISTRIBUTION_STRATEGY_CODE).append(EQUALS).append(SINGLE_QUOTE).append(StringEscapeUtils.escapeJson(resourceDistributionStrategyCode)).append(SINGLE_QUOTE) + .append(AND).append(JSON_PATH_FILTER_IS_REGISTRATION_AND_DISTRIBUTION_TOGETHER).append(EQUALS).append(SINGLE_QUOTE).append(StringEscapeUtils.escapeJson(isRegistrationAndDistributionTogether)).append(SINGLE_QUOTE) + .append(JSONPATH_FILTER_SUFFIX); + + return JSON_ROOT_PATH + MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_ASSUMPTION + jsonPathFilters + FILTER_ALL_ASSUMPTIONS; + } + + + /** + * Searches the plan config based on the plan config id provided + * + * @param planConfigId the plan config id to validate + * @param tenantId the tenant id of the plan config + * @return list of planConfiguration for the provided plan config id + */ + public List searchPlanConfigId(String planConfigId, String tenantId) { + List planConfigurations = planConfigurationRepository.search(PlanConfigurationSearchCriteria.builder() + .id(planConfigId) + .tenantId(tenantId) + .build()); + + return planConfigurations; + } + + /** + * This method returns the planConfigName for the provided planConfig id + * + * @param tenantId + * @param planConfigId + */ + public String getPlanConfigName(String tenantId, String planConfigId) { + + List planConfigsFromSearch = searchPlanConfigId(planConfigId, tenantId); + return planConfigsFromSearch.get(0).getName(); + } + + /** + * Validates the user information within the provided PlanConfigurationRequest. + * + * @param requestInfo the request info containing the user information to be validated + * @throws CustomException if the user information is missing in the request + */ + public void validateUserInfo(RequestInfo requestInfo) + { + if (ObjectUtils.isEmpty(requestInfo.getUserInfo())) { + log.error(USERINFO_MISSING_MESSAGE); + throw new CustomException(USERINFO_MISSING_CODE, USERINFO_MISSING_MESSAGE); + } + } + + /** + * This is a helper method to get the lowest and highest hierarchy for microplan from MDMS + * + * @param mdmsData the mdms data + * @return returns the lowest and highest hierarchy for microplan + */ + public Map getMicroplanHierarchy(Object mdmsData) { + + String jsonPathForMicroplanHierarchy = JSON_ROOT_PATH + MDMS_ADMIN_CONSOLE_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_HIERARCHY_SCHEMA + HIERARCHY_CONFIG_FOR_MICROPLAN; + + List> hierarchyForMicroplan; + + try { + log.info(jsonPathForMicroplanHierarchy); + hierarchyForMicroplan = JsonPath.read(mdmsData, jsonPathForMicroplanHierarchy); + } catch (Exception e) { + log.error(e.getMessage()); + throw new CustomException(JSONPATH_ERROR_CODE, JSONPATH_ERROR_MESSAGE); + } + + Map hierarchyMap = new HashMap<>(); + hierarchyMap.put(LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN, hierarchyForMicroplan.get(0).get(LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN).toString().toLowerCase()); + hierarchyMap.put(HIGHEST_HIERARCHY_FIELD_FOR_MICROPLAN, hierarchyForMicroplan.get(0).get(HIGHEST_HIERARCHY_FIELD_FOR_MICROPLAN).toString().toLowerCase()); + + return hierarchyMap; + } + + /** + * Checks if the setup process is completed based on the workflow action in the plan configuration. + * + * @param planConfiguration The plan configuration to check. + * @return true if the setup is completed, otherwise false. + */ + public boolean isSetupCompleted(PlanConfiguration planConfiguration) { + if(!ObjectUtils.isEmpty(planConfiguration.getWorkflow())) + return Objects.equals(planConfiguration.getWorkflow().getAction(), SETUP_COMPLETED_ACTION); + + return false; + } + + /** + * Checks if the setup process is completed based on the workflow action in the plan configuration. + * + * @param planConfiguration The plan configuration to check. + * @return true if the setup is completed, otherwise false. + */ + public boolean checkForEmptyOperationsOrAssumptions(PlanConfiguration planConfiguration) { + return !ObjectUtils.isEmpty(planConfiguration.getOperations()) && !ObjectUtils.isEmpty(planConfiguration.getAssumptions()); + } + + + /** + * Adds or updates the provided fields in the additional details object. + * + * @param additionalDetails the additional details object to be updated. + * @param fieldsToBeUpdated map of field to be updated and it's updated value. + * @return returns the updated additional details object. + */ + + public Map updateFieldInAdditionalDetails(Object additionalDetails, Map fieldsToBeUpdated) { + try { + + // Get or create the additionalDetails as an ObjectNode + ObjectNode objectNode = (additionalDetails == null || additionalDetails instanceof NullNode) + ? objectMapper.createObjectNode() + : objectMapper.convertValue(additionalDetails, ObjectNode.class); + + // Update or add the field in additional details object + fieldsToBeUpdated.forEach((key, value) -> objectNode.set(key, objectMapper.valueToTree(value))); + + // Convert updated ObjectNode back to a Map + return objectMapper.convertValue(objectNode, Map.class); + + } catch (Exception e) { + throw new CustomException(ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE, ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE + e); + } + } + + public void sortOperationsByExecutionOrder(List planConfigurations) { + for (PlanConfiguration planConfiguration : planConfigurations) { + List operations = planConfiguration.getOperations(); + if (!ObjectUtils.isEmpty(operations)) { + operations.sort(Comparator.comparing(Operation::getExecutionOrder)); + } + } + } + +} diff --git a/health-services/plan-service/src/main/java/digit/util/FacilityUtil.java b/health-services/plan-service/src/main/java/digit/util/FacilityUtil.java new file mode 100644 index 00000000000..025d4ea6bb6 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/FacilityUtil.java @@ -0,0 +1,84 @@ +package digit.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.config.Configuration; +import digit.web.models.PlanFacilityRequest; +import digit.web.models.facility.FacilityResponse; +import digit.web.models.facility.FacilitySearchCriteria; +import digit.web.models.facility.FacilitySearchRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_FROM_FACILITY; + +@Slf4j +@Component +public class FacilityUtil { + + private RestTemplate restTemplate; + private Configuration configs; + private ObjectMapper mapper; + + public FacilityUtil(RestTemplate restTemplate, Configuration configs, ObjectMapper mapper) { + this.restTemplate = restTemplate; + this.configs = configs; + this.mapper = mapper; + } + + public FacilityResponse fetchFacilityData(PlanFacilityRequest planFacilityRequest) { + String baseUri = configs.getFacilityHost()+ configs.getFacilitySearchEndPoint(); + + // Retrieve tenantId from planFacilityRequest + String tenantId = planFacilityRequest.getPlanFacility().getTenantId(); + + // Retrieve the limit and offset from the configuration + int limit = configs.getDefaultLimit(); + int offset = configs.getDefaultOffset(); + + // Use UriComponentsBuilder to construct the URI with query parameters + String uri = UriComponentsBuilder.fromHttpUrl(baseUri) + .queryParam("tenantId", tenantId) + .queryParam("limit", limit) + .queryParam("offset", offset) + .toUriString(); + + FacilitySearchRequest facilitySearchRequest = getFacilitySearchRequest(planFacilityRequest); + FacilityResponse facilityResponse = new FacilityResponse(); + Object response = new HashMap<>(); + try { + // Use postForObject to send the request with the URI containing query params + response = restTemplate.postForObject(uri, facilitySearchRequest, Map.class); + facilityResponse = mapper.convertValue(response , FacilityResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_FACILITY, e); + } + log.info(facilityResponse.toString()); + return facilityResponse; + } + + private FacilitySearchRequest getFacilitySearchRequest(PlanFacilityRequest planFacilityRequest) { + // Retrieve facilityId,requestInfo from planFacilityRequest + String facilityId = planFacilityRequest.getPlanFacility().getFacilityId(); + RequestInfo requestInfo = planFacilityRequest.getRequestInfo(); + + FacilitySearchCriteria searchCriteria = FacilitySearchCriteria.builder() + .id(Collections.singletonList(facilityId)) + .build(); + + return FacilitySearchRequest.builder() + .requestInfo(requestInfo) + .facilitySearchCriteria(searchCriteria) + .build(); + } + + + +} diff --git a/health-services/plan-service/src/main/java/digit/util/MdmsUtil.java b/health-services/plan-service/src/main/java/digit/util/MdmsUtil.java index fb0f6560908..a4979c5da52 100644 --- a/health-services/plan-service/src/main/java/digit/util/MdmsUtil.java +++ b/health-services/plan-service/src/main/java/digit/util/MdmsUtil.java @@ -2,7 +2,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import digit.config.Configuration; + import java.util.LinkedList; + import lombok.extern.slf4j.Slf4j; import org.egov.common.contract.request.RequestInfo; import org.egov.mdms.model.*; @@ -38,11 +40,11 @@ public Object fetchMdmsData(RequestInfo requestInfo, String tenantId) { StringBuilder uri = new StringBuilder(); uri.append(configs.getMdmsHost()).append(configs.getMdmsEndPoint()); MdmsCriteriaReq mdmsCriteriaReq = getMdmsRequest(requestInfo, tenantId); - Object mdmsResponseMap = new HashMap<>(); + Object mdmsResponseMap = new HashMap<>(); MdmsResponse mdmsResponse = new MdmsResponse(); try { - mdmsResponseMap = restTemplate.postForObject(uri.toString(), mdmsCriteriaReq, Map.class); - mdmsResponse = mapper.convertValue(mdmsResponseMap , MdmsResponse.class); + mdmsResponseMap = restTemplate.postForObject(uri.toString(), mdmsCriteriaReq, Map.class); + mdmsResponse = mapper.convertValue(mdmsResponseMap, MdmsResponse.class); } catch (Exception e) { log.error(ERROR_WHILE_FETCHING_FROM_MDMS, e); } @@ -55,18 +57,42 @@ public Object fetchMdmsData(RequestInfo requestInfo, String tenantId) { return result; } + /** + * This method constructs the criteria request for MDMS Api call + * + * @param requestInfo requestInfo from the provided request + * @param tenantId tenant id from the provided request + * @return Returns the mdms criteria request + */ public MdmsCriteriaReq getMdmsRequest(RequestInfo requestInfo, String tenantId) { ModuleDetail assumptionModuleDetail = getPlanModuleDetail(); + ModuleDetail adminConsoleModuleDetail = getAdminConsoleModuleDetail(); List moduleDetails = new LinkedList<>(); moduleDetails.add(assumptionModuleDetail); + moduleDetails.add(adminConsoleModuleDetail); MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId).build(); return MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria).requestInfo(requestInfo).build(); } + private ModuleDetail getAdminConsoleModuleDetail() { + List adminConsoleMasters = new ArrayList<>(); + + MasterDetail hierarchyConfigMaster = MasterDetail.builder().name(MDMS_MASTER_HIERARCHY_SCHEMA).build(); + + adminConsoleMasters.add(hierarchyConfigMaster); + + return ModuleDetail.builder().masterDetails(adminConsoleMasters).moduleName(MDMS_ADMIN_CONSOLE_MODULE_NAME).build(); + } + + /** + * This method constructs module detail object for 'hcm-microplanning' module + * + * @return Returns the module details for 'hcm-microplanning' module + */ private ModuleDetail getPlanModuleDetail() { List assumptionMasterDetails = new ArrayList<>(); diff --git a/health-services/plan-service/src/main/java/digit/util/MdmsV2Util.java b/health-services/plan-service/src/main/java/digit/util/MdmsV2Util.java new file mode 100644 index 00000000000..dad22d39d35 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/MdmsV2Util.java @@ -0,0 +1,76 @@ +package digit.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import digit.config.Configuration; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.client.RestTemplate; +import digit.web.models.mdmsV2.*; + +import java.util.*; + +import static digit.config.ServiceConstants.*; + +@Slf4j +@Component +public class MdmsV2Util { + + private RestTemplate restTemplate; + + private ObjectMapper objectMapper; + + private Configuration configs; + + public MdmsV2Util(RestTemplate restTemplate, ObjectMapper objectMapper, Configuration configs) + { + this.restTemplate = restTemplate; + this.objectMapper = objectMapper; + this.configs = configs; + } + + public List fetchMdmsV2Data(RequestInfo requestInfo, String tenantId, String schemaCode, String uniqueIdentifier) + { + StringBuilder uri = getMdmsV2Uri(); + MdmsCriteriaReqV2 mdmsCriteriaReqV2 = getMdmsV2Request(requestInfo, tenantId, schemaCode, uniqueIdentifier); + MdmsResponseV2 mdmsResponseV2 = null; + try { + mdmsResponseV2 = restTemplate.postForObject(uri.toString(), mdmsCriteriaReqV2, MdmsResponseV2.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_MDMS, e); + } + + if(ObjectUtils.isEmpty(mdmsResponseV2.getMdms())) + { + log.error(NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE + " - " + tenantId); + throw new CustomException(NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE, NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE); + } + + return mdmsResponseV2.getMdms(); + } + + private StringBuilder getMdmsV2Uri() + { + StringBuilder uri = new StringBuilder(); + return uri.append(configs.getMdmsHost()).append(configs.getMdmsV2EndPoint()); + } + + private MdmsCriteriaReqV2 getMdmsV2Request(RequestInfo requestInfo, String tenantId, String schemaCode, String uniqueIdentifier) + { + MdmsCriteriaV2 mdmsCriteriaV2 = MdmsCriteriaV2.builder() + .tenantId(tenantId) + .schemaCode(schemaCode) + .limit(configs.getDefaultLimit()) + .offset(configs.getDefaultOffset()).build(); + + if(!ObjectUtils.isEmpty(uniqueIdentifier)) + mdmsCriteriaV2.setUniqueIdentifiers(Collections.singletonList(uniqueIdentifier)); + + return MdmsCriteriaReqV2.builder() + .requestInfo(requestInfo) + .mdmsCriteriaV2(mdmsCriteriaV2).build(); + } + +} diff --git a/health-services/plan-service/src/main/java/digit/util/QueryUtil.java b/health-services/plan-service/src/main/java/digit/util/QueryUtil.java index 84817b60870..070f442a897 100644 --- a/health-services/plan-service/src/main/java/digit/util/QueryUtil.java +++ b/health-services/plan-service/src/main/java/digit/util/QueryUtil.java @@ -1,7 +1,15 @@ package digit.util; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; +import digit.config.Configuration; +import org.egov.tracer.model.CustomException; +import org.postgresql.util.PGobject; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -11,10 +19,17 @@ import static digit.config.ServiceConstants.DOT_REGEX; import static digit.config.ServiceConstants.DOT_SEPARATOR; - +@Component public class QueryUtil { - private QueryUtil(){} + private Configuration config; + + private ObjectMapper objectMapper; + + private QueryUtil(Configuration config, ObjectMapper objectMapper) { + this.config = config; + this.objectMapper = objectMapper; + } private static final Gson gson = new Gson(); @@ -22,13 +37,14 @@ private QueryUtil(){} * This method aids in adding "WHERE" clause and "AND" condition depending on preparedStatementList i.e., * if preparedStatementList is empty, it will understand that it is the first clause being added so it * will add "WHERE" to the query and otherwise it will + * * @param query * @param preparedStmtList */ - public static void addClauseIfRequired(StringBuilder query, List preparedStmtList){ - if(preparedStmtList.isEmpty()){ + public void addClauseIfRequired(StringBuilder query, List preparedStmtList) { + if (preparedStmtList.isEmpty()) { query.append(" WHERE "); - }else{ + } else { query.append(" AND "); } } @@ -36,10 +52,11 @@ public static void addClauseIfRequired(StringBuilder query, List prepare /** * This method returns a string with placeholders equal to the number of values that need to be put inside * "IN" clause + * * @param size * @return */ - public static String createQuery(Integer size) { + public String createQuery(Integer size) { StringBuilder builder = new StringBuilder(); IntStream.range(0, size).forEach(i -> { @@ -53,40 +70,48 @@ public static String createQuery(Integer size) { /** * This method adds a set of String values into preparedStatementList + * * @param preparedStmtList * @param ids */ - public static void addToPreparedStatement(List preparedStmtList, Set ids) { + public void addToPreparedStatement(List preparedStmtList, Set ids) { ids.forEach(id -> { preparedStmtList.add(id); }); } + public void addToPreparedStatement(List preparedStmtList, List ids) { + ids.forEach(id -> { + preparedStmtList.add(id); + }); + } /** * This method appends order by clause to the query + * * @param query * @param orderByClause * @return */ - public static String addOrderByClause(String query, String orderByClause){ + public String addOrderByClause(String query, String orderByClause) { return query + orderByClause; } /** * This method prepares partial json string from the filter map to query on jsonb column + * * @param filterMap * @return */ - public static String preparePartialJsonStringFromFilterMap(Map filterMap) { + public String preparePartialJsonStringFromFilterMap(Map filterMap) { Map queryMap = new HashMap<>(); filterMap.keySet().forEach(key -> { - if(key.contains(DOT_SEPARATOR)){ + if (key.contains(DOT_SEPARATOR)) { String[] keyArray = key.split(DOT_REGEX); Map nestedQueryMap = new HashMap<>(); prepareNestedQueryMap(0, keyArray, nestedQueryMap, filterMap.get(key)); queryMap.put(keyArray[0], nestedQueryMap.get(keyArray[0])); - } else{ + } else { queryMap.put(key, filterMap.get(key)); } }); @@ -100,14 +125,15 @@ public static String preparePartialJsonStringFromFilterMap(Map f * Tail recursive method to prepare n-level nested partial json for queries on nested data in * master data. For e.g. , if the key is in the format a.b.c, it will construct a nested json * object of the form - {"a":{"b":{"c": "value"}}} + * * @param index * @param nestedKeyArray * @param currentQueryMap * @param value */ - private static void prepareNestedQueryMap(int index, String[] nestedKeyArray, Map currentQueryMap, String value) { + private void prepareNestedQueryMap(int index, String[] nestedKeyArray, Map currentQueryMap, String value) { // Return when all levels have been reached. - if(index == nestedKeyArray.length) + if (index == nestedKeyArray.length) return; // For the final level simply put the value in the map. @@ -123,4 +149,43 @@ else if (index == nestedKeyArray.length - 1) { prepareNestedQueryMap(index + 1, nestedKeyArray, (Map) currentQueryMap.get(nestedKeyArray[index]), value); } + /** + * This method adds pagination to the query + * + * @param query + * @param preparedStmtList + * @return + */ + public String getPaginatedQuery(String query, List preparedStmtList) { + StringBuilder paginatedQuery = new StringBuilder(query); + + // Append offset + paginatedQuery.append(" OFFSET ? "); + preparedStmtList.add(config.getDefaultOffset()); + + // Append limit + paginatedQuery.append(" LIMIT ? "); + preparedStmtList.add(config.getDefaultLimit()); + + return paginatedQuery.toString(); + } + + /** + * This method is used to extract and parse JSON data into a JsonNode object + * + * @param pGobject postgreSQL specific object + * @return returns a JsonNode + */ + public JsonNode getAdditionalDetail(PGobject pGobject) { + JsonNode additionalDetail = null; + + try { + if (!ObjectUtils.isEmpty(pGobject)) { + additionalDetail = objectMapper.readTree(pGobject.getValue()); + } + } catch (IOException e) { + throw new CustomException("PARSING_ERROR", "Failed to parse additionalDetails object"); + } + return additionalDetail; + } } diff --git a/health-services/plan-service/src/main/java/digit/util/ServiceUtil.java b/health-services/plan-service/src/main/java/digit/util/ServiceUtil.java index 51959990534..19c7bebeea7 100644 --- a/health-services/plan-service/src/main/java/digit/util/ServiceUtil.java +++ b/health-services/plan-service/src/main/java/digit/util/ServiceUtil.java @@ -8,16 +8,5 @@ @Component public class ServiceUtil { - /** - * Validates the given input string against the provided regex pattern. - * - * @param patternString the regex pattern to validate against - * @param inputString the input string to be validated - * @return true if the input string matches the regex pattern, false otherwise - */ - public Boolean validateStringAgainstRegex(String patternString, String inputString) { - Pattern pattern = Pattern.compile(patternString); - Matcher matcher = pattern.matcher(inputString); - return matcher.matches(); - } + } diff --git a/health-services/plan-service/src/main/java/digit/util/UserUtil.java b/health-services/plan-service/src/main/java/digit/util/UserUtil.java new file mode 100644 index 00000000000..0e60073313e --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/util/UserUtil.java @@ -0,0 +1,73 @@ +package digit.util; + +import digit.config.Configuration; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.user.UserDetailResponse; +import org.egov.common.contract.user.UserSearchRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; + +import static digit.config.ServiceConstants.ERROR_WHILE_FETCHING_FROM_USER_SERVICE; + +@Slf4j +@Component +public class UserUtil { + + private Configuration config; + + private RestTemplate restTemplate; + + public UserUtil(RestTemplate restTemplate, Configuration config) { + this.restTemplate = restTemplate; + this.config = config; + } + + /** + * This method fetches user details from User Service for the provided search request + * + * @param userSearchReq Search request to search for user detail response + */ + public UserDetailResponse fetchUserDetail(UserSearchRequest userSearchReq) { + + UserDetailResponse userDetailResponse = new UserDetailResponse(); + + try { + userDetailResponse = restTemplate.postForObject(getUserServiceUri().toString(), userSearchReq, UserDetailResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_USER_SERVICE, e); + } + + return userDetailResponse; + } + + /** + * This method creates the uri for User service + * + * @return uri for user detail search + */ + private StringBuilder getUserServiceUri() { + return new StringBuilder().append(config.getUserServiceHost()).append(config.getUserSearchEndPoint()); + } + + /** + * This method creates the search request body for user detail search + * + * @param requestInfo Request Info from the request body + * @param employeeId Employee id for the provided plan employee assignment request + * @param tenantId Tenant id from the plan employee assignment request + * @return Search request body for user detail search + */ + public UserSearchRequest getUserSearchReq(RequestInfo requestInfo, String employeeId, String tenantId) { + + UserSearchRequest userSearchRequest = new UserSearchRequest(); + + userSearchRequest.setRequestInfo(requestInfo); + userSearchRequest.setTenantId(tenantId); + userSearchRequest.setUuid(Collections.singletonList(employeeId)); + + return userSearchRequest; + } +} diff --git a/health-services/plan-service/src/main/java/digit/web/controllers/PlanConfigController.java b/health-services/plan-service/src/main/java/digit/web/controllers/PlanConfigController.java index 4729cdd736e..d50610832e9 100644 --- a/health-services/plan-service/src/main/java/digit/web/controllers/PlanConfigController.java +++ b/health-services/plan-service/src/main/java/digit/web/controllers/PlanConfigController.java @@ -3,7 +3,6 @@ import digit.service.PlanConfigurationService; import digit.util.ResponseInfoFactory; -import digit.web.models.PlanConfiguration; import digit.web.models.PlanConfigurationRequest; import digit.web.models.PlanConfigurationResponse; import digit.web.models.PlanConfigurationSearchRequest; diff --git a/health-services/plan-service/src/main/java/digit/web/controllers/PlanController.java b/health-services/plan-service/src/main/java/digit/web/controllers/PlanController.java index 2381b009d40..43f50e6a5af 100644 --- a/health-services/plan-service/src/main/java/digit/web/controllers/PlanController.java +++ b/health-services/plan-service/src/main/java/digit/web/controllers/PlanController.java @@ -30,7 +30,7 @@ public PlanController(PlanService planService) { * @return */ @RequestMapping(value = "/_create", method = RequestMethod.POST) - public ResponseEntity createPost(@Valid @RequestBody PlanRequest body) { + public ResponseEntity create(@Valid @RequestBody PlanRequest body) { PlanResponse planResponse = planService.createPlan(body); return ResponseEntity.status(HttpStatus.ACCEPTED).body(planResponse); } @@ -41,7 +41,7 @@ public ResponseEntity createPost(@Valid @RequestBody PlanRequest b * @return */ @RequestMapping(value = "/_search", method = RequestMethod.POST) - public ResponseEntity searchPost(@Valid @RequestBody PlanSearchRequest body) { + public ResponseEntity search(@Valid @RequestBody PlanSearchRequest body) { PlanResponse planResponse = planService.searchPlan(body); return ResponseEntity.status(HttpStatus.OK).body(planResponse); } @@ -52,9 +52,20 @@ public ResponseEntity searchPost(@Valid @RequestBody PlanSearchReq * @return */ @RequestMapping(value = "/_update", method = RequestMethod.POST) - public ResponseEntity updatePost(@Valid @RequestBody PlanRequest body) { + public ResponseEntity update(@Valid @RequestBody PlanRequest body) { PlanResponse planResponse = planService.updatePlan(body); return ResponseEntity.status(HttpStatus.ACCEPTED).body(planResponse); } + /** + * Request handler for serving bulk plan update requests + * @param body + * @return + */ + @RequestMapping(value = "/bulk/_update", method = RequestMethod.POST) + public ResponseEntity bulkUpdate(@Valid @RequestBody BulkPlanRequest body) { + PlanResponse planResponse = planService.bulkUpdate(body); + return ResponseEntity.status(HttpStatus.CREATED).body(planResponse); + } + } diff --git a/health-services/plan-service/src/main/java/digit/web/controllers/PlanEmployeeController.java b/health-services/plan-service/src/main/java/digit/web/controllers/PlanEmployeeController.java new file mode 100644 index 00000000000..db707f9e872 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/controllers/PlanEmployeeController.java @@ -0,0 +1,62 @@ +package digit.web.controllers; + +import digit.service.PlanEmployeeService; +import digit.web.models.*; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +public class PlanEmployeeController { + + PlanEmployeeService planEmployeeService; + + public PlanEmployeeController(PlanEmployeeService planEmployeeService) + { + this.planEmployeeService = planEmployeeService; + } + + /** + * Request handler for serving plan employee assignment create requests + * @param body + * @return + */ + @RequestMapping(value = "/employee/_create", method = RequestMethod.POST) + public ResponseEntity employeeCreatePost(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody PlanEmployeeAssignmentRequest body) { + + PlanEmployeeAssignmentResponse response = planEmployeeService.create(body); + return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); + + } + + /** + * Request handler for serving plan employee assignment search requests + * @param body + * @return + */ + @RequestMapping(value = "/employee/_search", method = RequestMethod.POST) + public ResponseEntity employeeSearchPost(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody PlanEmployeeAssignmentSearchRequest body) { + + PlanEmployeeAssignmentResponse response = planEmployeeService.search(body); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + /** + * Request handler for serving plan employee assignment update requests + * @param body + * @return + */ + @RequestMapping(value = "/employee/_update", method = RequestMethod.POST) + public ResponseEntity employeeUpdatePost(@Parameter(in = ParameterIn.DEFAULT, description = "", schema = @Schema()) @Valid @RequestBody PlanEmployeeAssignmentRequest body) { + + PlanEmployeeAssignmentResponse response = planEmployeeService.update(body); + return ResponseEntity.status(HttpStatus.ACCEPTED).body(response); + } +} diff --git a/health-services/plan-service/src/main/java/digit/web/controllers/PlanFacilityController.java b/health-services/plan-service/src/main/java/digit/web/controllers/PlanFacilityController.java new file mode 100644 index 00000000000..bca90f5c544 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/controllers/PlanFacilityController.java @@ -0,0 +1,61 @@ +package digit.web.controllers; + +import digit.service.PlanFacilityService; +import digit.web.models.*; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Validated +@Controller +@RequestMapping("plan") +public class PlanFacilityController { + + private PlanFacilityService planFacilityService; + + public PlanFacilityController(PlanFacilityService planFacilityService) { + this.planFacilityService = planFacilityService; + } + + /** + * Request handler for serving plan facility create requests + * + * @param planFacilityRequest + * @return + */ + @RequestMapping(value = "/facility/_create", method = RequestMethod.POST) + public ResponseEntity createPlanFacility(@Valid @RequestBody PlanFacilityRequest planFacilityRequest) { + PlanFacilityResponse planFacilityResponse = planFacilityService.createPlanFacility(planFacilityRequest); + return ResponseEntity.status(HttpStatus.ACCEPTED).body(planFacilityResponse); + } + + /** + * Request handler for serving plan facility search requests + * + * @param planFacilityRequest + * @return + */ + @RequestMapping(value = "/facility/_search", method = RequestMethod.POST) + public ResponseEntity searchPlanFacility(@Valid @RequestBody PlanFacilitySearchRequest planFacilityRequest) { + PlanFacilityResponse planFacilityResponse = planFacilityService.searchPlanFacility(planFacilityRequest); + return ResponseEntity.status(HttpStatus.OK).body(planFacilityResponse); + } + + /** + * Request handler for serving plan facility update requests + * + * @param planFacilityRequest + * @return + */ + @RequestMapping(value = "/facility/_update", method = RequestMethod.POST) + public ResponseEntity updatePlanFacility(@Valid @RequestBody PlanFacilityRequest planFacilityRequest) { + PlanFacilityResponse planFacilityResponse = planFacilityService.updatePlanFacility(planFacilityRequest); + return ResponseEntity.status(HttpStatus.ACCEPTED).body(planFacilityResponse); + } + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/Assumption.java b/health-services/plan-service/src/main/java/digit/web/models/Assumption.java index a4b7e3ad01f..81486894093 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/Assumption.java +++ b/health-services/plan-service/src/main/java/digit/web/models/Assumption.java @@ -38,10 +38,19 @@ public class Assumption { @NotNull @Valid @DecimalMin(value = "0.01", inclusive = true, message = "The Assumption value must be greater than 0") - @DecimalMax(value = "9999999999.99", inclusive = true, message = "The assumption value must not exceed 10 digits in total, including up to 2 decimal places.") - @Digits(integer = 10, fraction = 2, message = "The Assumption value must have up to 10 digits and up to 2 decimal points") + @DecimalMax(value = "1000.00", inclusive = true, message = "The assumption value must not exceed 4 digits in total, including up to 2 decimal places.") + @Digits(integer = 4, fraction = 2, message = "The Assumption value must have up to 10 digits and up to 2 decimal points") private BigDecimal value = null; + @JsonProperty("source") + @NotNull(message = "Source cannot be null. Please specify a valid source.") + private Source source = null; + + @JsonProperty("category") + @NotNull + @Size(min = 2, max = 64) + private String category = null; + @JsonProperty("active") @NotNull private Boolean active = true; diff --git a/health-services/plan-service/src/main/java/digit/web/models/BulkPlanRequest.java b/health-services/plan-service/src/main/java/digit/web/models/BulkPlanRequest.java new file mode 100644 index 00000000000..f12bbc1ee8f --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/BulkPlanRequest.java @@ -0,0 +1,33 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +import java.util.List; + +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BulkPlanRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("Plans") + @Valid + @NotNull + @NotEmpty + private List plans = null; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/Operation.java b/health-services/plan-service/src/main/java/digit/web/models/Operation.java index fbc647cfc1d..b12c44341fb 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/Operation.java +++ b/health-services/plan-service/src/main/java/digit/web/models/Operation.java @@ -40,14 +40,30 @@ public class Operation { @JsonProperty("assumptionValue") @NotNull - @Size(min = 2, max = 256) + @Size(min = 1, max = 256) private String assumptionValue = null; @JsonProperty("output") @NotNull - @Size(min = 1, max = 64) + @Size(min = 1, max = 256) private String output = null; + @JsonProperty("showOnEstimationDashboard") + @NotNull + private Boolean showOnEstimationDashboard = true; + + @JsonProperty("source") + @NotNull(message = "Source cannot be null. Please specify a valid source.") + private Source source = null; + + @JsonProperty("category") + @NotNull + @Size(min = 2, max = 64) + private String category = null; + + @JsonProperty("executionOrder") + private Integer executionOrder = null; + @JsonProperty("active") @NotNull private Boolean active = true; diff --git a/health-services/plan-service/src/main/java/digit/web/models/Pagination.java b/health-services/plan-service/src/main/java/digit/web/models/Pagination.java new file mode 100644 index 00000000000..6d3fd64df77 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/Pagination.java @@ -0,0 +1,35 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Pagination + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Pagination { + + @JsonIgnore + private String sortBy; + + @JsonIgnore + private String sortOrder; + + @JsonProperty("limit") + @Min(1) + @Max(50) + private Integer limit; + + @JsonProperty("offset") + @Min(0) + private Integer offset; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/Plan.java b/health-services/plan-service/src/main/java/digit/web/models/Plan.java index f1fb59a400e..3ff0e8ce87d 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/Plan.java +++ b/health-services/plan-service/src/main/java/digit/web/models/Plan.java @@ -1,12 +1,15 @@ package digit.web.models; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; +import java.util.Map; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @@ -35,14 +38,21 @@ public class Plan { @Size(min = 1, max = 64) private String locality = null; - @JsonProperty("executionPlanId") + @JsonProperty("campaignId") @Size(max = 64) - private String executionPlanId = null; + private String campaignId = null; @JsonProperty("planConfigurationId") @Size(max = 64) private String planConfigurationId = null; + @JsonProperty("status") + @Size(max = 64) + private String status = null; + + @JsonProperty("assignee") + private List assignee = null; + @JsonProperty("additionalDetails") private Object additionalDetails = null; @@ -61,4 +71,20 @@ public class Plan { @JsonProperty("auditDetails") private AuditDetails auditDetails = null; + @JsonProperty("jurisdictionMapping") + private Map jurisdictionMapping; + + @JsonIgnore + private String boundaryAncestralPath = null; + + @JsonIgnore + private boolean isRequestFromResourceEstimationConsumer; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + } diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanConfiguration.java b/health-services/plan-service/src/main/java/digit/web/models/PlanConfiguration.java index 9032da0bfed..50c43741d48 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/PlanConfiguration.java +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanConfiguration.java @@ -1,9 +1,6 @@ package digit.web.models; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonValue; -import jakarta.validation.constraints.NotEmpty; import java.util.ArrayList; import java.util.List; import jakarta.validation.Valid; @@ -11,6 +8,7 @@ import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Pattern; import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @@ -41,50 +39,40 @@ public class PlanConfiguration { @Size(min = 3, max = 128) private String name = null; - @JsonProperty("executionPlanId") + @JsonProperty("campaignId") @NotNull @Size(min = 2, max = 64) - @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Execution Plan Id must not contain only special characters") - private String executionPlanId = null; + @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Campaign Id must not contain only special characters") + private String campaignId = null; @JsonProperty("status") - @NotNull - private StatusEnum status = null; + private String status = null; @JsonProperty("files") - @NotNull - @NotEmpty @Valid private List files = new ArrayList<>(); @JsonProperty("assumptions") - @NotNull - @NotEmpty @Valid private List assumptions = new ArrayList<>(); @JsonProperty("operations") - @NotNull - @NotEmpty @Valid private List operations = new ArrayList<>(); @JsonProperty("resourceMapping") - @NotNull - @NotEmpty @Valid private List resourceMapping = new ArrayList<>(); @JsonProperty("auditDetails") private @Valid AuditDetails auditDetails; - /** - * The status used in the Plan Configuration - */ - public enum StatusEnum { - DRAFT , - GENERATED, - INVALID_DATA - } + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + } diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanConfigurationSearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/PlanConfigurationSearchCriteria.java index ab7c827c49a..1f0b8f6be6f 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/PlanConfigurationSearchCriteria.java +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanConfigurationSearchCriteria.java @@ -1,6 +1,5 @@ package digit.web.models; -import jakarta.validation.Valid; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; @@ -12,6 +11,8 @@ import lombok.Data; import lombok.Builder; +import java.util.List; + /** * PlanConfigurationSearchCriteria */ @@ -30,14 +31,17 @@ public class PlanConfigurationSearchCriteria { @JsonProperty("id") private String id = null; + @JsonProperty("ids") + private List ids = null; + @JsonProperty("name") private String name = null; - @JsonProperty("executionPlanId") - private String executionPlanId = null; + @JsonProperty("campaignId") + private String campaignId = null; @JsonProperty("status") - private String status = null; + private List status = null; @JsonProperty("userUuid") private String userUuid = null; @@ -50,5 +54,4 @@ public class PlanConfigurationSearchCriteria { @Min(1) @Max(50) private Integer limit; - } diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java new file mode 100644 index 00000000000..f56569c1349 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java @@ -0,0 +1,90 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +/** + * Plan + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanDTO { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("locality") + @Size(min = 1, max = 64) + private String locality = null; + + @JsonProperty("campaignId") + @Size(max = 64) + private String campaignId = null; + + @JsonProperty("planConfigurationId") + @Size(max = 64) + private String planConfigurationId = null; + + @JsonProperty("status") + @Size(max = 64) + private String status = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("jurisdictionMapping") + private Map jurisdictionMapping; + + @JsonProperty("activities") + @Valid + private List activities; + + @JsonProperty("resources") + @Valid + private List resources; + + @JsonProperty("targets") + @Valid + private List targets; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("boundaryAncestralPath") + private String boundaryAncestralPath = null; + + @JsonIgnore + private Boolean partnerAssignmentValidationEnabled; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignment.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignment.java new file mode 100644 index 00000000000..c27b1e1eb1b --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignment.java @@ -0,0 +1,71 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Set; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * PlanEmployeeAssignment + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignment { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + @Size(min = 2, max = 64) + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("employeeId") + @NotNull + @Size(min = 2, max = 64) + private String employeeId = null; + + @JsonProperty("role") + @NotNull + @Size(min = 2, max = 64) + private String role = null; + + @JsonProperty("hierarchyLevel") + @Size(min = 2, max = 64) + private String hierarchyLevel = null; + + @JsonProperty("jurisdiction") + @Valid + @NotEmpty + private Set jurisdiction = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("active") + private Boolean active = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentDTO.java new file mode 100644 index 00000000000..555d916c1ff --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentDTO.java @@ -0,0 +1,69 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * PlanEmployeeAssignmentDTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentDTO { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + @Size(min = 2, max = 64) + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("employeeId") + @NotNull + @Size(min = 2, max = 64) + private String employeeId = null; + + @JsonProperty("role") + @NotNull + @Size(min = 2, max = 64) + private String role = null; + + @JsonProperty("hierarchyLevel") + @Size(min = 2, max = 64) + private String hierarchyLevel = null; + + @JsonProperty("jurisdiction") + @Valid + @NotEmpty + private String jurisdiction = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("active") + private Boolean active = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentRequest.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentRequest.java new file mode 100644 index 00000000000..4361b16dde2 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentRequest.java @@ -0,0 +1,30 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + + +/** + * PlanEmployeeAssignmentRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("PlanEmployeeAssignment") + @Valid + private PlanEmployeeAssignment planEmployeeAssignment = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentRequestDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentRequestDTO.java new file mode 100644 index 00000000000..f21bc83a3e8 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentRequestDTO.java @@ -0,0 +1,30 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + + +/** + * PlanEmployeeAssignmentRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentRequestDTO { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("PlanEmployeeAssignment") + @Valid + private PlanEmployeeAssignmentDTO planEmployeeAssignmentDTO = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentResponse.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentResponse.java new file mode 100644 index 00000000000..6c6ecdc516c --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentResponse.java @@ -0,0 +1,36 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import jakarta.validation.Valid; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + + +/** + * PlanEmployeeAssignmentResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("PlanEmployeeAssignment") + @Valid + private List planEmployeeAssignment = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; +} + diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentSearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentSearchCriteria.java new file mode 100644 index 00000000000..22008b64135 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentSearchCriteria.java @@ -0,0 +1,72 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Set; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * PlanEmployeeAssignmentSearchCriteria + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentSearchCriteria { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @Size(min = 2, max = 100) + @NotNull + private String tenantId = null; + + @JsonProperty("employeeId") + private List employeeId = null; + + @JsonProperty("planConfigurationId") + @Size(max = 64) + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("planConfigurationStatus") + private Set planConfigurationStatus = null; + + @JsonProperty("role") + @Valid + private List role = null; + + @JsonProperty("hierarchyLevel") + private String hierarchyLevel = null; + + @JsonProperty("jurisdiction") + @Valid + private List jurisdiction = null; + + @JsonProperty("active") + @Builder.Default + private Boolean active = Boolean.TRUE; + + @JsonProperty("filterUniqueByPlanConfig") + @Builder.Default + private Boolean filterUniqueByPlanConfig = Boolean.FALSE; + + @JsonProperty("offset") + private Integer offset = null; + + @JsonProperty("limit") + private Integer limit = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentSearchRequest.java b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentSearchRequest.java new file mode 100644 index 00000000000..a8b3a40a41f --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanEmployeeAssignmentSearchRequest.java @@ -0,0 +1,29 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * PlanEmployeeAssignmentSearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanEmployeeAssignmentSearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("PlanEmployeeAssignmentSearchCriteria") + @Valid + private PlanEmployeeAssignmentSearchCriteria planEmployeeAssignmentSearchCriteria = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacility.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacility.java new file mode 100644 index 00000000000..87c024324ac --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacility.java @@ -0,0 +1,92 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Plan Facility + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacility { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + @Size(max = 64) + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("boundaryAncestralPath") + private String boundaryAncestralPath = null; + + @JsonProperty("facilityId") + @NotNull + @Size(max = 64) + private String facilityId = null; + + @JsonProperty("facilityName") + private String facilityName = null; + + @JsonProperty("residingBoundary") + @NotNull + @Size(min = 1, max = 64) + private String residingBoundary = null; + + @JsonProperty("serviceBoundaries") + @NotNull + @Valid + private List serviceBoundaries; + + @JsonIgnore + private List initiallySetServiceBoundaries; + + @JsonProperty("jurisdictionMapping") + private Map jurisdictionMapping; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("active") + @NotNull + private Boolean active = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + public PlanFacility addServiceBoundariesItem(String serviceBoundariesItem) { + if (this.serviceBoundaries == null) { + this.serviceBoundaries = new ArrayList<>(); + } + this.serviceBoundaries.add(serviceBoundariesItem); + return this; + } + + + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityDTO.java new file mode 100644 index 00000000000..771c602f760 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityDTO.java @@ -0,0 +1,79 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +/** + * Plan Facility DTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilityDTO { + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + @Size(max = 64) + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("boundaryAncestralPath") + private String boundaryAncestralPath = null; + + @JsonProperty("facilityId") + @NotNull + @Size(max = 64) + private String facilityId = null; + + @JsonProperty("residingBoundary") + @NotNull + @Size(min = 1, max = 64) + private String residingBoundary = null; + + // Changed List to String to store as JSON + @JsonProperty("serviceBoundaries") + @NotNull + @Size(min = 1) + private String serviceBoundaries = null; // Store as JSON string + + @JsonProperty("initiallySetServiceBoundaries") + private List initiallySetServiceBoundaries; + + @JsonProperty("facilityName") + private String facilityName = null; + + @JsonProperty("jurisdictionMapping") + private Map jurisdictionMapping; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("active") + @NotNull + private Boolean active = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityRequest.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityRequest.java new file mode 100644 index 00000000000..9eb2652d0a7 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityRequest.java @@ -0,0 +1,33 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * PlanFacilityRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilityRequest { + + @JsonProperty("RequestInfo") + @Valid + @NotNull + private RequestInfo requestInfo; + + @JsonProperty("PlanFacility") + @Valid + @NotNull + private PlanFacility planFacility; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityRequestDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityRequestDTO.java new file mode 100644 index 00000000000..a78b2ba0047 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityRequestDTO.java @@ -0,0 +1,33 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * PlanFacilityRequestDTO + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilityRequestDTO { + + @JsonProperty("RequestInfo") + @Valid + @NotNull + private RequestInfo requestInfo; + + @JsonProperty("PlanFacility") + @Valid + @NotNull + private PlanFacilityDTO planFacilityDTO; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityResponse.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityResponse.java new file mode 100644 index 00000000000..1ac3db052c6 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilityResponse.java @@ -0,0 +1,33 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; +import java.util.List; + +/** + * PlanFacilityResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilityResponse { + + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo = null; + + @JsonProperty("PlanFacility") + @Valid + private List planFacility = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacilitySearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilitySearchCriteria.java new file mode 100644 index 00000000000..a5a4e49c42c --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilitySearchCriteria.java @@ -0,0 +1,64 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilitySearchCriteria { + + @JsonProperty("ids") + private Set ids = null; + + @JsonProperty("tenantId") + @NotNull + private String tenantId = null; + + @JsonProperty("planConfigurationId") + @NotNull + private String planConfigurationId = null; + + @JsonProperty("planConfigurationName") + private String planConfigurationName = null; + + @JsonProperty("facilityName") + private String facilityName = null; + + @JsonProperty("facilityStatus") + private String facilityStatus = null; + + @JsonProperty("facilityType") + private String facilityType = null; + + @JsonProperty("residingBoundaries") + private List residingBoundaries = null; + + @JsonProperty("jurisdiction") + private List jurisdiction = null; + + @JsonProperty("facilityId") + private String facilityId = null; + + @JsonProperty("offset") + private Integer offset = null; + + @JsonProperty("limit") + private Integer limit = null; + + @JsonIgnore + private Map filtersMap = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanFacilitySearchRequest.java b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilitySearchRequest.java new file mode 100644 index 00000000000..65517af3d18 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanFacilitySearchRequest.java @@ -0,0 +1,34 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; + +/** + * PlanFacilitySearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanFacilitySearchRequest { + + @JsonProperty("RequestInfo") + @NotNull + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("PlanFacilitySearchCriteria") + @NotNull + @Valid + private PlanFacilitySearchCriteria planFacilitySearchCriteria = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanRequestDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanRequestDTO.java new file mode 100644 index 00000000000..1b60fca0696 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanRequestDTO.java @@ -0,0 +1,29 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * PlanCreateRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanRequestDTO { + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("Plan") + @Valid + private PlanDTO planDTO = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanResponse.java b/health-services/plan-service/src/main/java/digit/web/models/PlanResponse.java index bf08ba0059c..51e11ec94a5 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/PlanResponse.java +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanResponse.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; +import java.util.Map; + import org.egov.common.contract.response.ResponseInfo; import org.springframework.validation.annotation.Validated; import jakarta.validation.Valid; @@ -28,4 +30,12 @@ public class PlanResponse { @Valid private List plan = null; + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; + + @JsonProperty("StatusCount") + @Valid + private Map statusCount = null; + } diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java index 6e9cc9449d1..357b306fa8f 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java +++ b/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java @@ -1,15 +1,16 @@ package digit.web.models; -import java.util.Set; - import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.Data; import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Set; /** * PlanSearchCriteria @@ -29,14 +30,24 @@ public class PlanSearchCriteria { private String tenantId = null; @JsonProperty("locality") - private String locality = null; + private List locality = null; - @JsonProperty("executionPlanId") - private String executionPlanId = null; + @JsonProperty("campaignId") + private String campaignId = null; @JsonProperty("planConfigurationId") private String planConfigurationId = null; + @JsonProperty("status") + private String status = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("jurisdiction") + @Valid + private List jurisdiction = null; + @JsonProperty("offset") private Integer offset = null; diff --git a/health-services/plan-service/src/main/java/digit/web/models/RequestInfoWrapper.java b/health-services/plan-service/src/main/java/digit/web/models/RequestInfoWrapper.java new file mode 100644 index 00000000000..9613a753b82 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/RequestInfoWrapper.java @@ -0,0 +1,18 @@ +package digit.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Component +public class RequestInfoWrapper { + + @JsonProperty("RequestInfo") + private RequestInfo requestInfo; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/Resource.java b/health-services/plan-service/src/main/java/digit/web/models/Resource.java index 0b64b3108c1..d0d8ce2500b 100644 --- a/health-services/plan-service/src/main/java/digit/web/models/Resource.java +++ b/health-services/plan-service/src/main/java/digit/web/models/Resource.java @@ -25,7 +25,7 @@ public class Resource { @JsonProperty("resourceType") @NotNull - @Size(min = 2, max = 256) + @Size(min = 1, max = 256) private String resourceType = null; @JsonProperty("estimatedNumber") diff --git a/health-services/plan-service/src/main/java/digit/web/models/Source.java b/health-services/plan-service/src/main/java/digit/web/models/Source.java new file mode 100644 index 00000000000..e58bfba27be --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/Source.java @@ -0,0 +1,5 @@ +package digit.web.models; + +public enum Source { + MDMS, CUSTOM, VEHICLE; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundarySearchResponse.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundarySearchResponse.java new file mode 100644 index 00000000000..7f92e1f53c8 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundarySearchResponse.java @@ -0,0 +1,42 @@ +package digit.web.models.boundary; + +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.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; + +/** + * BoundarySearchResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundarySearchResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("TenantBoundary") + @Valid + private List tenantBoundary = null; + + + public BoundarySearchResponse addTenantBoundaryItem(HierarchyRelation tenantBoundaryItem) { + if (this.tenantBoundary == null) { + this.tenantBoundary = new ArrayList<>(); + } + this.tenantBoundary.add(tenantBoundaryItem); + return this; + } + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchy.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchy.java new file mode 100644 index 00000000000..45ab5a19141 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchy.java @@ -0,0 +1,30 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +/** + * BoundaryTypeHierarchy + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchy { + + @JsonProperty("boundaryType") + private String boundaryType = null; + + @JsonProperty("parentBoundaryType") + private String parentBoundaryType = null; + + @JsonProperty("active") + private Boolean active = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyDefinition.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyDefinition.java new file mode 100644 index 00000000000..6faf8ac0b49 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyDefinition.java @@ -0,0 +1,45 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * BoundaryTypeHierarchyDefinition + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchyDefinition { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("hierarchyType") + private String hierarchyType = null; + + @JsonProperty("boundaryHierarchy") + @Valid + private List boundaryHierarchy = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("boundaryHierarchyJsonNode") + private JsonNode boundaryHierarchyJsonNode = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyResponse.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyResponse.java new file mode 100644 index 00000000000..6372620c43c --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchyResponse.java @@ -0,0 +1,36 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * BoundaryTypeHierarchyResponse + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchyResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("totalCount") + private Integer totalCount = null; + + @JsonProperty("BoundaryHierarchy") + @Valid + private List boundaryHierarchy = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchCriteria.java new file mode 100644 index 00000000000..ba335f7f037 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchCriteria.java @@ -0,0 +1,39 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +/** + * BoundaryTypeHierarchySearchCriteria + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchySearchCriteria { + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + @NotNull + private String tenantId = null; + + @JsonProperty("hierarchyType") + @Size(min = 1, max = 100) + private String hierarchyType = null; + + @JsonProperty("offset") + private Integer offset; + + @JsonProperty("limit") + private Integer limit; + +} + diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchRequest.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchRequest.java new file mode 100644 index 00000000000..46cb6eb463a --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/BoundaryTypeHierarchySearchRequest.java @@ -0,0 +1,31 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * BoundaryTypeHierarchySearchRequest + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2023-10-16T17:02:11.361704+05:30[Asia/Kolkata]") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BoundaryTypeHierarchySearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("BoundaryTypeHierarchySearchCriteria") + @Valid + private BoundaryTypeHierarchySearchCriteria boundaryTypeHierarchySearchCriteria = null; + +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/EnrichedBoundary.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/EnrichedBoundary.java new file mode 100644 index 00000000000..c42af3737ce --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/EnrichedBoundary.java @@ -0,0 +1,42 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * EnrichedBoundary + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EnrichedBoundary { + + @JsonProperty("id") + private String id; + + @JsonProperty("code") + @NotNull + private String code; + + @JsonProperty("boundaryType") + private String boundaryType; + + @JsonProperty("children") + @Valid + private List children = null; + + @JsonIgnore + private String parent = null; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/boundary/HierarchyRelation.java b/health-services/plan-service/src/main/java/digit/web/models/boundary/HierarchyRelation.java new file mode 100644 index 00000000000..7a3e26f6595 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/boundary/HierarchyRelation.java @@ -0,0 +1,34 @@ +package digit.web.models.boundary; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import java.util.List; + +/** + * HierarchyRelation + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class HierarchyRelation { + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("hierarchyType") + private String hierarchyType = null; + + @JsonProperty("boundary") + @Valid + private List boundary = null; + + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/AdditionalField.java b/health-services/plan-service/src/main/java/digit/web/models/census/AdditionalField.java new file mode 100644 index 00000000000..65ae4222831 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/census/AdditionalField.java @@ -0,0 +1,48 @@ +package digit.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; + +/** + * AdditionalField + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AdditionalField { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("key") + @Valid + @NotNull + private String key = null; + + @JsonProperty("value") + @Valid + @NotNull + private BigDecimal value = null; + + @JsonProperty("showOnUi") + private Boolean showOnUi = Boolean.TRUE; + + @JsonProperty("editable") + private Boolean editable = Boolean.TRUE; + + @JsonProperty("order") + private Integer order = null; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/Census.java b/health-services/plan-service/src/main/java/digit/web/models/census/Census.java new file mode 100644 index 00000000000..e8f384d1e98 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/census/Census.java @@ -0,0 +1,139 @@ +package digit.web.models.census; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.List; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; + + +/** + * Census + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Census { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("tenantId") + @NotNull + private String tenantId = null; + + @JsonProperty("hierarchyType") + @NotNull + private String hierarchyType = null; + + @JsonProperty("boundaryCode") + @NotNull + private String boundaryCode = null; + + @JsonProperty("assignee") + @Size(max = 64) + private String assignee = null; + + @JsonProperty("status") + @Size(max = 64) + private String status = null; + + @JsonProperty("type") + @NotNull + private TypeEnum type = null; + + @JsonProperty("totalPopulation") + @NotNull + private Long totalPopulation = null; + + @JsonProperty("populationByDemographics") + @Valid + private List populationByDemographics = null; + + @JsonProperty("additionalFields") + @Valid + private List additionalFields = null; + + @JsonProperty("effectiveFrom") + private Long effectiveFrom = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("source") + @NotNull + private String source = null; + + @JsonIgnore + private List boundaryAncestralPath = null; + + @JsonIgnore + @Builder.Default + private Boolean partnerAssignmentValidationEnabled = Boolean.TRUE; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("auditDetails") + private @Valid AuditDetails auditDetails; + + /** + * Gets or Sets type + */ + public enum TypeEnum { + PEOPLE("people"), + ANIMALS("animals"), + PLANTS("plants"), + OTHER("other"); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String text) { + for (TypeEnum b : TypeEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + } + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/CensusResponse.java b/health-services/plan-service/src/main/java/digit/web/models/census/CensusResponse.java new file mode 100644 index 00000000000..dabc56422b6 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/census/CensusResponse.java @@ -0,0 +1,42 @@ +package digit.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.Map; + +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("Census") + @Valid + private List census = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; + + @JsonProperty("StatusCount") + @Valid + private Map statusCount = null; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/CensusSearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/census/CensusSearchCriteria.java new file mode 100644 index 00000000000..4a5a1414d19 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/census/CensusSearchCriteria.java @@ -0,0 +1,72 @@ +package digit.web.models.census; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.springframework.validation.annotation.Validated; +import jakarta.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusSearchCriteria + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusSearchCriteria { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("ids") + private Set ids = null; + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + private String tenantId = null; + + @JsonProperty("areaCodes") + private List areaCodes = null; + + @JsonProperty("status") + private String status = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("source") + private String source = null; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("jurisdiction") + private List jurisdiction = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("limit") + private Integer limit = null; + + @JsonProperty("offset") + private Integer offset = null; + + public CensusSearchCriteria addAreaCodesItem(String areaCodesItem) { + if (this.areaCodes == null) { + this.areaCodes = new ArrayList<>(); + } + this.areaCodes.add(areaCodesItem); + return this; + } + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/CensusSearchRequest.java b/health-services/plan-service/src/main/java/digit/web/models/census/CensusSearchRequest.java new file mode 100644 index 00000000000..157cf16870f --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/census/CensusSearchRequest.java @@ -0,0 +1,31 @@ +package digit.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * CensusSearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusSearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("CensusSearchCriteria") + @Valid + private CensusSearchCriteria censusSearchCriteria = null; + + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/PopulationByDemographic.java b/health-services/plan-service/src/main/java/digit/web/models/census/PopulationByDemographic.java new file mode 100644 index 00000000000..b36d25b21de --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/census/PopulationByDemographic.java @@ -0,0 +1,69 @@ +package digit.web.models.census; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * PopulationByDemographic + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PopulationByDemographic { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("demographicVariable") + private DemographicVariableEnum demographicVariable = null; + + @JsonProperty("populationDistribution") + private Object populationDistribution = null; + + /** + * Gets or Sets demographicVariable + */ + public enum DemographicVariableEnum { + AGE("age"), + + GENDER("gender"), + + ETHNICITY("ethnicity"); + + private String value; + + DemographicVariableEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static DemographicVariableEnum fromValue(String text) { + for (DemographicVariableEnum b : DemographicVariableEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + } + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/AdditionalFields.java b/health-services/plan-service/src/main/java/digit/web/models/facility/AdditionalFields.java new file mode 100644 index 00000000000..e3b7f19cd31 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/AdditionalFields.java @@ -0,0 +1,25 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdditionalFields { + + @JsonProperty("schema") + private String schema; + + @JsonProperty("version") + private int version; + + @JsonProperty("fields") + private List fields; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/Address.java b/health-services/plan-service/src/main/java/digit/web/models/facility/Address.java new file mode 100644 index 00000000000..f6a5e2c8c0b --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/Address.java @@ -0,0 +1,62 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Address { + + @JsonProperty("id") + private String id; + + @JsonProperty("tenantId") + private String tenantId; + + @JsonProperty("clientReferenceId") + private String clientReferenceId; + + @JsonProperty("doorNo") + private String doorNo; + + @JsonProperty("latitude") + private Double latitude; + + @JsonProperty("longitude") + private Double longitude; + + @JsonProperty("locationAccuracy") + private Double locationAccuracy; + + @JsonProperty("type") + private String type; + + @JsonProperty("addressLine1") + private String addressLine1; + + @JsonProperty("addressLine2") + private String addressLine2; + + @JsonProperty("landmark") + private String landmark; + + @JsonProperty("city") + private String city; + + @JsonProperty("pincode") + private String pincode; + + @JsonProperty("buildingName") + private String buildingName; + + @JsonProperty("street") + private String street; + + @JsonProperty("locality") + private Locality locality; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/Facility.java b/health-services/plan-service/src/main/java/digit/web/models/facility/Facility.java new file mode 100644 index 00000000000..fcfec9b1ee9 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/Facility.java @@ -0,0 +1,64 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; + +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Facility { + @JsonProperty("id") + private String id; + + @JsonProperty("tenantId") + private String tenantId; + + @JsonProperty("source") + private String source; + + @JsonProperty("rowVersion") + private Integer rowVersion; + + @JsonProperty("applicationId") + private String applicationId; + + @JsonProperty("hasErrors") + private boolean hasErrors; + + @JsonProperty("additionalFields") + private AdditionalFields additionalFields; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails; + + @JsonProperty("clientReferenceId") + private String clientReferenceId; + + @JsonProperty("clientAuditDetails") + private String clientAuditDetails; + + @JsonProperty("isPermanent") + private boolean isPermanent; + + @JsonProperty("name") + private String name; + + @JsonProperty("usage") + private String usage; + + @JsonProperty("storageCapacity") + private Integer storageCapacity; + + @JsonProperty("address") + private Address address; + + @JsonProperty("isDeleted") + private boolean isDeleted; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/FacilityDetail.java b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilityDetail.java new file mode 100644 index 00000000000..9f09df28102 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilityDetail.java @@ -0,0 +1,19 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FacilityDetail { + @JsonProperty("facility") + private Facility facility; + + @JsonProperty("additionalInfo") + private String additionalInfo; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/FacilityResponse.java b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilityResponse.java new file mode 100644 index 00000000000..46e98759e48 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilityResponse.java @@ -0,0 +1,22 @@ +package digit.web.models.facility; + +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 +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FacilityResponse { + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo; + + @JsonProperty("Facilities") + private List facilities; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/FacilitySearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilitySearchCriteria.java new file mode 100644 index 00000000000..ae76b88fa4a --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilitySearchCriteria.java @@ -0,0 +1,30 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import digit.web.models.Pagination; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FacilitySearchCriteria { + @JsonProperty("tenantId") + private String tenantId; + + @JsonProperty("id") + private List id; + + @JsonProperty("name") + private String name; + + @JsonProperty("isDeleted") + private Boolean isDeleted; + + @JsonProperty("pagination") + private Pagination pagination; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/FacilitySearchRequest.java b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilitySearchRequest.java new file mode 100644 index 00000000000..c7d39e4053e --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/FacilitySearchRequest.java @@ -0,0 +1,20 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FacilitySearchRequest { + @JsonProperty("RequestInfo") + private RequestInfo requestInfo; + + @JsonProperty("Facility") + private FacilitySearchCriteria facilitySearchCriteria; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/Field.java b/health-services/plan-service/src/main/java/digit/web/models/facility/Field.java new file mode 100644 index 00000000000..3cbd343bf76 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/Field.java @@ -0,0 +1,20 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Field { + + @JsonProperty("key") + private String key; + + @JsonProperty("value") + private String value; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/facility/Locality.java b/health-services/plan-service/src/main/java/digit/web/models/facility/Locality.java new file mode 100644 index 00000000000..68552c0898a --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/facility/Locality.java @@ -0,0 +1,33 @@ +package digit.web.models.facility; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Locality { + + @JsonProperty("id") + private String id; + + @JsonProperty("tenantId") + private String tenantId; + + @JsonProperty("code") + private String code; + + @JsonProperty("geometry") + private String geometry; // Assuming geometry is a string, adjust based on your actual data + + @JsonProperty("auditDetails") + private AuditDetails auditDetails; + + @JsonProperty("additionalDetails") + private String additionalDetails; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/Mdms.java b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/Mdms.java new file mode 100644 index 00000000000..be5e324cd5f --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/Mdms.java @@ -0,0 +1,55 @@ +package digit.web.models.mdmsV2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * Mdms + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Mdms { + + @JsonProperty("id") + @Size(min = 2, max = 64) + private String id; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 128) + private String tenantId = null; + + @JsonProperty("schemaCode") + @NotNull + @Size(min = 2, max = 128) + private String schemaCode = null; + + @JsonProperty("uniqueIdentifier") + @Size(min = 2, max = 128) + private String uniqueIdentifier = null; + + @JsonProperty("data") + @NotNull + private JsonNode data = null; + + @JsonProperty("isActive") + private Boolean isActive = true; + + @JsonProperty("auditDetails") + @Valid + private AuditDetails auditDetails = null; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsCriteriaReqV2.java b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsCriteriaReqV2.java new file mode 100644 index 00000000000..f6af90e1b82 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsCriteriaReqV2.java @@ -0,0 +1,23 @@ +package digit.web.models.mdmsV2; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.egov.common.contract.request.RequestInfo; + +import javax.validation.Valid; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class MdmsCriteriaReqV2 { + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo; + + @JsonProperty("MdmsCriteria") + @Valid + private MdmsCriteriaV2 mdmsCriteriaV2; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsCriteriaV2.java b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsCriteriaV2.java new file mode 100644 index 00000000000..88e8a715810 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsCriteriaV2.java @@ -0,0 +1,62 @@ +package digit.web.models.mdmsV2; + +import com.fasterxml.jackson.annotation.JsonIgnore; +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.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Data +@Validated +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class MdmsCriteriaV2 { + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + @NotNull + private String tenantId; + + @JsonProperty("ids") + private Set ids; + + @JsonProperty("uniqueIdentifier") + @Size(min = 1, max = 64) + private String uniqueIdentifier; + + @JsonProperty("uniqueIdentifiers") + private List uniqueIdentifiers; + + @JsonProperty("schemaCode") + private String schemaCode; + + @JsonProperty("filters") + private Map filterMap; + + @JsonProperty("isActive") + private Boolean isActive; + + @JsonIgnore + private Map schemaCodeFilterMap; + + @JsonIgnore + private Set uniqueIdentifiersForRefVerification; + + @JsonProperty("offset") + private Integer offset; + + @JsonProperty("limit") + private Integer limit; + +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsResponseV2.java b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsResponseV2.java new file mode 100644 index 00000000000..090b41f90da --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/mdmsV2/MdmsResponseV2.java @@ -0,0 +1,25 @@ +package digit.web.models.mdmsV2; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.egov.common.contract.response.ResponseInfo; + +import javax.validation.Valid; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) + +public class MdmsResponseV2 { + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("mdms") + @Valid + private List mdms = null; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Boundary.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Boundary.java new file mode 100644 index 00000000000..a1b725cbd6f --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Boundary.java @@ -0,0 +1,32 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Boundary + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Boundary { + + @JsonProperty("code") + private String code; + + @JsonProperty("type") + private String type; + + @JsonProperty("isRoot") + private Boolean isRoot; + + @JsonProperty("includeAllChildren") + private Boolean includeAllChildren; + + @JsonProperty("parent") + private String parent; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignDetail.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignDetail.java new file mode 100644 index 00000000000..ee9d064547a --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignDetail.java @@ -0,0 +1,85 @@ +package digit.web.models.projectFactory; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; + +import java.util.List; + +/** + * CampaignDetails + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CampaignDetail { + + @JsonProperty("id") + private String id; + + @JsonProperty("tenantId") + @NotNull + private String tenantId; + + @JsonProperty("status") + private String status; + + @JsonProperty("action") + private String action; + + @JsonProperty("campaignNumber") + private String campaignNumber; + + @JsonProperty("isActive") + private Boolean isActive; + + @JsonProperty("parentId") + private String parentId; + + @JsonProperty("campaignName") + private String campaignName; + + @JsonProperty("projectType") + private String projectType; + + @JsonProperty("hierarchyType") + private String hierarchyType; + + @JsonProperty("boundaryCode") + private String boundaryCode; + + @JsonProperty("projectId") + private String projectId; + + @JsonProperty("startDate") + private Long startDate; + + @JsonProperty("endDate") + private Long endDate; + + @JsonProperty("additionalDetails") + @Valid + private Object additionalDetails; + + @JsonProperty("resources") + @Valid + private List resources; + + @JsonProperty("boundaries") + @Valid + private List boundaries; + + @JsonProperty("deliveryRules") + @Valid + private List deliveryRules; + + @JsonProperty("auditDetails") + @Valid + private AuditDetails auditDetails; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignResponse.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignResponse.java new file mode 100644 index 00000000000..e03e4bb01c7 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignResponse.java @@ -0,0 +1,29 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.*; +import org.egov.common.contract.response.ResponseInfo; + +import java.util.List; + +/** + * CampaignResponse + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CampaignResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("CampaignDetails") + @Valid + private List campaignDetails = null; + + @JsonProperty("totalCount") + @Valid + private Integer totalCount = null; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignSearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignSearchCriteria.java new file mode 100644 index 00000000000..36ef6675594 --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignSearchCriteria.java @@ -0,0 +1,51 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * CampaignSearchCriteria + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@Validated +public class CampaignSearchCriteria { + + @JsonProperty("ids") + @Size(min = 1) + private List ids; + + @JsonProperty("tenantId") + @Size(min = 2, max = 256) + private String tenantId; + + @JsonIgnore + private List status; + + @JsonIgnore + private String createdBy; + + @JsonIgnore + private Boolean campaignsIncludeDates; + + @JsonIgnore + private Integer startDate; + + @JsonIgnore + private Integer endDate; + + @JsonProperty("pagination") + @Valid + private Pagination pagination; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignSearchReq.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignSearchReq.java new file mode 100644 index 00000000000..391420396ed --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/CampaignSearchReq.java @@ -0,0 +1,22 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.Builder; +import lombok.Data; +import org.egov.common.contract.request.RequestInfo; + +/** + * CampaignSearchReq + */ +@Data +@Builder +public class CampaignSearchReq { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo; + + @JsonProperty("CampaignDetails") + private CampaignSearchCriteria campaignSearchCriteria; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Condition.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Condition.java new file mode 100644 index 00000000000..5ad19e29aef --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Condition.java @@ -0,0 +1,27 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Condition + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Condition { + + @JsonProperty("value") + private String value; + + @JsonProperty("operator") + private String operator; + + @JsonProperty("attribute") + private String attribute; +} + diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/DeliveryRule.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/DeliveryRule.java new file mode 100644 index 00000000000..7eef58fd8ae --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/DeliveryRule.java @@ -0,0 +1,43 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * DeliveryRule + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DeliveryRule { + + @JsonProperty("endDate") + private Long endDate; + + @JsonProperty("products") + @Valid + private List products; + + @JsonProperty("startDate") + private Long startDate; + + @JsonProperty("conditions") + @Valid + private List conditions; + + @JsonProperty("cycleNumber") + private Integer cycleNumber; + + @JsonProperty("deliveryNumber") + private Integer deliveryNumber; + + @JsonProperty("deliveryRuleNumber") + private Integer deliveryRuleNumber; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Pagination.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Pagination.java new file mode 100644 index 00000000000..7e8d28c30ce --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Pagination.java @@ -0,0 +1,35 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Pagination + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Pagination { + + @JsonIgnore + private String sortBy; + + @JsonIgnore + private String sortOrder; + + @JsonProperty("limit") + @Min(1) + @Max(50) + private Integer limit; + + @JsonProperty("offset") + @Min(0) + private Integer offset; +} diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Product.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Product.java new file mode 100644 index 00000000000..9c4e560cc7f --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Product.java @@ -0,0 +1,26 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Product + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Product { + + @JsonProperty("name") + private String name; + + @JsonProperty("count") + private Integer count; + + @JsonProperty("value") + private String value; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Resource.java b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Resource.java new file mode 100644 index 00000000000..07f04a281bb --- /dev/null +++ b/health-services/plan-service/src/main/java/digit/web/models/projectFactory/Resource.java @@ -0,0 +1,32 @@ +package digit.web.models.projectFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Resource + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Resource { + + @JsonProperty("type") + private String type; + + @JsonProperty("filename") + private String filename; + + @JsonProperty("resourceId") + private String resourceId; + + @JsonProperty("filestoreId") + private String filestoreId; + + @JsonProperty("createResourceId") + private String createResourceId; +} \ No newline at end of file diff --git a/health-services/plan-service/src/main/resources/application.properties b/health-services/plan-service/src/main/resources/application.properties index 03ee3da916d..cb941721390 100644 --- a/health-services/plan-service/src/main/resources/application.properties +++ b/health-services/plan-service/src/main/resources/application.properties @@ -30,6 +30,7 @@ spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.Strin spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer spring.kafka.listener.missing-topics-fatal=false spring.kafka.consumer.properties.spring.json.use.type.headers=false +spring.kafka.producer.properties.max.request.size=3000000 # KAFKA CONSUMER CONFIGURATIONS kafka.consumer.config.auto_commit=true @@ -48,14 +49,57 @@ plan.configuration.update.topic=plan-config-update-topic plan.create.topic=save-plan plan.update.topic=update-plan +plan.bulk.update.topic=bulk-update-plan -#mdms urls +plan.facility.update.topic=update-plan-facility +plan.facility.create.topic=save-plan-facility + +plan.employee.assignment.create.topic=plan-employee-assignment-create-topic +plan.employee.assignment.update.topic=plan-employee-assignment-update-topic + +# Census +egov.census.host=https://unified-dev.digit.org +egov.census.search.endpoint=/census-service/_search + +# Facility +egov.facility.host=https://unified-dev.digit.org +egov.facility.search.endpoint=/facility/v1/_search + +# MDMS urls egov.mdms.host=https://unified-dev.digit.org -egov.mdms.search.endpoint=/egov-mdms-service/v1/_search +egov.mdms.search.endpoint=/mdms-v2/v1/_search +egov.mdms.search.v2.endpoint=/mdms-v2/v2/_search + +# Project factory urls +egov.project.factory.host=https://unified-dev.digit.org +egov.project.factory.search.endpoint=/project-factory/v1/project-type/search + +#User Service +egov.user.service.host=https://unified-dev.digit.org +egov.user.search.endpoint=/user/_search + +#Boundary service urls +egov.boundary.service.host=https://unified-dev.digit.org +egov.boundary.relationship.search.endpoint=/boundary-service/boundary-relationships/_search +egov.boundary.hierarchy.search.endpoint=/boundary-service/boundary-hierarchy-definition/_search + +# Workflow +egov.workflow.host=https://unified-dev.digit.org +egov.workflow.transition.path=/egov-workflow-v2/egov-wf/process/_transition +egov.business.service.search.endpoint=/egov-workflow-v2/egov-wf/businessservice/_search +workflow.initiate.action=INITIATE +workflow.intermediate.action=EDIT_AND_SEND_FOR_APPROVAL,APPROVE +workflow.send.back.actions=SEND_BACK_FOR_CORRECTION # Pagination config plan.default.offset=0 plan.default.limit=10 +# CONSUMER TOPICS resource.config.consumer.plan.create.topic=resource-microplan-create-topic -resource.update.plan.config.consumer.topic=resource-plan-config-update-topic \ No newline at end of file +resource.update.plan.config.consumer.topic=resource-plan-config-update-topic +project.factory.save.plan.facility.consumer.topic=project-factory-save-plan-facility + +# Role Map +plan.estimation.approver.roles = ROOT_PLAN_ESTIMATION_APPROVER, PLAN_ESTIMATION_APPROVER +role.map = {'ROOT_FACILITY_CATCHMENT_MAPPER':'FACILITY_CATCHMENT_MAPPER', 'FACILITY_CATCHMENT_MAPPER':'ROOT_FACILITY_CATCHMENT_MAPPER', 'ROOT_POPULATION_DATA_APPROVER':'POPULATION_DATA_APPROVER', 'POPULATION_DATA_APPROVER':'ROOT_POPULATION_DATA_APPROVER', 'ROOT_PLAN_ESTIMATION_APPROVER':'PLAN_ESTIMATION_APPROVER', 'PLAN_ESTIMATION_APPROVER':'ROOT_PLAN_ESTIMATION_APPROVER'} diff --git a/health-services/plan-service/src/main/resources/db/Dockerfile b/health-services/plan-service/src/main/resources/db/Dockerfile index 60fc07ce69f..f38638a269f 100644 --- a/health-services/plan-service/src/main/resources/db/Dockerfile +++ b/health-services/plan-service/src/main/resources/db/Dockerfile @@ -1,4 +1,4 @@ -FROM egovio/flyway:4.1.2 +FROM egovio/flyway:10.7.1 COPY ./migration/main /flyway/sql @@ -6,4 +6,4 @@ COPY migrate.sh /usr/bin/migrate.sh RUN chmod +x /usr/bin/migrate.sh -CMD ["/usr/bin/migrate.sh"] \ No newline at end of file +ENTRYPOINT ["/usr/bin/migrate.sh"] diff --git a/health-services/plan-service/src/main/resources/db/migrate.sh b/health-services/plan-service/src/main/resources/db/migrate.sh index 43960b25cdb..f9d6617822c 100644 --- a/health-services/plan-service/src/main/resources/db/migrate.sh +++ b/health-services/plan-service/src/main/resources/db/migrate.sh @@ -1,3 +1,3 @@ #!/bin/sh -flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true -ignoreMissingMigrations=true migrate \ No newline at end of file +flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true migrate diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20240305113045__plan_configuration_create_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20240305113045__plan_configuration_create_ddl.sql index e9630421bcb..fa1cc87dc6a 100644 --- a/health-services/plan-service/src/main/resources/db/migration/main/V20240305113045__plan_configuration_create_ddl.sql +++ b/health-services/plan-service/src/main/resources/db/migration/main/V20240305113045__plan_configuration_create_ddl.sql @@ -1,9 +1,11 @@ -- Table: plan_configuration CREATE TABLE plan_configuration ( id character varying(64), - tenant_id character varying(64), - name character varying(128), - execution_plan_id character varying(64), + tenant_id character varying(64) not null, + name character varying(128) not null, + campaign_id character varying(64) not null, + status character varying(64) not null, + additional_details JSONB, created_by character varying(64), created_time bigint, last_modified_by character varying(64), @@ -16,8 +18,10 @@ CREATE TABLE plan_configuration ( CREATE TABLE plan_configuration_files ( id character varying(64), plan_configuration_id character varying(64), - filestore_id character varying(128), - input_file_type character varying(64), + filestore_id character varying(128) not null, + input_file_type character varying(64) not null, + template_identifier character varying(128) not null, + active boolean not null, created_by character varying(64), created_time bigint, last_modified_by character varying(64), @@ -30,9 +34,12 @@ CREATE TABLE plan_configuration_files ( -- Table: plan_configuration_assumptions CREATE TABLE plan_configuration_assumptions ( id character varying(64), - key character varying(256), - value numeric(12,2), + key character varying(256) not null, + value numeric(12,2) not null, + source character varying(64), + category character varying(64), plan_configuration_id character varying(64), + active boolean not null, created_by character varying(64), created_time bigint, last_modified_by character varying(64), @@ -45,10 +52,15 @@ CREATE TABLE plan_configuration_assumptions ( -- Table: plan_configuration_operations CREATE TABLE plan_configuration_operations ( id character varying(64), - input character varying(256), - operator character varying(64), - assumption_value character varying(256), - output character varying(64), + input character varying(256) not null, + operator character varying(64) not null, + assumption_value character varying(256) not null, + output character varying(256) not null, + show_on_estimation_dashboard boolean, + source character varying(64), + category character varying(64), + active boolean not null, + execution_order numeric(12,2), plan_configuration_id character varying(64), created_by character varying(64), created_time bigint, @@ -62,8 +74,10 @@ CREATE TABLE plan_configuration_operations ( -- Table: plan_configuration_mapping CREATE TABLE plan_configuration_mapping ( id character varying(64), - mapped_from character varying(256), - mapped_to character varying(256), + mapped_from character varying(256) not null, + mapped_to character varying(256) not null, + filestore_id character varying(128) not null, + active boolean not null, plan_configuration_id character varying(64), created_by character varying(64), created_time bigint, diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20240305113047__plan_create_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20240305113047__plan_create_ddl.sql index 942cc36af38..b8a194f4afa 100644 --- a/health-services/plan-service/src/main/resources/db/migration/main/V20240305113047__plan_create_ddl.sql +++ b/health-services/plan-service/src/main/resources/db/migration/main/V20240305113047__plan_create_ddl.sql @@ -2,7 +2,10 @@ CREATE TABLE plan ( id varchar(64), tenant_id varchar(64), locality varchar(64), - execution_plan_id varchar(64), + campaign_id varchar(64), + status character varying(64) not null, + assignee varchar(64), + boundary_ancestral_path TEXT, plan_configuration_id varchar(64), additional_details JSONB, created_by varchar(64), diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20240404113045__plan_configuration_add_filestoreid_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20240404113045__plan_configuration_add_filestoreid_ddl.sql deleted file mode 100644 index fcd7cfe2bbc..00000000000 --- a/health-services/plan-service/src/main/resources/db/migration/main/V20240404113045__plan_configuration_add_filestoreid_ddl.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE plan_configuration_mapping ADD filestore_id character varying(128); diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20240404150000__plan_configuration_add_template_identifier_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20240404150000__plan_configuration_add_template_identifier_ddl.sql deleted file mode 100644 index e1c67ff2696..00000000000 --- a/health-services/plan-service/src/main/resources/db/migration/main/V20240404150000__plan_configuration_add_template_identifier_ddl.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE plan_configuration_files ADD template_identifier character varying(128); diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20240923113045__plan_facility_create_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20240923113045__plan_facility_create_ddl.sql new file mode 100644 index 00000000000..0f037765d8f --- /dev/null +++ b/health-services/plan-service/src/main/resources/db/migration/main/V20240923113045__plan_facility_create_ddl.sql @@ -0,0 +1,19 @@ +-- Table: plan_facility_linkage +CREATE TABLE plan_facility_linkage ( + id varchar(64), + tenant_id varchar(64), + plan_configuration_id varchar(64), + facility_id varchar(64), + residing_boundary varchar(64), + service_boundaries TEXT, + additional_details JSONB, + plan_configuration_name character varying(128), + facility_name character varying(64), + active boolean, + created_by varchar(64), + created_time bigint, + last_modified_by varchar(64), + last_modified_time bigint, + CONSTRAINT uk_plan_facility_linkage_id PRIMARY KEY (id), + FOREIGN KEY (plan_configuration_id) REFERENCES plan_configuration(id) +); \ No newline at end of file diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20241604150000__plan_configuration_add_status_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20241604150000__plan_configuration_add_status_ddl.sql deleted file mode 100644 index 8683e34faef..00000000000 --- a/health-services/plan-service/src/main/resources/db/migration/main/V20241604150000__plan_configuration_add_status_ddl.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE plan_configuration ADD status character varying(64); -UPDATE plan_configuration SET status = 'DRAFT' WHERE status IS NULL; diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20242105150000__plan_configuration_add_active_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20242105150000__plan_configuration_add_active_ddl.sql deleted file mode 100644 index 3586cd4cb45..00000000000 --- a/health-services/plan-service/src/main/resources/db/migration/main/V20242105150000__plan_configuration_add_active_ddl.sql +++ /dev/null @@ -1,11 +0,0 @@ -ALTER TABLE plan_configuration_files ADD active boolean; -UPDATE plan_configuration_files SET active = true WHERE active IS NULL; - -ALTER TABLE plan_configuration_assumptions ADD active boolean; -UPDATE plan_configuration_assumptions SET active = true WHERE active IS NULL; - -ALTER TABLE plan_configuration_operations ADD active boolean; -UPDATE plan_configuration_operations SET active = true WHERE active IS NULL; - -ALTER TABLE plan_configuration_mapping ADD active boolean; -UPDATE plan_configuration_mapping SET active = true WHERE active IS NULL; \ No newline at end of file diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20242109141800__plan_employee_assignment_create_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20242109141800__plan_employee_assignment_create_ddl.sql new file mode 100644 index 00000000000..1e28950ca6f --- /dev/null +++ b/health-services/plan-service/src/main/resources/db/migration/main/V20242109141800__plan_employee_assignment_create_ddl.sql @@ -0,0 +1,19 @@ +-- Table: plan_employee_assignment +CREATE TABLE plan_employee_assignment ( + id character varying(64), + tenant_id character varying(64), + plan_configuration_id character varying(64), + employee_id character varying(64), + role character varying(64), + hierarchy_level character varying(64), + jurisdiction TEXT, + plan_configuration_name character varying(128), + additional_details JSONB, + active boolean DEFAULT true, + created_by character varying(64), + created_time bigint, + last_modified_by character varying(64), + last_modified_time bigint, + CONSTRAINT uk_plan_employee_assignment_id PRIMARY KEY (id), + FOREIGN KEY (plan_configuration_id) REFERENCES plan_configuration(id) +); diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20242110115700__alter_plan_assignee_create_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20242110115700__alter_plan_assignee_create_ddl.sql new file mode 100644 index 00000000000..584399af592 --- /dev/null +++ b/health-services/plan-service/src/main/resources/db/migration/main/V20242110115700__alter_plan_assignee_create_ddl.sql @@ -0,0 +1 @@ +ALTER TABLE plan ALTER COLUMN assignee TYPE TEXT; diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20242112141500__alter plan_facility_add_boundary_ancestral_path_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20242112141500__alter plan_facility_add_boundary_ancestral_path_ddl.sql new file mode 100644 index 00000000000..0bf373ddcd7 --- /dev/null +++ b/health-services/plan-service/src/main/resources/db/migration/main/V20242112141500__alter plan_facility_add_boundary_ancestral_path_ddl.sql @@ -0,0 +1 @@ +ALTER TABLE plan_facility_linkage ADD boundary_ancestral_path TEXT; \ No newline at end of file diff --git a/health-services/resource-estimation-service/CHANGELOG.md b/health-services/resource-estimation-service/CHANGELOG.md deleted file mode 100644 index e5ce4491284..00000000000 --- a/health-services/resource-estimation-service/CHANGELOG.md +++ /dev/null @@ -1,8 +0,0 @@ -# Changelog -## 1.0.0 - 2024-06-24 -#### Base Resource Estimation Service - 1. Resource Estimation Service manages file processing: validating data, calculating for files, updating plans, and integrating with campaigns. - 2. File Processing: In file processing, it processes files present in plan configuration by calculating resources. - 3. Updating Plan: It creates plans based on rows and updates those by putting them on topics that are consumed by the plan service. - 4. Integrate with Campaign Manager: After processing calculations, it also integrates resources and boundary with the Campaign Manager. - 5. Boundary and Data Validation: Validates boundaries and excel data during calculations. \ No newline at end of file diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/config/ServiceConstants.java b/health-services/resource-estimation-service/src/main/java/org/egov/processor/config/ServiceConstants.java deleted file mode 100644 index 797c9a894c3..00000000000 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/config/ServiceConstants.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.egov.processor.config; - - -import org.springframework.stereotype.Component; - - -@Component -public class ServiceConstants { - - public static final String EXTERNAL_SERVICE_EXCEPTION = "External Service threw an Exception: "; - public static final String SEARCHER_SERVICE_EXCEPTION = "Exception while fetching from searcher: "; - - public static final String ERROR_WHILE_FETCHING_FROM_MDMS = "Exception occurred while fetching category lists from mdms: "; - - public static final String TENANTID_REPLACER = "{tenantId}"; - - public static final String TENANTID = "tenantId"; - - public static final String FILESTORE_ID_REPLACER = "{fileStoreId}"; - - public static final String FILES = "files"; - - public static final String FILESTORE_ID = "fileStoreId"; - - public static final String MODULE = "module"; - - public static final String MICROPLANNING_MODULE = "microplan"; - - public static final String PROPERTIES = "properties"; - - public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE = "NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT"; - public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE = "Invalid or incorrect TenantId. No mdms data found for provided Tenant."; - - public static final String ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE = "Exception occurred while fetching plan configuration from plan service "; - - public static final String NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM_CODE = "NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM"; - public static final String NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM_MESSAGE = "Not able to fetch byte stream from a multipart file"; - - public static final String BOUNDARY_CODE = "boundaryCode"; - public static final String ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE_FOR_LOCALITY = "Exception occurred while fetching plan configuration from plan service for Locality "; - - public static final String ERROR_WHILE_SEARCHING_CAMPAIGN = "Exception occurred while searching/updating campaign."; - public static final String FILE_NAME = "output.xls"; - public static final String FILE_TYPE = "boundaryWithTarget"; - public static final String FILE_TEMPLATE_IDENTIFIER = "Population"; - public static final String INPUT_IS_NOT_VALID = "File does not contain valid input for row "; - - public static final String MDMS_SCHEMA_TYPE = "type"; - public static final String MDMS_SCHEMA_SECTION = "section"; - public static final String MDMS_PLAN_MODULE_NAME = "hcm-microplanning"; - public static final String MDMS_MASTER_SCHEMAS = "Schemas"; - public static final String MDMS_CAMPAIGN_TYPE = "campaignType"; - - public static final String ERROR_WHILE_UPDATING_PLAN_CONFIG = "Exception occurred while updating plan configuration."; - - public static final String VALIDATE_STRING_REGX = "^(?!\\d+$).+$"; - public static final String VALIDATE_NUMBER_REGX = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$"; - public static final String VALIDATE_BOOLEAN_REGX = "^(?i)(true|false)$"; - - public static final String FILE_TEMPLATE = "Facilities"; - public static final String HIERARCHYTYPE_REPLACER = "{hierarchyType}"; - public static final String FILE_EXTENSION = "excel"; - - public static final String SCIENTIFIC_NOTATION_INDICATOR = "E"; - public static final String ATTRIBUTE_IS_REQUIRED ="isRequired"; - public static final int DEFAULT_SCALE=2; - - public static final String MDMS_LOCALE_SEARCH_MODULE ="rainmaker-microplanning,rainmaker-boundary-undefined,rainmaker-hcm-admin-schemas"; - public static final String ERROR_WHILE_SEARCHING_LOCALE = "Exception occurred while searching locale. "; - public static final String MDMS_MASTER_COMMON_CONSTANTS = "CommonConstants"; - -} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/MetricDetail.java b/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/MetricDetail.java deleted file mode 100644 index 32a5c0b4ccc..00000000000 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/MetricDetail.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.egov.processor.web.models; - -import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import java.math.BigDecimal; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; - -@Validated -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class MetricDetail { - - @JsonProperty("value") - @NotNull - private BigDecimal metricValue = null; - - @JsonProperty("comparator") - @NotNull - @Size(min = 1, max = 64) - private String metricComparator = null; - - @JsonProperty("unit") - @NotNull - @Size(min = 1, max = 128) - private String metricUnit = null; - -} diff --git a/health-services/resource-generator/CHANGELOG.md b/health-services/resource-generator/CHANGELOG.md new file mode 100644 index 00000000000..f56811de913 --- /dev/null +++ b/health-services/resource-generator/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +## 1.0.0 - 2024-12-03 +#### Resource Generator Service +The Resource Generator Service introduces comprehensive functionalities for microplanning resource estimation and campaign integration: + +1. File Processing: Supports Excel, Shapefiles, and GeoJSON for resource estimation and validates input data against the plan configuration. +2. Resource Estimation: Calculates resources using predefined formulas and updates plans by publishing data to relevant topics. +3. Boundary Validation: Ensures boundary integrity and validates data during resource calculations. +4. Process Triggers: Automates Plan-Facility creation, Census data creation, and Plan generation based on file uploads and validations. +5. Campaign Integration: Integrates estimated resources and boundaries with the Campaign Manager for streamlined planning. +6. Result Upload: Automates the upload of approved resource sheets to the filestore and updates plan configurations. +7. HCM Integration: Updates project factory with resource estimates for accurate campaign planning. \ No newline at end of file diff --git a/health-services/resource-estimation-service/LOCALSETUP.md b/health-services/resource-generator/LOCALSETUP.md similarity index 100% rename from health-services/resource-estimation-service/LOCALSETUP.md rename to health-services/resource-generator/LOCALSETUP.md diff --git a/health-services/resource-estimation-service/README.md b/health-services/resource-generator/README.md similarity index 100% rename from health-services/resource-estimation-service/README.md rename to health-services/resource-generator/README.md diff --git a/health-services/resource-estimation-service/pom.xml b/health-services/resource-generator/pom.xml similarity index 95% rename from health-services/resource-estimation-service/pom.xml rename to health-services/resource-generator/pom.xml index c73dd06629d..33ac3c4c7d6 100644 --- a/health-services/resource-estimation-service/pom.xml +++ b/health-services/resource-generator/pom.xml @@ -1,7 +1,7 @@ 4.0.0 org.egov - resource-estimation-service + resource-generator jar file-processor-utility 1.0.0 @@ -98,6 +98,12 @@ tracer 2.9.0-SNAPSHOT + + org.egov.services + services-common + 2.9.0-SNAPSHOT + compile + org.egov mdms-client diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/Main.java b/health-services/resource-generator/src/main/java/org/egov/processor/Main.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/Main.java rename to health-services/resource-generator/src/main/java/org/egov/processor/Main.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/config/Configuration.java b/health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java similarity index 54% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/config/Configuration.java rename to health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java index 12ce0667abd..c652337ba28 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/config/Configuration.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java @@ -11,6 +11,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; + @Component @Data @Import({ TracerConfiguration.class }) @@ -27,12 +29,18 @@ public class Configuration { @Value("${egov.mdms.search.endpoint}") private String mdmsEndPoint; + @Value("${egov.mdms.search.v2.endpoint}") + private String mdmsV2EndPoint; + @Value("${egov.plan.config.host}") private String planConfigHost; @Value("${egov.plan.config.endpoint}") private String planConfigEndPoint; + @Value("${egov.plan.search.endpoint}") + private String planSearchEndPoint; + // Filestore @Value("${egov.filestore.service.host}") @@ -53,18 +61,18 @@ public class Configuration { @Value("${egov.project.factory.update.endpoint}") private String campaignIntegrationUpdateEndPoint; + @Value("${egov.project.factory.data.create.endpoint}") + private String campaignIntegrationDataCreateEndPoint; + + @Value("${egov.project.factory.fetch.from.microplan.endpoint}") + private String campaignIntegrationFetchFromMicroplanEndPoint; + @Value("${egov.project.factory.host}") private String projectFactoryHostEndPoint; - @Value("${resource.microplan.create.topic}") - private String resourceMicroplanCreateTopic; - @Value("${integrate.with.admin.console}") private boolean isIntegrateWithAdminConsole; - @Value("${resource.update.plan.config.consumer.topic}") - private String resourceUpdatePlanConfigConsumerTopic; - @Value("${egov.boundary.service.host}") private String egovBoundaryServiceHost; @@ -77,4 +85,51 @@ public class Configuration { @Value("${egov.locale.search.endpoint}") private String egovLocaleSearchEndpoint; + //trigger statuses + @Value("${plan.config.trigger.plan.estimates.status}") + private String planConfigTriggerPlanEstimatesStatus; + + @Value("${plan.config.trigger.census.records.status}") + private String planConfigTriggerCensusRecordsStatus; + + @Value("${plan.config.update.plan.estimates.into.output.file.status}") + private String planConfigUpdatePlanEstimatesIntoOutputFileStatus; + + @Value("${plan.config.trigger.plan.facility.mappings.status}") + private String planConfigTriggerPlanFacilityMappingsStatus; + + //Kafka topics for creating or updating records in dependent microservices + @Value("${resource.microplan.create.topic}") + private String resourceMicroplanCreateTopic; + + @Value("${resource.update.plan.config.consumer.topic}") + private String resourceUpdatePlanConfigConsumerTopic; + + @Value("${resource.census.create.topic}") + private String resourceCensusCreateTopic; + + //Default + @Value("${resource.default.offset}") + private Integer defaultOffset; + + @Value("${resource.default.limit}") + private Integer defaultLimit; + + //census additonal field configs + @Value("${census.additional.field.override.keys}") + public List censusAdditionalFieldOverrideKeys; + + @Value("${census.additional.field.prefix.append.keys}") + public List censusAdditionalPrefixAppendKeys; + + @Value("${census.additional.field.show.on.ui.false.keys}") + public List censusAdditionalFieldShowOnUIFalseKeys; + + //census host + @Value("${egov.census.host}") + private String censusHost; + + @Value("${egov.census.search.endpoint}") + private String censusSearchEndPoint; + } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/config/MainConfiguration.java b/health-services/resource-generator/src/main/java/org/egov/processor/config/MainConfiguration.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/config/MainConfiguration.java rename to health-services/resource-generator/src/main/java/org/egov/processor/config/MainConfiguration.java diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java b/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java new file mode 100644 index 00000000000..7200f29cecc --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java @@ -0,0 +1,123 @@ +package org.egov.processor.config; + + +import org.springframework.stereotype.Component; + + +@Component +public class ServiceConstants { + + public static final String EXTERNAL_SERVICE_EXCEPTION = "External Service threw an Exception: "; + public static final String SEARCHER_SERVICE_EXCEPTION = "Exception while fetching from searcher: "; + + public static final String ERROR_WHILE_FETCHING_FROM_MDMS = "Exception occurred while fetching category lists from mdms: "; + + public static final String ERROR_WHILE_FETCHING_FROM_CENSUS = "Exception occurred while fetching records from census: "; + + public static final String TENANTID_REPLACER = "{tenantId}"; + + public static final String TENANTID = "tenantId"; + + public static final String FILESTORE_ID_REPLACER = "{fileStoreId}"; + + public static final String FILES = "files"; + + public static final String FILESTORE_ID = "fileStoreId"; + + public static final String MODULE = "module"; + + public static final String MICROPLANNING_MODULE = "microplan"; + + public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE = "NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT"; + public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE = "Invalid or incorrect TenantId. No mdms data found for provided Tenant."; + + public static final String ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE = "Exception occurred while fetching plan configuration from plan service "; + + public static final String NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM_CODE = "NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM"; + public static final String NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM_MESSAGE = "Not able to fetch byte stream from a multipart file"; + + public static final String FILE_NOT_FOUND_CODE = "FILE_NOT_FOUND"; + public static final String FILE_NOT_FOUND_MESSAGE = "No file with the specified templateIdentifier found - "; + + public static final String UNABLE_TO_CREATE_ADDITIONAL_DETAILS_CODE = "UNABLE_TO_CREATE_ADDITIONAL_DETAILS"; + public static final String UNABLE_TO_CREATE_ADDITIONAL_DETAILS_MESSAGE = "Unable to create additional details for facility creation."; + + public static final String NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_CODE = "NO_PLAN_FOUND_FOR_GIVEN_DETAILS"; + public static final String NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_MESSAGE = "Census records do not exists for the given details: "; + + public static final String NO_PLAN_FOUND_FOR_GIVEN_DETAILS_CODE = "NO_PLAN_FOUND_FOR_GIVEN_DETAILS"; + public static final String NO_PLAN_FOUND_FOR_GIVEN_DETAILS_MESSAGE = "Plan records do not exists for the given details: "; + + public static final String BOUNDARY_CODE = "HCM_ADMIN_CONSOLE_BOUNDARY_CODE"; + public static final String TOTAL_POPULATION = "HCM_ADMIN_CONSOLE_TOTAL_POPULATION"; + + public static final String ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE_FOR_LOCALITY = "Exception occurred while fetching plan configuration from plan service for Locality "; + public static final String ERROR_WHILE_PUSHING_TO_PLAN_SERVICE_FOR_LOCALITY = "Exception occurred while fetching plan configuration from plan service for Locality "; + public static final String ERROR_WHILE_SEARCHING_CAMPAIGN = "Exception occurred while searching/updating campaign."; + public static final String ERROR_WHILE_DATA_CREATE_CALL = "Exception occurred while creating data for campaign - "; + public static final String ERROR_WHILE_CALLING_MICROPLAN_API = + "Unexpected error while calling fetch from Microplan API for plan config Id: "; + + public static final String FILE_NAME = "output.xls"; + public static final String FILE_TYPE = "boundaryWithTarget"; + public static final String FILE_TEMPLATE_IDENTIFIER_POPULATION = "Population"; + public static final String FILE_TEMPLATE_IDENTIFIER_FACILITY = "Facilities"; + public static final String INPUT_IS_NOT_VALID = "File does not contain valid input for row "; + + public static final String MDMS_SCHEMA_TYPE = "type"; + public static final String MDMS_SCHEMA_SECTION = "section"; + public static final String MDMS_PLAN_MODULE_NAME = "hcm-microplanning"; + public static final String MDMS_MASTER_SCHEMAS = "Schemas"; + public static final String MDMS_CAMPAIGN_TYPE = "campaignType"; + public static final String MDMS_SCHEMA_ADMIN_SCHEMA = "adminSchema"; + public static final String MDMS_ADMIN_CONSOLE_MODULE_NAME = "HCM-ADMIN-CONSOLE"; + public static final String BOUNDARY = "boundary"; + public static final String DOT_SEPARATOR = "."; + public static final String MICROPLAN_PREFIX = "MP-"; + + //MDMS field Constants + public static final String DATA = "data"; + public static final String PROPERTIES = "properties"; + public static final String NUMBER_PROPERTIES = "numberProperties"; + public static final String STRING_PROPERTIES = "stringProperties"; + public static final String NAME = "name"; + + public static final String ERROR_WHILE_UPDATING_PLAN_CONFIG = "Exception occurred while updating plan configuration."; + public static final String ERROR_WHILE_SEARCHING_PLAN = "Exception occurred while search plans."; + + public static final String VALIDATE_STRING_REGX = "^(?!\\d+$).+$"; + public static final String VALIDATE_NUMBER_REGX = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$"; + public static final String VALIDATE_BOOLEAN_REGX = "^(?i)(true|false)$"; + + public static final String FILE_TEMPLATE = "Facilities"; + public static final String HIERARCHYTYPE_REPLACER = "{hierarchyType}"; + public static final String FILE_EXTENSION = "excel"; + + public static final String SCIENTIFIC_NOTATION_INDICATOR = "E"; + public static final String ATTRIBUTE_IS_REQUIRED ="isRequired"; + public static final int DEFAULT_SCALE=2; + + public static final String MDMS_LOCALE_SEARCH_MODULE ="rainmaker-microplanning,rainmaker-boundary-undefined,hcm-admin-schemas"; + public static final String ERROR_WHILE_SEARCHING_LOCALE = "Exception occurred while searching locale. "; + public static final String MDMS_MASTER_COMMON_CONSTANTS = "CommonConstants"; + + //override sheet names + public static final String HCM_ADMIN_CONSOLE_BOUNDARY_DATA = "HCM_ADMIN_CONSOLE_BOUNDARY_DATA"; + public static final String READ_ME_SHEET_NAME = "readMeSheetName"; + + //Workflow constants + public static final String WORKFLOW_ACTION_INITIATE = "INITIATE"; + public static final String WORKFLOW_COMMENTS_INITIATING_CENSUS = "Initiating census record creation"; + public static final String WORKFLOW_COMMENTS_INITIATING_ESTIMATES = "Initiating plan estimation record creation"; + + //Facility Create constants + public static final String TYPE_FACILITY = "facility"; + public static final String ACTION_CREATE = "create"; + public static final String SOURCE_KEY = "source"; + public static final String MICROPLAN_SOURCE_KEY = "microplan"; + public static final String MICROPLAN_ID_KEY = "microplanId"; + + //Census additional field constants + public static final String UPLOADED_KEY = "UPLOADED_"; + public static final String CONFIRMED_KEY = "CONFIRMED_"; +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/kafka/PlanConsumer.java b/health-services/resource-generator/src/main/java/org/egov/processor/kafka/PlanConsumer.java similarity index 66% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/kafka/PlanConsumer.java rename to health-services/resource-generator/src/main/java/org/egov/processor/kafka/PlanConsumer.java index 2aa31907413..8b0c54a4010 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/kafka/PlanConsumer.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/kafka/PlanConsumer.java @@ -1,11 +1,9 @@ package org.egov.processor.kafka; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Collections; -import java.util.Map; import lombok.extern.slf4j.Slf4j; +import org.egov.processor.config.Configuration; import org.egov.processor.service.ResourceEstimationService; -import org.egov.processor.web.models.PlanConfiguration; import org.egov.processor.web.models.PlanConfigurationRequest; import org.egov.tracer.model.CustomException; import org.springframework.http.HttpStatus; @@ -13,6 +11,9 @@ import org.springframework.kafka.support.KafkaHeaders; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.util.Map; @Component @Slf4j @@ -22,16 +23,22 @@ public class PlanConsumer { private ResourceEstimationService resourceEstimationService; - public PlanConsumer(ObjectMapper objectMapper, ResourceEstimationService resourceEstimationService) { + private Configuration config; + + public PlanConsumer(ObjectMapper objectMapper, ResourceEstimationService resourceEstimationService, Configuration config) { this.objectMapper = objectMapper; this.resourceEstimationService = resourceEstimationService; + this.config = config; } @KafkaListener(topics = { "${plan.config.consumer.kafka.save.topic}", "${plan.config.consumer.kafka.update.topic}" }) public void listen(Map consumerRecord, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { try { PlanConfigurationRequest planConfigurationRequest = objectMapper.convertValue(consumerRecord, PlanConfigurationRequest.class); - if (planConfigurationRequest.getPlanConfiguration().getStatus().equals(PlanConfiguration.StatusEnum.GENERATED)) { + if (!ObjectUtils.isEmpty(planConfigurationRequest.getPlanConfiguration().getWorkflow()) && (planConfigurationRequest.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerPlanEstimatesStatus()) + || planConfigurationRequest.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerCensusRecordsStatus()) + || planConfigurationRequest.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerPlanFacilityMappingsStatus()) + || planConfigurationRequest.getPlanConfiguration().getStatus().equals(config.getPlanConfigUpdatePlanEstimatesIntoOutputFileStatus()))) { resourceEstimationService.estimateResources(planConfigurationRequest); log.info("Successfully estimated resources for plan."); } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/kafka/Producer.java b/health-services/resource-generator/src/main/java/org/egov/processor/kafka/Producer.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/kafka/Producer.java rename to health-services/resource-generator/src/main/java/org/egov/processor/kafka/Producer.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/repository/ServiceRequestRepository.java b/health-services/resource-generator/src/main/java/org/egov/processor/repository/ServiceRequestRepository.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/repository/ServiceRequestRepository.java rename to health-services/resource-generator/src/main/java/org/egov/processor/repository/ServiceRequestRepository.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ExcelParser.java b/health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java similarity index 73% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ExcelParser.java rename to health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java index ee005cd81c8..2cd695cec55 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ExcelParser.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java @@ -1,36 +1,19 @@ package org.egov.processor.service; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.egov.processor.config.Configuration; import org.egov.processor.config.ServiceConstants; -import org.egov.processor.util.BoundaryUtil; -import org.egov.processor.util.CalculationUtil; -import org.egov.processor.util.CampaignIntegrationUtil; -import org.egov.processor.util.FilestoreUtil; -import org.egov.processor.util.LocaleUtil; -import org.egov.processor.util.MdmsUtil; -import org.egov.processor.util.ParsingUtil; -import org.egov.processor.util.PlanUtil; +import org.egov.processor.util.*; +import org.egov.processor.web.models.*; import org.egov.processor.web.models.Locale; -import org.egov.processor.web.models.LocaleResponse; -import org.egov.processor.web.models.Operation; -import org.egov.processor.web.models.PlanConfiguration; -import org.egov.processor.web.models.PlanConfiguration.StatusEnum; -import org.egov.processor.web.models.PlanConfigurationRequest; -import org.egov.processor.web.models.ResourceMapping; import org.egov.processor.web.models.boundary.BoundarySearchResponse; import org.egov.processor.web.models.boundary.EnrichedBoundary; import org.egov.processor.web.models.campaignManager.Boundary; @@ -40,13 +23,16 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; +import static org.egov.processor.config.ServiceConstants.HCM_ADMIN_CONSOLE_BOUNDARY_DATA; +import static org.egov.processor.config.ServiceConstants.READ_ME_SHEET_NAME; @Slf4j @Service @@ -72,9 +58,15 @@ public class ExcelParser implements FileParser { private LocaleUtil localeUtil; + private CensusUtil censusUtil; + + private EnrichmentUtil enrichmentUtil; + + private PlanConfigurationUtil planConfigurationUtil; + public ExcelParser(ObjectMapper objectMapper, ParsingUtil parsingUtil, FilestoreUtil filestoreUtil, - CalculationUtil calculationUtil, PlanUtil planUtil, CampaignIntegrationUtil campaignIntegrationUtil, - Configuration config, MdmsUtil mdmsUtil, BoundaryUtil boundaryUtil,LocaleUtil localeUtil) { + CalculationUtil calculationUtil, PlanUtil planUtil, CampaignIntegrationUtil campaignIntegrationUtil, + Configuration config, MdmsUtil mdmsUtil, BoundaryUtil boundaryUtil, LocaleUtil localeUtil, CensusUtil censusUtil, EnrichmentUtil enrichmentUtil, PlanConfigurationUtil planConfigurationUtil) { this.objectMapper = objectMapper; this.parsingUtil = parsingUtil; this.filestoreUtil = filestoreUtil; @@ -85,7 +77,10 @@ public ExcelParser(ObjectMapper objectMapper, ParsingUtil parsingUtil, Filestore this.mdmsUtil = mdmsUtil; this.boundaryUtil = boundaryUtil; this.localeUtil = localeUtil; - } + this.censusUtil = censusUtil; + this.enrichmentUtil = enrichmentUtil; + this.planConfigurationUtil = planConfigurationUtil; + } /** * Parses file data, extracts information from the file, and processes it. @@ -99,7 +94,7 @@ public ExcelParser(ObjectMapper objectMapper, ParsingUtil parsingUtil, Filestore */ @Override public Object parseFileData(PlanConfigurationRequest planConfigurationRequest, String fileStoreId, - Object campaignResponse) { + Object campaignResponse) { PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); byte[] byteArray = filestoreUtil.getFile(planConfig.getTenantId(), fileStoreId); File file = parsingUtil.convertByteArrayToFile(byteArray, ServiceConstants.FILE_EXTENSION); @@ -108,7 +103,8 @@ public Object parseFileData(PlanConfigurationRequest planConfigurationRequest, S throw new CustomException("FileNotFound", "The file with ID " + fileStoreId + " was not found in the tenant " + planConfig.getTenantId()); } - return processExcelFile(planConfigurationRequest, file, fileStoreId, campaignResponse); + processExcelFile(planConfigurationRequest, file, fileStoreId, campaignResponse); + return null; } /** @@ -122,20 +118,17 @@ public Object parseFileData(PlanConfigurationRequest planConfigurationRequest, S * @param fileStoreId The ID of the file in the file store. * @param campaignResponse The response object to be updated with * processed data. - * @return The ID of the uploaded file. */ - private String processExcelFile(PlanConfigurationRequest planConfigurationRequest, File file, String fileStoreId, + private void processExcelFile(PlanConfigurationRequest planConfigurationRequest, File file, String fileStoreId, Object campaignResponse) { - PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); try (Workbook workbook = new XSSFWorkbook(file)) { List campaignBoundaryList = new ArrayList<>(); List campaignResourcesList = new ArrayList<>(); DataFormatter dataFormatter = new DataFormatter(); - processSheets(planConfigurationRequest, fileStoreId, campaignResponse, planConfig, workbook, - campaignBoundaryList, campaignResourcesList, dataFormatter); - String uploadedFileStoreId = uploadFileAndIntegrateCampaign(planConfigurationRequest, campaignResponse, - planConfig, workbook, campaignBoundaryList, campaignResourcesList); - return uploadedFileStoreId; + processSheets(planConfigurationRequest, fileStoreId, campaignResponse, workbook, + campaignBoundaryList, dataFormatter); + uploadFileAndIntegrateCampaign(planConfigurationRequest, campaignResponse, + workbook, campaignBoundaryList, campaignResourcesList); } catch (FileNotFoundException e) { log.error("File not found: {}", e.getMessage()); throw new CustomException("FileNotFound", "The specified file was not found."); @@ -154,28 +147,26 @@ private String processExcelFile(PlanConfigurationRequest planConfigurationReques * * @param planConfigurationRequest The request containing configuration details including tenant ID. * @param campaignResponse The response object containing campaign details. - * @param planConfig The configuration details specific to the plan. * @param workbook The workbook containing data to be uploaded and integrated. * @param campaignBoundaryList List of boundary objects related to the campaign. * @param campaignResourcesList List of campaign resources to be integrated. - * @return The ID of the uploaded file in the file store. */ - private String uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfigurationRequest, - Object campaignResponse, PlanConfiguration planConfig, Workbook workbook, + private void uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfigurationRequest, + Object campaignResponse, Workbook workbook, List campaignBoundaryList, List campaignResourcesList) { File fileToUpload = null; try { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); fileToUpload = convertWorkbookToXls(workbook); - String uploadedFileStoreId = uploadConvertedFile(fileToUpload, planConfig.getTenantId()); - - if (config.isIntegrateWithAdminConsole()) { - campaignIntegrationUtil.updateCampaignResources(uploadedFileStoreId, campaignResourcesList, - fileToUpload.getName()); - - campaignIntegrationUtil.updateCampaignDetails(planConfigurationRequest, campaignResponse, - campaignBoundaryList, campaignResourcesList); + if (planConfig.getStatus().equals(config.getPlanConfigTriggerPlanEstimatesStatus())) { + String uploadedFileStoreId = uploadConvertedFile(fileToUpload, planConfig.getTenantId()); + planUtil.setFileStoreIdForPopulationTemplate(planConfigurationRequest, uploadedFileStoreId); + planUtil.update(planConfigurationRequest); } - return uploadedFileStoreId; + if (planConfig.getStatus().equals(config.getPlanConfigUpdatePlanEstimatesIntoOutputFileStatus()) && config.isIntegrateWithAdminConsole()) { + String uploadedFileStoreId = uploadConvertedFile(fileToUpload, planConfig.getTenantId()); + campaignIntegrationUtil.updateResourcesInProjectFactory(planConfigurationRequest, uploadedFileStoreId); + } } finally { try { if (fileToUpload != null && !fileToUpload.delete()) { @@ -191,32 +182,48 @@ private String uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfi * Processes each sheet in the workbook for plan configuration data. * Validates column names, processes rows, and integrates campaign details. * - * @param planConfigurationRequest The request containing configuration details including tenant ID. + * @param request The request containing configuration details including tenant ID. * @param fileStoreId The ID of the uploaded file in the file store. * @param campaignResponse The response object containing campaign details. - * @param planConfig The configuration details specific to the plan. * @param excelWorkbook The workbook containing sheets to be processed. * @param campaignBoundaryList List of boundary objects related to the campaign. - * @param campaignResourcesList List of campaign resources to be integrated. * @param dataFormatter The data formatter for formatting cell values. */ - private void processSheets(PlanConfigurationRequest planConfigurationRequest, String fileStoreId, - Object campaignResponse, PlanConfiguration planConfig, Workbook excelWorkbook, - List campaignBoundaryList, List campaignResourcesList, - DataFormatter dataFormatter) { - LocaleResponse localeResponse = localeUtil.searchLocale(planConfigurationRequest); - CampaignResponse campaign = parseCampaignResponse(campaignResponse); - Map attributeNameVsDataTypeMap = prepareAttributeVsIndexMap(planConfigurationRequest, - fileStoreId, campaign, planConfig); - List boundaryCodeList = getBoundaryCodeList(planConfigurationRequest, campaign, planConfig); - + //TODO: processsheetforestimate and processsheetforcensus + private void processSheets(PlanConfigurationRequest request, String fileStoreId, + Object campaignResponse, Workbook excelWorkbook, + List campaignBoundaryList, + DataFormatter dataFormatter) { + CampaignResponse campaign = campaignIntegrationUtil.parseCampaignResponse(campaignResponse); + LocaleResponse localeResponse = localeUtil.searchLocale(request); + Object mdmsData = mdmsUtil.fetchMdmsData(request.getRequestInfo(), + request.getPlanConfiguration().getTenantId()); + planConfigurationUtil.orderPlanConfigurationOperations(request); + enrichmentUtil.enrichResourceMapping(request, localeResponse, campaign.getCampaign().get(0).getProjectType(), fileStoreId); + Map attributeNameVsDataTypeMap = prepareAttributeVsIndexMap(request, + fileStoreId, campaign, request.getPlanConfiguration(), mdmsData); + + List boundaryCodeList = getBoundaryCodeList(request, campaign); + Map mappedValues = request.getPlanConfiguration().getResourceMapping().stream() + .filter(f -> f.getFilestoreId().equals(fileStoreId)) + .collect(Collectors.toMap( + ResourceMapping::getMappedTo, + ResourceMapping::getMappedFrom, + (existing, replacement) -> existing, + LinkedHashMap::new + )); excelWorkbook.forEach(excelWorkbookSheet -> { - if (isSheetAlloedToProcess(planConfigurationRequest, excelWorkbookSheet.getSheetName(),localeResponse)) { - Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(excelWorkbookSheet); - List columnNamesList = mapOfColumnNameAndIndex.keySet().stream().toList(); - parsingUtil.validateColumnNames(columnNamesList, planConfig, fileStoreId); - processRows(planConfigurationRequest, excelWorkbookSheet, dataFormatter, fileStoreId, - campaignBoundaryList, attributeNameVsDataTypeMap, boundaryCodeList); + if (isSheetAllowedToProcess(request, excelWorkbookSheet.getSheetName(), localeResponse)) { + if (request.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerPlanEstimatesStatus())) { + enrichmentUtil.enrichsheetWithApprovedCensusRecords(excelWorkbookSheet, request, fileStoreId, mappedValues); + processRows(request, excelWorkbookSheet, dataFormatter, fileStoreId, + campaignBoundaryList, attributeNameVsDataTypeMap, boundaryCodeList); + } else if (request.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerCensusRecordsStatus())) { + processRowsForCensusRecords(request, excelWorkbookSheet, + fileStoreId, attributeNameVsDataTypeMap, boundaryCodeList, campaign.getCampaign().get(0).getHierarchyType()); + } else if (request.getPlanConfiguration().getStatus().equals(config.getPlanConfigUpdatePlanEstimatesIntoOutputFileStatus())) { + enrichmentUtil.enrichsheetWithApprovedPlanEstimates(excelWorkbookSheet, request, fileStoreId, mappedValues); + } } }); } @@ -241,8 +248,41 @@ private void processSheets(PlanConfigurationRequest planConfigurationRequest, St */ private void processRows(PlanConfigurationRequest planConfigurationRequest, Sheet sheet, DataFormatter dataFormatter, String fileStoreId, List campaignBoundaryList, Map attributeNameVsDataTypeMap, List boundaryCodeList) { PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + performRowLevelCalculations(planConfigurationRequest, sheet, dataFormatter, fileStoreId, campaignBoundaryList, planConfig, attributeNameVsDataTypeMap, boundaryCodeList); + } + + private void processRowsForCensusRecords(PlanConfigurationRequest planConfigurationRequest, Sheet sheet, String fileStoreId, Map attributeNameVsDataTypeMap, List boundaryCodeList, String hierarchyType) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + + Map mappedValues = planConfig.getResourceMapping().stream() + .filter(f -> f.getFilestoreId().equals(fileStoreId)) + .collect(Collectors.toMap( + ResourceMapping::getMappedTo, + ResourceMapping::getMappedFrom, + (existing, replacement) -> existing, + LinkedHashMap::new + )); + + Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet); + Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0, + parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues); Row firstRow = null; - performRowLevelCalculations(planConfigurationRequest, sheet, dataFormatter, fileStoreId, campaignBoundaryList, planConfig, attributeNameVsDataTypeMap, boundaryCodeList, firstRow); + + for (Row row : sheet) { + if (parsingUtil.isRowEmpty(row)) + continue; + + if (row.getRowNum() == 0) { + firstRow = row; + continue; + } + + validateRows(indexOfBoundaryCode, row, firstRow, attributeNameVsDataTypeMap, mappedValues, mapOfColumnNameAndIndex, + planConfigurationRequest, boundaryCodeList, sheet); + JsonNode currentRow = createFeatureNodeFromRow(row, mapOfColumnNameAndIndex); + + censusUtil.create(planConfigurationRequest, currentRow, mappedValues, hierarchyType); + } } /** @@ -250,12 +290,11 @@ private void processRows(PlanConfigurationRequest planConfigurationRequest, Shee * * @param planConfigurationRequest The request containing configuration details including tenant ID. * @param campaign The campaign response object containing campaign details. - * @param planConfig The configuration details specific to the plan. * @return A list of boundary codes corresponding to the specified hierarchy type and tenant ID. */ private List getBoundaryCodeList(PlanConfigurationRequest planConfigurationRequest, - CampaignResponse campaign, PlanConfiguration planConfig) { - BoundarySearchResponse boundarySearchResponse = boundaryUtil.search(planConfig.getTenantId(), + CampaignResponse campaign) { + BoundarySearchResponse boundarySearchResponse = boundaryUtil.search(planConfigurationRequest.getPlanConfiguration().getTenantId(), campaign.getCampaign().get(0).getHierarchyType(), planConfigurationRequest); List boundaryList = new ArrayList<>(); List boundaryCodeList = getAllBoundaryPresentforHierarchyType( @@ -272,29 +311,16 @@ private List getBoundaryCodeList(PlanConfigurationRequest planConfigurat * @param planConfig The configuration details specific to the plan. * @return A map of attribute names to their corresponding indices or data types. */ + + //TODO: fetch from adminSchema master private Map prepareAttributeVsIndexMap(PlanConfigurationRequest planConfigurationRequest, - String fileStoreId, CampaignResponse campaign, PlanConfiguration planConfig) { - Object mdmsData = mdmsUtil.fetchMdmsData(planConfigurationRequest.getRequestInfo(), - planConfigurationRequest.getPlanConfiguration().getTenantId()); + String fileStoreId, CampaignResponse campaign, PlanConfiguration planConfig, Object mdmsData) { org.egov.processor.web.models.File file = planConfig.getFiles().stream() .filter(f -> f.getFilestoreId().equalsIgnoreCase(fileStoreId)).findFirst().get(); - Map attributeNameVsDataTypeMap = mdmsUtil.filterMasterData(mdmsData.toString(), file.getInputFileType(), - file.getTemplateIdentifier(), campaign.getCampaign().get(0).getProjectType()); - return attributeNameVsDataTypeMap; + return mdmsUtil.filterMasterData(mdmsData.toString(), file.getInputFileType(), + file.getTemplateIdentifier(), campaign.getCampaign().get(0).getProjectType()); } - - /** - * Parses an object representing campaign response into a CampaignResponse object. - * - * @param campaignResponse The object representing campaign response to be parsed. - * @return CampaignResponse object parsed from the campaignResponse. - */ - private CampaignResponse parseCampaignResponse(Object campaignResponse) { - CampaignResponse campaign = null; - campaign = objectMapper.convertValue(campaignResponse, CampaignResponse.class); - return campaign; - } /** * Performs row-level calculations and processing on each row in the sheet. @@ -309,14 +335,23 @@ private CampaignResponse parseCampaignResponse(Object campaignResponse) { * @param planConfig The configuration details specific to the plan. * @param attributeNameVsDataTypeMap Mapping of attribute names to their data types. * @param boundaryCodeList List of boundary codes. - * @param firstRow The first row of the sheet. */ private void performRowLevelCalculations(PlanConfigurationRequest planConfigurationRequest, Sheet sheet, DataFormatter dataFormatter, String fileStoreId, List campaignBoundaryList, - PlanConfiguration planConfig, Map attributeNameVsDataTypeMap, List boundaryCodeList, - Row firstRow) { + PlanConfiguration planConfig, Map attributeNameVsDataTypeMap, List boundaryCodeList) { + Row firstRow = null; + Map mappedValues = planConfig.getResourceMapping().stream() + .filter(f -> f.getFilestoreId().equals(fileStoreId)) + .collect(Collectors.toMap(ResourceMapping::getMappedTo, ResourceMapping::getMappedFrom)); + Map assumptionValueMap = calculationUtil + .convertAssumptionsToMap(planConfig.getAssumptions()); + Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet); + + Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0, + parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues); + for (Row row : sheet) { - if(isRowEmpty(row)) + if(parsingUtil.isRowEmpty(row)) continue; if (row.getRowNum() == 0) { @@ -325,18 +360,9 @@ private void performRowLevelCalculations(PlanConfigurationRequest planConfigurat } Map resultMap = new HashMap<>(); - Map mappedValues = planConfig.getResourceMapping().stream() - .filter(f -> f.getFilestoreId().equals(fileStoreId)) - .collect(Collectors.toMap(ResourceMapping::getMappedTo, ResourceMapping::getMappedFrom)); - Map assumptionValueMap = calculationUtil - .convertAssumptionsToMap(planConfig.getAssumptions()); - Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet); - - Integer indexOfBoundaryCode = campaignIntegrationUtil.getIndexOfBoundaryCode(0, - campaignIntegrationUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues); validateRows(indexOfBoundaryCode, row, firstRow, attributeNameVsDataTypeMap, mappedValues, mapOfColumnNameAndIndex, planConfigurationRequest, boundaryCodeList, sheet); - JsonNode feature = createFeatureNodeFromRow(row, dataFormatter, mapOfColumnNameAndIndex); + JsonNode feature = createFeatureNodeFromRow(row, mapOfColumnNameAndIndex); performCalculationsOnOperations(sheet, planConfig, row, resultMap, mappedValues, assumptionValueMap, feature); if (config.isIntegrateWithAdminConsole()) @@ -344,29 +370,10 @@ private void performRowLevelCalculations(PlanConfigurationRequest planConfigurat mapOfColumnNameAndIndex, campaignBoundaryList, resultMap); planUtil.create(planConfigurationRequest, feature, resultMap, mappedValues); // TODO: remove after testing - printRow(sheet, row); + parsingUtil.printRow(sheet, row); } } - /** - * Checks if a given row is empty. - * - * A row is considered empty if it is null or if all of its cells are empty or of type BLANK. - * - * @param row the Row to check - * @return true if the row is empty, false otherwise - */ - public static boolean isRowEmpty(Row row) { - if (row == null) { - return true; - } - for (Cell cell : row) { - if (cell != null && cell.getCellType() != CellType.BLANK) { - return false; - } - } - return true; - } /** * Performs calculations on operations for a specific row in the sheet. @@ -407,7 +414,7 @@ private void performCalculationsOnOperations(Sheet sheet, PlanConfiguration plan * * @param convertedFile The converted XLS file to upload. * @param tenantId The tenant ID for the file upload. - * @return The file store ID of the uploaded file, or null if an error occurred. + * @return The file store ID of the uploaded file, or null if an error occurred. */ private String uploadConvertedFile(File convertedFile, String tenantId) { if (convertedFile != null) { @@ -458,12 +465,10 @@ private File convertWorkbookToXls(Workbook workbook) { * Creates a JSON feature node from a row in the Excel sheet. * * @param row The row in the Excel sheet. - * @param dataFormatter The data formatter for formatting cell values. * @param columnIndexMap The mapping of column names to column indices. * @return The JSON feature node representing the row. */ - private JsonNode createFeatureNodeFromRow(Row row, DataFormatter dataFormatter, - Map columnIndexMap) { + private JsonNode createFeatureNodeFromRow(Row row, Map columnIndexMap) { ObjectNode featureNode = objectMapper.createObjectNode(); ObjectNode propertiesNode = featureNode.putObject("properties"); @@ -474,47 +479,51 @@ private JsonNode createFeatureNodeFromRow(Row row, DataFormatter dataFormatter, // Get the cell value from the row based on the columnIndex Cell cell = row.getCell(columnIndex); - String cellValue = dataFormatter.formatCellValue(cell); - - // Add the columnName and cellValue to the propertiesNode - propertiesNode.put(columnName, cellValue); - } -// System.out.println("Feature Node ---- > " + featureNode); - return featureNode; - } + if (cell == null) { + // Handle null cells if needed + propertiesNode.putNull(columnName); + continue; + } - public void printRow(Sheet sheet, Row row) { - System.out.print("Row -> "); - for (Cell cell : row) { - int columnIndex = cell.getColumnIndex(); - // String columnName = sheet.getRow(0).getCell(columnIndex).toString(); - // System.out.print("Column " + columnName + " - "); switch (cell.getCellType()) { - case STRING: - System.out.print(cell.getStringCellValue() + "\t"); - break; - case NUMERIC: - if (DateUtil.isCellDateFormatted(cell)) { - System.out.print(cell.getDateCellValue() + "\t"); - } else { - System.out.print(cell.getNumericCellValue() + "\t"); - } - break; - case BOOLEAN: - System.out.print(cell.getBooleanCellValue() + "\t"); - break; - case FORMULA: - System.out.print(cell.getCellFormula() + "\t"); - break; - case BLANK: - System.out.print("\t"); - break; - default: - System.out.print("\t"); - break; + case STRING: + propertiesNode.put(columnName, cell.getStringCellValue()); + break; + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + // Handle date values + propertiesNode.put(columnName, cell.getDateCellValue().toString()); + } else { + propertiesNode.put(columnName, BigDecimal.valueOf(cell.getNumericCellValue())); + } + break; + case BOOLEAN: + propertiesNode.put(columnName, cell.getBooleanCellValue()); + break; + case FORMULA: + // Attempt to get the cached formula result value directly + switch (cell.getCachedFormulaResultType()) { + case NUMERIC: + propertiesNode.put(columnName, BigDecimal.valueOf(cell.getNumericCellValue())); + break; + case STRING: + propertiesNode.put(columnName, cell.getStringCellValue()); + break; + case BOOLEAN: + propertiesNode.put(columnName, cell.getBooleanCellValue()); + break; + default: + propertiesNode.putNull(columnName); + break; + } + break; + default: + propertiesNode.putNull(columnName); + break; } } - System.out.println(); // Move to the next line after printing the row + + return featureNode; } /** @@ -543,13 +552,13 @@ public void validateRows(Integer indexOfBoundaryCode, Row row, Row columnHeaderR boundaryCodeList); } catch (JsonProcessingException e) { log.info(ServiceConstants.INPUT_IS_NOT_VALID + (row.getRowNum() + 1) + " at sheet - " + sheet); - planConfigurationRequest.getPlanConfiguration().setStatus(StatusEnum.INVALID_DATA); + planConfigurationRequest.getPlanConfiguration().setStatus("INVALID_DATA"); planUtil.update(planConfigurationRequest); throw new CustomException(Integer.toString(HttpStatus.INTERNAL_SERVER_ERROR.value()), ServiceConstants.INPUT_IS_NOT_VALID + row.getRowNum() + " at sheet - " + sheet); } catch (CustomException customException) { log.info(customException.toString()+ "at sheet - " + sheet.getSheetName()); - planConfigurationRequest.getPlanConfiguration().setStatus(StatusEnum.INVALID_DATA); + planConfigurationRequest.getPlanConfiguration().setStatus("INVALID_DATA"); planUtil.update(planConfigurationRequest); throw new CustomException(Integer.toString(HttpStatus.INTERNAL_SERVER_ERROR.value()), customException.getMessage()+ "at sheet - " + sheet.getSheetName()); @@ -718,13 +727,14 @@ public List getAllBoundaryPresentforHierarchyType(List * @throws JsonMappingException If there's an issue mapping JSON response to Java objects. * @throws JsonProcessingException If there's an issue processing JSON during conversion. */ - private boolean isSheetAlloedToProcess(PlanConfigurationRequest planConfigurationRequest, String sheetName,LocaleResponse localeResponse) { + private boolean isSheetAllowedToProcess(PlanConfigurationRequest planConfigurationRequest, String sheetName, LocaleResponse localeResponse) { Map mdmsDataConstants = mdmsUtil.fetchMdmsDataForCommonConstants( planConfigurationRequest.getRequestInfo(), planConfigurationRequest.getPlanConfiguration().getTenantId()); - String value = (String) mdmsDataConstants.get("readMeSheetName"); + for (Locale locale : localeResponse.getMessages()) { - if ((locale.getCode().equalsIgnoreCase(value))) { + if ((locale.getCode().equalsIgnoreCase((String) mdmsDataConstants.get(READ_ME_SHEET_NAME))) + || locale.getCode().equalsIgnoreCase(HCM_ADMIN_CONSOLE_BOUNDARY_DATA)) { if (sheetName.equals(locale.getMessage())) return false; } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/FileParser.java b/health-services/resource-generator/src/main/java/org/egov/processor/service/FileParser.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/service/FileParser.java rename to health-services/resource-generator/src/main/java/org/egov/processor/service/FileParser.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/GeoJsonParser.java b/health-services/resource-generator/src/main/java/org/egov/processor/service/GeoJsonParser.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/service/GeoJsonParser.java rename to health-services/resource-generator/src/main/java/org/egov/processor/service/GeoJsonParser.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ResourceEstimationService.java b/health-services/resource-generator/src/main/java/org/egov/processor/service/ResourceEstimationService.java similarity index 82% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ResourceEstimationService.java rename to health-services/resource-generator/src/main/java/org/egov/processor/service/ResourceEstimationService.java index c1b1ab07b7a..978633933b9 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ResourceEstimationService.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/service/ResourceEstimationService.java @@ -1,9 +1,7 @@ package org.egov.processor.service; -import java.util.HashMap; -import java.util.Map; - +import lombok.extern.slf4j.Slf4j; import org.egov.processor.config.Configuration; import org.egov.processor.config.ServiceConstants; import org.egov.processor.repository.ServiceRequestRepository; @@ -11,10 +9,12 @@ import org.egov.processor.web.models.File; import org.egov.processor.web.models.PlanConfiguration; import org.egov.processor.web.models.PlanConfigurationRequest; +import org.egov.processor.web.models.campaignManager.CampaignResponse; import org.egov.processor.web.models.campaignManager.CampaignSearchRequest; import org.springframework.stereotype.Service; -import lombok.extern.slf4j.Slf4j; +import java.util.HashMap; +import java.util.Map; @Service @Slf4j @@ -48,7 +48,8 @@ public void estimateResources(PlanConfigurationRequest planConfigurationRequest) Map parserMap = getInputFileTypeMap(); Object campaignSearchResponse = performCampaignSearch(planConfigurationRequest); - processFiles(planConfigurationRequest, planConfiguration, parserMap, campaignSearchResponse); + processFacilityFile(planConfigurationRequest, campaignSearchResponse); + processFiles(planConfigurationRequest, planConfiguration, parserMap, campaignSearchResponse); } /** @@ -108,5 +109,21 @@ public Map getInputFileTypeMap() return parserMap; } + + /** + * Processes the facility file by parsing the campaign response and initiating + * a data creation call to the Project Factory service. + * + * @param planConfigurationRequest the request containing plan configuration details + * @param campaignResponseObject the campaign response object to be parsed + */ + public void processFacilityFile(PlanConfigurationRequest planConfigurationRequest, Object campaignResponseObject) { + if (planConfigurationRequest.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerPlanFacilityMappingsStatus())) { + CampaignResponse campaignResponse = campaignIntegrationUtil.parseCampaignResponse(campaignResponseObject); + campaignIntegrationUtil.createProjectFactoryDataCall(planConfigurationRequest, campaignResponse); + log.info("Facility Data creation successful."); + } + } + } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ShapeFileParser.java b/health-services/resource-generator/src/main/java/org/egov/processor/service/ShapeFileParser.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/service/ShapeFileParser.java rename to health-services/resource-generator/src/main/java/org/egov/processor/service/ShapeFileParser.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/BoundaryUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/BoundaryUtil.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/BoundaryUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/BoundaryUtil.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/CalculationUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java similarity index 74% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/CalculationUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java index d6b53891a8b..551206d9b94 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/CalculationUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java @@ -2,13 +2,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.egov.processor.config.ServiceConstants; import org.egov.processor.web.models.Assumption; import org.egov.processor.web.models.Operation; @@ -16,6 +9,13 @@ import org.egov.processor.web.models.PlanConfigurationRequest; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import static org.egov.processor.config.ServiceConstants.PROPERTIES; @@ -86,21 +86,23 @@ public void calculateResources(JsonNode jsonNode, PlanConfigurationRequest planC /** * Retrieves the input value from the JSON node based on the input and input mapping. * - * @param resultMap The map containing previous results. - * @param feature The JSON node feature. - * @param input The input key. - * @param columnName The input from mapping. + * @param resultMap The map containing previous results. + * @param feature The JSON node feature. + * @param assumptionValueMap The assumptions and their value map from plan config. + * @param input The input key. + * @param columnName The input from mapping. * @return The input value. */ - public BigDecimal getInputValueFromJsonFeature(Map resultMap, JsonNode feature, String input, String columnName) { - if (resultMap.containsKey(input)) { - return resultMap.get(input); - } else { - if (feature.get(PROPERTIES).get(columnName) != null) { + private BigDecimal getInputValueFromFeatureOrMap(JsonNode feature, Map resultMap, Map assumptionValueMap, String input, String columnName) { + // Try to fetch the value from resultMap, If not found in the resultMap, use the assumptionValueMap as a fallback + BigDecimal inputValue = resultMap.getOrDefault(input, assumptionValueMap.get(input)); + + // Try to fetch the value from the feature (if it exists) + if(ObjectUtils.isEmpty(inputValue)) { + if (feature.has(PROPERTIES) && feature.get(PROPERTIES).has(columnName)) { try { String cellValue = String.valueOf(feature.get(PROPERTIES).get(columnName)); BigDecimal value; - // Handle scientific notation if (cellValue.contains(ServiceConstants.SCIENTIFIC_NOTATION_INDICATOR)) { value = new BigDecimal(cellValue); } else { @@ -109,12 +111,12 @@ public BigDecimal getInputValueFromJsonFeature(Map resultMap } return value; } catch (NumberFormatException | NullPointerException e) { - return BigDecimal.ZERO; - } - } else { - throw new CustomException("INPUT_VALUE_NOT_FOUND", "Input value not found: " + input); + // Handle potential parsing issues + throw new CustomException("INPUT_VALUE_NOT_FOUND", "Input value not found: " + input); } } } + + return inputValue; } /** @@ -127,12 +129,18 @@ public BigDecimal getInputValueFromJsonFeature(Map resultMap * @param resultMap A map to store and update the calculated results. * @return The calculated result as a BigDecimal. */ - public BigDecimal calculateResult(Operation operation, JsonNode feature, Map mappedValues, Map assumptionValueMap, Map resultMap) - { + public BigDecimal calculateResult(Operation operation, JsonNode feature, Map mappedValues, Map assumptionValueMap, Map resultMap) { + // Fetch the input value String input = operation.getInput(); String inputFromMapping = mappedValues.get(input); - BigDecimal inputValue = getInputValueFromJsonFeature(resultMap, feature, operation.getInput(), inputFromMapping); - BigDecimal assumptionValue = assumptionValueMap.get(operation.getAssumptionValue()); + BigDecimal inputValue = getInputValueFromFeatureOrMap(feature, resultMap, assumptionValueMap, input, inputFromMapping); + + // Fetch the assumption value with priority: feature -> resultMap -> assumptionValueMap + String assumptionKey = operation.getAssumptionValue(); + String assumptionFromMapping = mappedValues.get(assumptionKey); + BigDecimal assumptionValue = getInputValueFromFeatureOrMap(feature, resultMap, assumptionValueMap, assumptionKey, assumptionFromMapping); + + // Calculate and return the output return calculateOutputValue(inputValue, operation.getOperator(), assumptionValue); } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java similarity index 61% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java index d14b3512aef..9bfbe7cb62c 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java @@ -1,48 +1,25 @@ package org.egov.processor.util; -import static org.egov.processor.config.ServiceConstants.PROPERTIES; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; - +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.apache.poi.ss.usermodel.DataFormatter; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.egov.processor.config.Configuration; import org.egov.processor.config.ServiceConstants; import org.egov.processor.repository.ServiceRequestRepository; -import org.egov.processor.service.ExcelParser; import org.egov.processor.web.models.File; -import org.egov.processor.web.models.Operation; import org.egov.processor.web.models.PlanConfiguration; import org.egov.processor.web.models.PlanConfigurationRequest; -import org.egov.processor.web.models.ResourceMapping; -import org.egov.processor.web.models.campaignManager.Boundary; -import org.egov.processor.web.models.campaignManager.CampaignDetails; -import org.egov.processor.web.models.campaignManager.CampaignRequest; -import org.egov.processor.web.models.campaignManager.CampaignResources; -import org.egov.processor.web.models.campaignManager.CampaignResponse; -import org.egov.processor.web.models.campaignManager.CampaignSearchRequest; +import org.egov.processor.web.models.campaignManager.*; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; +import java.util.Map.Entry; -import lombok.extern.slf4j.Slf4j; +import static org.egov.processor.config.ServiceConstants.*; @Component @Slf4j @@ -51,6 +28,7 @@ public class CampaignIntegrationUtil { private ServiceRequestRepository serviceRequestRepository; private Configuration config; private ObjectMapper mapper; + private ParsingUtil parsingUtil; public CampaignIntegrationUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, ObjectMapper mapper, FilestoreUtil filestoreUtil, ParsingUtil parsingUtil) { @@ -58,6 +36,50 @@ public CampaignIntegrationUtil(ServiceRequestRepository serviceRequestRepository this.serviceRequestRepository = serviceRequestRepository; this.config = config; this.mapper = mapper; + this.parsingUtil= parsingUtil; + } + + /** + * Updates resources in the Project Factory by calling an external API with the given plan configuration + * request and file store ID. Logs the operation status. + * + * @param planConfigurationRequest The plan configuration request details. + * @param fileStoreId The file store ID to update. + * @throws CustomException if the API call fails. + */ + public void updateResourcesInProjectFactory(PlanConfigurationRequest planConfigurationRequest, String fileStoreId) { + try { + serviceRequestRepository.fetchResult( + new StringBuilder(config.getProjectFactoryHostEndPoint() + config.getCampaignIntegrationFetchFromMicroplanEndPoint()), + buildMicroplanDetailsForUpdate(planConfigurationRequest, fileStoreId)); + log.info("Updated resources file into project factory - " + fileStoreId); + } catch (Exception e) { + log.error(ERROR_WHILE_CALLING_MICROPLAN_API + planConfigurationRequest.getPlanConfiguration().getId(), e); + throw new CustomException(ERROR_WHILE_CALLING_MICROPLAN_API, e.toString()); + } + + } + + /** + * Builds a campaign request object for updating campaign details based on the provided plan configuration request and campaign response. + * + * @param planConfigurationRequest The plan configuration request containing necessary information for updating the campaign. + * @param fileStoreId The filestoreId with calculated resources + * @return The microplan details request object built for updating resource filestore id. + */ + private MicroplanDetailsRequest buildMicroplanDetailsForUpdate(PlanConfigurationRequest planConfigurationRequest, String fileStoreId) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + + MicroplanDetails microplanDetails = MicroplanDetails.builder() + .tenantId(planConfig.getTenantId()) + .planConfigurationId(planConfig.getId()) + .campaignId(planConfig.getCampaignId()) + .resourceFilestoreId(fileStoreId).build(); + + return MicroplanDetailsRequest.builder() + .microplanDetails(microplanDetails) + .requestInfo(planConfigurationRequest.getRequestInfo()).build(); + } /** @@ -83,7 +105,28 @@ public void updateCampaignDetails(PlanConfigurationRequest planConfigurationRequ log.info("Campaign Integration successful."); } catch (Exception e) { log.error(ServiceConstants.ERROR_WHILE_SEARCHING_CAMPAIGN - + planConfigurationRequest.getPlanConfiguration().getExecutionPlanId(), e); + + planConfigurationRequest.getPlanConfiguration().getCampaignId(), e); + throw new CustomException("Failed to update campaign details in CampaignIntegration class within method updateCampaignDetails.", e.toString()); + } + } + + /** + * Sends a data creation request to the Project Factory service using the provided + * plan and campaign details. + * + * @param planConfigurationRequest the plan configuration request containing campaign data + * @param campaignResponse the response with additional campaign information + * @throws CustomException if the data creation call fails + */ + public void createProjectFactoryDataCall(PlanConfigurationRequest planConfigurationRequest, CampaignResponse campaignResponse) { + try { + serviceRequestRepository.fetchResult( + new StringBuilder(config.getProjectFactoryHostEndPoint() + config.getCampaignIntegrationDataCreateEndPoint()), + buildResourceDetailsObjectForFacilityCreate(planConfigurationRequest, campaignResponse)); + log.info("Campaign Data create successful."); + } catch (Exception e) { + log.error(ServiceConstants.ERROR_WHILE_DATA_CREATE_CALL + + planConfigurationRequest.getPlanConfiguration().getCampaignId(), e); throw new CustomException("Failed to update campaign details in CampaignIntegration class within method updateCampaignDetails.", e.toString()); } } @@ -123,6 +166,42 @@ private CampaignRequest buildCampaignRequestForUpdate(PlanConfigurationRequest p } + /** + * Builds a {@link ResourceDetailsRequest} object for facility creation using the provided + * plan configuration and campaign details. + * + * @param planConfigurationRequest the request containing plan configuration data + * @param campaignResponse the campaign response with additional data + * @return a {@link ResourceDetailsRequest} for facility creation + * @throws CustomException if the required facility file is not found + */ + private ResourceDetailsRequest buildResourceDetailsObjectForFacilityCreate(PlanConfigurationRequest planConfigurationRequest, + CampaignResponse campaignResponse) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + + String facilityFilestoreId = String.valueOf(planConfig.getFiles().stream() + .filter(file -> FILE_TEMPLATE_IDENTIFIER_FACILITY.equals(file.getTemplateIdentifier())) + .map(File::getFilestoreId) + .findFirst() + .orElseThrow(() -> new CustomException(FILE_NOT_FOUND_CODE, FILE_NOT_FOUND_MESSAGE + FILE_TEMPLATE_IDENTIFIER_FACILITY))); + + ResourceDetails resourceDetails = ResourceDetails.builder() + .type(TYPE_FACILITY) + .hierarchyType(campaignResponse.getCampaign().get(0).getHierarchyType()) + .tenantId(planConfig.getTenantId()) + .fileStoreId(facilityFilestoreId) + .action(ACTION_CREATE) + .campaignId(planConfig.getCampaignId()) + .additionalDetails(createAdditionalDetailsforFacilityCreate(MICROPLAN_SOURCE_KEY, planConfig.getId())) + .build(); + + return ResourceDetailsRequest.builder() + .requestInfo(planConfigurationRequest.getRequestInfo()) + .resourceDetails(resourceDetails) + .build(); + + } + /** * Updates campaign boundary based on the provided plan configuration, feature, assumption values, mapped values, column index map, boundary list, and result map. * @@ -143,29 +222,13 @@ public void updateCampaignBoundary(PlanConfiguration planConfig, JsonNode featur boolean validToAdd = false; Integer indexValue = 0; Boundary boundary = new Boundary(); - List> sortedColumnList = sortColumnByIndex(mapOfColumnNameAndIndex); - indexValue = getIndexOfBoundaryCode(indexValue, sortedColumnList, mappedValues); + List> sortedColumnList = parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex); + indexValue = parsingUtil.getIndexOfBoundaryCode(indexValue, sortedColumnList, mappedValues); prepareBoundary(indexOfType, indexValue, sortedColumnList, feature, boundary, mappedValues); if (isValidToAdd(boundaryList, resultMap, validToAdd, boundary)) boundaryList.add(boundary); } - /** - * Retrieves the index value of the boundary code from the sorted column list based on the mapped values. - * - * @param indexValue The initial index value. - * @param sortedColumnList The sorted list of column names and indices. - * @param mappedValues The map containing mapped values. - * @return The index value of the boundary code. - */ - public Integer getIndexOfBoundaryCode(Integer indexValue, List> sortedColumnList,Map mappedValues) { - for (Map.Entry entry : sortedColumnList) { - if (entry.getKey().equals(mappedValues.get(ServiceConstants.BOUNDARY_CODE))) { - indexValue = entry.getValue(); - } - } - return indexValue; - } /** * Prepares a campaign boundary based on the provided index values, sorted column list, feature, and mapped values. @@ -219,22 +282,7 @@ private boolean isValidToAdd(List boundaryList, Map> sortColumnByIndex(Map mapOfColumnNameAndIndex) { - List> sortedColumnList = new ArrayList<>(mapOfColumnNameAndIndex.entrySet()); - Collections.sort(sortedColumnList, new Comparator>() { - @Override - public int compare(Map.Entry o1, Map.Entry o2) { - return o1.getValue().compareTo(o2.getValue()); - } - }); - return sortedColumnList; - } + /** * Retrieves the value of the boundary code from the feature JSON node based on the mapped values. @@ -279,9 +327,35 @@ public CampaignSearchRequest buildCampaignRequestForSearch(PlanConfigurationRequ PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); List id = new ArrayList(); - id.add(planConfig.getExecutionPlanId()); + id.add(planConfig.getCampaignId()); return CampaignSearchRequest.builder().requestInfo(planConfigurationRequest.getRequestInfo()) .campaignDetails(CampaignDetails.builder().ids(id).tenantId(planConfig.getTenantId()).build()).build(); } + + /** + * Parses an object representing campaign response into a CampaignResponse object. + * + * @param campaignResponse The object representing campaign response to be parsed. + * @return CampaignResponse object parsed from the campaignResponse. + */ + public CampaignResponse parseCampaignResponse(Object campaignResponse) { + CampaignResponse campaign = null; + campaign = mapper.convertValue(campaignResponse, CampaignResponse.class); + return campaign; + } + + public JsonNode createAdditionalDetailsforFacilityCreate(String source, String microplanId) { + try { + // Create a map to hold the additional details + Map additionalDetailsMap = new HashMap<>(); + additionalDetailsMap.put(SOURCE_KEY, source); + additionalDetailsMap.put(MICROPLAN_ID_KEY, microplanId); + + // Convert the map to a JsonNode + return mapper.valueToTree(additionalDetailsMap); + } catch (Exception e) { + throw new CustomException(UNABLE_TO_CREATE_ADDITIONAL_DETAILS_CODE, UNABLE_TO_CREATE_ADDITIONAL_DETAILS_MESSAGE);// Or throw a custom exception + } + } } diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/CensusUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/CensusUtil.java new file mode 100644 index 00000000000..2e79ff60af5 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/CensusUtil.java @@ -0,0 +1,191 @@ +package org.egov.processor.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.models.Workflow; +import org.egov.processor.config.Configuration; +import org.egov.processor.config.ServiceConstants; +import org.egov.processor.kafka.Producer; +import org.egov.processor.repository.ServiceRequestRepository; +import org.egov.processor.web.models.PlanConfiguration; +import org.egov.processor.web.models.PlanConfigurationRequest; +import org.egov.processor.web.models.census.*; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.egov.processor.config.ServiceConstants.*; + +@Component +@Slf4j +public class CensusUtil { + + private ServiceRequestRepository serviceRequestRepository; + + private Configuration config; + + private Producer producer; + + private ParsingUtil parsingUtil; + + private ObjectMapper mapper; + + public CensusUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, Producer producer, ParsingUtil parsingUtil, ObjectMapper objectMapper) { + this.serviceRequestRepository = serviceRequestRepository; + this.config = config; + this.producer = producer; + this.parsingUtil = parsingUtil; + this.mapper = objectMapper; + } + + /** + * Creates and pushes a CensusRequest based on the provided plan configuration, feature JSON node, and mapped values. + * + * @param planConfigurationRequest The plan configuration request with the necessary details. + * @param feature The JSON node containing feature data for the census. + * @param mappedValues A map of property names to their values from the feature node. + * @param heirarchyType The type of hierarchy to be used in the census. + */ + public void create(PlanConfigurationRequest planConfigurationRequest, JsonNode feature, Map mappedValues, String heirarchyType) { + CensusRequest censusRequest = buildCensusRequest(planConfigurationRequest, feature, mappedValues, heirarchyType); + try { + log.info("Census request - " + censusRequest.getCensus()); + producer.push(config.getResourceCensusCreateTopic(), censusRequest); + } catch (Exception e) { + log.error(ERROR_WHILE_PUSHING_TO_PLAN_SERVICE_FOR_LOCALITY + censusRequest.getCensus().getBoundaryCode(), e); + } + } + + /** + * Builds and returns a CensusRequest using the provided plan configuration, feature JSON node, and mapped values. + * + * @param planConfigurationRequest The plan configuration request containing configuration details. + * @param feature The feature JSON node containing property values. + * @param mappedValues The mapped values for extracting properties. + * @param heirarchyType The hierarchy type of the census. + * @return A constructed CensusRequest object with populated details. + */ + private CensusRequest buildCensusRequest(PlanConfigurationRequest planConfigurationRequest, JsonNode feature, Map mappedValues, String heirarchyType) { + + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + return CensusRequest.builder() + .census(Census.builder() + .tenantId(planConfig.getTenantId()) + .hierarchyType(heirarchyType) + .boundaryCode((String) parsingUtil.extractMappedValueFromFeatureForAnInput(ServiceConstants.BOUNDARY_CODE, feature, mappedValues)) + .type(Census.TypeEnum.PEOPLE) + .facilityAssigned(Boolean.FALSE) + .partnerAssignmentValidationEnabled(Boolean.TRUE) + .totalPopulation((BigDecimal) parsingUtil.extractMappedValueFromFeatureForAnInput(ServiceConstants.TOTAL_POPULATION, feature, mappedValues)) + .workflow(Workflow.builder().action(WORKFLOW_ACTION_INITIATE).build()) + .source(planConfig.getId()) + .additionalFields(enrichAdditionalField(feature, mappedValues)).build()) + .requestInfo(planConfigurationRequest.getRequestInfo()).build(); + + } + + /** + * Enriches and returns additional details by extracting values from the feature JSON node based on the provided mappings. + * + * @param feature The feature JSON node containing property values. + * @param mappedValues The mapped values for extracting properties. + * @return A map containing enriched additional details based on the extracted values. + */ + public List enrichAdditionalField(JsonNode feature, Map mappedValues) { + // Initialize orderCounter inside the function + List additionalFieldList = new ArrayList<>(); + int orderCounter = 1; + + for (String key : mappedValues.keySet()) { + // Skip keys in the override list + if (config.getCensusAdditionalFieldOverrideKeys().contains(key)) + continue; + + // Get the corresponding value from the feature JsonNode + Object valueFromRow = parsingUtil.extractMappedValueFromFeatureForAnInput(key, feature, mappedValues); + + // Check if the value exists in the JSON + if (!ObjectUtils.isEmpty(valueFromRow)) { + // Add additional fields with "UPLOADED" and "CONFIRMED" prefixes if key is in override list + if (config.getCensusAdditionalPrefixAppendKeys().contains(key)) { + AdditionalField uploadedField = AdditionalField.builder() + .key(UPLOADED_KEY + key) + .value((BigDecimal) valueFromRow) + .editable(Boolean.FALSE) + .showOnUi(Boolean.TRUE) + .order(orderCounter++) // Increment for "UPLOADED" field + .build(); + additionalFieldList.add(uploadedField); + + AdditionalField confirmedField = AdditionalField.builder() + .key(CONFIRMED_KEY + key) + .value((BigDecimal) valueFromRow) + .editable(Boolean.TRUE) + .showOnUi(Boolean.TRUE) + .order(orderCounter++) // Increment for "CONFIRMED" field + .build(); + additionalFieldList.add(confirmedField); + } else { + AdditionalField additionalField = AdditionalField.builder() + .key(key) + .value((BigDecimal) valueFromRow) + .order(orderCounter++) // Use and increment the local orderCounter + .build(); + if(config.getCensusAdditionalFieldShowOnUIFalseKeys().contains(key)) { + additionalField.setShowOnUi(Boolean.FALSE); + additionalField.setEditable(Boolean.FALSE); + } else { + additionalField.setShowOnUi(Boolean.TRUE); + additionalField.setEditable(Boolean.TRUE); + } + additionalFieldList.add(additionalField); + } + } + } + + return additionalFieldList; + } + + /** + * This method fetches data from Census based on the given census search request. + * + * @param searchRequest The census search request containing the search criteria. + * @return returns the census response. + */ + public CensusResponse fetchCensusRecords(CensusSearchRequest searchRequest) { + + // Get census search uri + String uri = getCensusUri().toString(); + + CensusResponse censusResponse = null; + try { + Object response = serviceRequestRepository.fetchResult(new StringBuilder(uri), searchRequest); + censusResponse = mapper.convertValue(response, CensusResponse.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_CENSUS, e); + } + + if (CollectionUtils.isEmpty(censusResponse.getCensus())) { + throw new CustomException(NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_CODE, NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_MESSAGE); + } + + return censusResponse; + } + + /** + * Builds the census search uri. + * + * @return returns the complete uri for census search. + */ + private StringBuilder getCensusUri() { + return new StringBuilder().append(config.getCensusHost()).append(config.getCensusSearchEndPoint()); + } + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java new file mode 100644 index 00000000000..d0cd40201ff --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java @@ -0,0 +1,304 @@ +package org.egov.processor.util; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.egov.common.utils.UUIDEnrichmentUtil; +import org.egov.processor.config.Configuration; +import org.egov.processor.web.PlanResponse; +import org.egov.processor.web.PlanSearchCriteria; +import org.egov.processor.web.PlanSearchRequest; +import org.egov.processor.web.models.*; +import org.egov.processor.web.models.census.Census; +import org.egov.processor.web.models.census.CensusResponse; +import org.egov.processor.web.models.census.CensusSearchCriteria; +import org.egov.processor.web.models.census.CensusSearchRequest; +import org.egov.processor.web.models.mdmsV2.Mdms; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.egov.processor.config.ServiceConstants.*; + +@Component +@Slf4j +public class EnrichmentUtil { + + private MdmsV2Util mdmsV2Util; + + private LocaleUtil localeUtil; + + private ParsingUtil parsingUtil; + + private CensusUtil censusUtil; + + private PlanUtil planUtil; + + private Configuration config; +// private MultiStateInstanceUtil centralInstanceUtil; + + public EnrichmentUtil(MdmsV2Util mdmsV2Util, LocaleUtil localeUtil, ParsingUtil parsingUtil, CensusUtil censusUtil, PlanUtil planUtil, Configuration config) { + this.mdmsV2Util = mdmsV2Util; + this.localeUtil = localeUtil; +// this.centralInstanceUtil = centralInstanceUtil; + this.parsingUtil = parsingUtil; + this.censusUtil = censusUtil; + this.planUtil = planUtil; + this.config = config; + } + + /** + * Enriches the `PlanConfiguration` with resource mappings based on MDMS data and locale messages. + * + * @param request The request containing the configuration to enrich. + * @param localeResponse The response containing locale messages. + * @param campaignType The campaign type identifier. + * @param fileStoreId The associated file store ID. + */ + public void enrichResourceMapping(PlanConfigurationRequest request, LocaleResponse localeResponse, String campaignType, String fileStoreId) + { +// String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlanConfiguration().getTenantId()); + String rootTenantId = request.getPlanConfiguration().getTenantId().split("\\.")[0]; + String uniqueIndentifier = BOUNDARY + DOT_SEPARATOR + MICROPLAN_PREFIX + campaignType; + List mdmsV2Data = mdmsV2Util.fetchMdmsV2Data(request.getRequestInfo(), rootTenantId, MDMS_ADMIN_CONSOLE_MODULE_NAME + DOT_SEPARATOR + MDMS_SCHEMA_ADMIN_SCHEMA, uniqueIndentifier); + List columnNameList = parsingUtil.extractPropertyNamesFromAdminSchema(mdmsV2Data.get(0).getData()); + + List resourceMappingList = new ArrayList<>(); + for(String columnName : columnNameList) { + ResourceMapping resourceMapping = ResourceMapping + .builder() + .filestoreId(fileStoreId) + .mappedTo(columnName) + .active(Boolean.TRUE) + .mappedFrom(localeUtil.localeSearch(localeResponse.getMessages(), columnName)) + .build(); + UUIDEnrichmentUtil.enrichRandomUuid(resourceMapping, "id"); + resourceMappingList.add(resourceMapping); + } + + //enrich plan configuration with enriched resource mapping list + request.getPlanConfiguration().setResourceMapping(resourceMappingList); + + } + + public void enrichsheetWithApprovedCensusRecords(Sheet sheet, PlanConfigurationRequest planConfigurationRequest, String fileStoreId, Map mappedValues) { + List boundaryCodes = getBoundaryCodesFromTheSheet(sheet, planConfigurationRequest, fileStoreId); + + Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet); + Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0, + parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues); + + //Getting census records for the list of boundaryCodes + List censusList = getCensusRecordsForEnrichment(planConfigurationRequest, boundaryCodes); + + // Create a map from boundaryCode to Census for quick lookups + Map censusMap = censusList.stream() + .collect(Collectors.toMap(Census::getBoundaryCode, census -> census)); + + + for(Row row: sheet) { + parsingUtil.printRow(sheet, row); + // Skip the header row and empty rows + if (row.getRowNum() == 0 || parsingUtil.isRowEmpty(row)) { + continue; + } + + // Get the boundaryCode in the current row + Cell boundaryCodeCell = row.getCell(indexOfBoundaryCode); + String boundaryCode = boundaryCodeCell.getStringCellValue(); + + Census census = censusMap.get(boundaryCode); + + if (census != null) { + // For each field in the sheetToCensusMap, update the cell if the field is editable + for (Map.Entry entry : mappedValues.entrySet()) { + String censusKey = entry.getKey(); + String sheetColumn = entry.getValue(); + + if(config.getCensusAdditionalFieldOverrideKeys().contains(censusKey)) + continue; + censusKey = config.getCensusAdditionalPrefixAppendKeys().contains(censusKey) ? CONFIRMED_KEY + censusKey : censusKey; + + // Get the column index from the mapOfColumnNameAndIndex + Integer columnIndex = mapOfColumnNameAndIndex.get(sheetColumn); + if (columnIndex != null) { + // Get the value for this field in the census, if editable + BigDecimal editableValue = getEditableValue(census, censusKey); + + if(ObjectUtils.isEmpty(editableValue)) continue; + + Cell cell = row.getCell(columnIndex); + if (cell == null) { + cell = row.createCell(columnIndex); + } + cell.setCellValue(editableValue.doubleValue()); + + } + } + } + //TODO: remove after testing + log.info("After updating values in sheet -> "); + parsingUtil.printRow(sheet, row); + + log.info("Successfully update file with approved census data."); + } + } + + public List getBoundaryCodesFromTheSheet(Sheet sheet, PlanConfigurationRequest planConfigurationRequest, String fileStoreId) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + + Map mappedValues = planConfig.getResourceMapping().stream() + .filter(f -> f.getFilestoreId().equals(fileStoreId)) + .collect(Collectors.toMap(ResourceMapping::getMappedTo, ResourceMapping::getMappedFrom)); + + Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet); + + Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0, + parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues); + + List boundaryCodes = new ArrayList<>(); + + for (Row row : sheet) { + // Skip the header row and empty rows + if (row.getRowNum() == 0 || parsingUtil.isRowEmpty(row)) { + continue; + } + + // Get the boundary code cell + Cell boundaryCodeCell = row.getCell(indexOfBoundaryCode); + + // Check if the cell is non-empty and collect its value + if (boundaryCodeCell != null && boundaryCodeCell.getCellType() == CellType.STRING) { + String boundaryCode = boundaryCodeCell.getStringCellValue().trim(); + if (!boundaryCode.isEmpty()) { + boundaryCodes.add(boundaryCode); + } + } + } + + return boundaryCodes; + } + + public List getCensusRecordsForEnrichment(PlanConfigurationRequest planConfigurationRequest, List boundaryCodes) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + CensusSearchCriteria censusSearchCriteria = CensusSearchCriteria.builder() + .tenantId(planConfig.getTenantId()) + .areaCodes(boundaryCodes) + .limit(boundaryCodes.size()) + .source(planConfig.getId()).build(); + + CensusSearchRequest censusSearchRequest = CensusSearchRequest.builder() + .censusSearchCriteria(censusSearchCriteria) + .requestInfo(planConfigurationRequest.getRequestInfo()).build(); + + CensusResponse censusResponse = censusUtil.fetchCensusRecords(censusSearchRequest); + + if(censusResponse.getCensus().isEmpty()) + throw new CustomException(NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_CODE, NO_CENSUS_FOUND_FOR_GIVEN_DETAILS_MESSAGE); + + return censusResponse.getCensus(); + + } + + private BigDecimal getEditableValue(Census census, String key) { + return census.getAdditionalFields().stream() + .filter(field -> field.getEditable() && key.equals(field.getKey())) // Filter by editability and matching key + .map(field -> field.getValue()) + .findFirst() + .orElse(null); + } + + public void enrichsheetWithApprovedPlanEstimates(Sheet sheet, PlanConfigurationRequest planConfigurationRequest, String fileStoreId, Map mappedValues) { + List boundaryCodes = getBoundaryCodesFromTheSheet(sheet, planConfigurationRequest, fileStoreId); + + Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet); + Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0, + parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues); + + //Getting census records for the list of boundaryCodes + List planList = getPlanRecordsForEnrichment(planConfigurationRequest, boundaryCodes); + + // Create a map from boundaryCode to Census for quick lookups + Map planMap = planList.stream() + .collect(Collectors.toMap(Plan::getLocality, plan -> plan)); + + List outputColumnList = planList.get(0).getResources().stream() + .map(Resource::getResourceType) + .toList(); + + + for(Row row: sheet) { + parsingUtil.printRow(sheet, row); + // Skip the header row and empty rows + if (row.getRowNum() == 0 || parsingUtil.isRowEmpty(row)) { + continue; + } + // Get the boundaryCode in the current row + Cell boundaryCodeCell = row.getCell(indexOfBoundaryCode); + String boundaryCode = boundaryCodeCell.getStringCellValue(); + + Plan planEstimate = planMap.get(boundaryCode); + + if (planEstimate != null) { + Map resourceTypeToEstimatedNumberMap = planEstimate.getResources().stream() + .collect(Collectors.toMap(Resource::getResourceType, Resource::getEstimatedNumber)); + + // Iterate over each output column to update the row cells with resource values + for (String resourceType : outputColumnList) { + BigDecimal estimatedValue = resourceTypeToEstimatedNumberMap.get(resourceType); + + if (estimatedValue != null) { + // Get the index of the column to update + Integer columnIndex = mapOfColumnNameAndIndex.get(resourceType); + if (columnIndex != null) { + // Update the cell with the resource value + Cell cell = row.getCell(columnIndex); + if (cell == null) { + cell = row.createCell(columnIndex); + } + cell.setCellValue(estimatedValue.doubleValue()); + } + } + } + } + //TODO: remove after testing + log.info("After updating values in sheet -> "); + parsingUtil.printRow(sheet, row); + + log.info("Successfully update file with approved census data."); + } + } + + + public List getPlanRecordsForEnrichment(PlanConfigurationRequest planConfigurationRequest, List boundaryCodes) { + PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration(); + PlanSearchCriteria planSearchCriteria = PlanSearchCriteria.builder() + .tenantId(planConfig.getTenantId()) + .locality(boundaryCodes) + .limit(boundaryCodes.size()) + .planConfigurationId(planConfig.getId()).build(); + + PlanSearchRequest planSearchRequest = PlanSearchRequest.builder() + .planSearchCriteria(planSearchCriteria) + .requestInfo(planConfigurationRequest.getRequestInfo()).build(); + + PlanResponse planResponse = planUtil.search(planSearchRequest); + + if(planResponse.getPlan().isEmpty()) + throw new CustomException(NO_PLAN_FOUND_FOR_GIVEN_DETAILS_CODE, NO_PLAN_FOUND_FOR_GIVEN_DETAILS_MESSAGE); + + return planResponse.getPlan(); + } + + + + +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/FilestoreUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/FilestoreUtil.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/FilestoreUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/FilestoreUtil.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/LocaleUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/LocaleUtil.java similarity index 89% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/LocaleUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/LocaleUtil.java index 1ecb725308f..12fefec0405 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/LocaleUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/LocaleUtil.java @@ -5,12 +5,9 @@ import org.egov.processor.config.Configuration; import org.egov.processor.config.ServiceConstants; import org.egov.processor.repository.ServiceRequestRepository; +import org.egov.processor.web.models.Locale; import org.egov.processor.web.models.LocaleResponse; import org.egov.processor.web.models.PlanConfigurationRequest; -import org.egov.processor.web.models.boundary.BoundarySearchResponse; -import org.egov.processor.web.models.campaignManager.Boundary; -import org.egov.processor.web.models.campaignManager.CampaignResources; -import org.egov.processor.web.models.campaignManager.CampaignResponse; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; @@ -71,10 +68,19 @@ public LocaleResponse searchLocale(PlanConfigurationRequest planConfigurationReq log.info("Locale Search successful."); return localeResponse; } catch (Exception e) { - log.error(ServiceConstants.ERROR_WHILE_SEARCHING_LOCALE + localeToUse + " and tenantId" + tenantId, e); + log.error(ServiceConstants.ERROR_WHILE_SEARCHING_LOCALE + localeToUse + " and tenantId " + tenantId, e); throw new CustomException( - ServiceConstants.ERROR_WHILE_SEARCHING_LOCALE + localeToUse + " and tenantId" + tenantId, + ServiceConstants.ERROR_WHILE_SEARCHING_LOCALE + localeToUse + " and tenantId " + tenantId, e.toString()); } } + + public String localeSearch(List localeMessages, String code) { + for (Locale locale : localeMessages) { + if (locale.getCode().equalsIgnoreCase(code)) { + return locale.getMessage(); // Return the message if code matches + } + } + return null; + } } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/MdmsUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java similarity index 99% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/MdmsUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java index 229dbf57f45..e3019ded61b 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/MdmsUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java @@ -132,7 +132,7 @@ public Map filterMasterData(String masterDataJson, File.InputFil String type = (String) schema.get(ServiceConstants.MDMS_SCHEMA_TYPE); String campaign = (String) schema.get(ServiceConstants.MDMS_CAMPAIGN_TYPE); // String fileT = InputFileTypeEnum.valueOf(type); - if (schema.get(ServiceConstants.MDMS_SCHEMA_SECTION).equals(ServiceConstants.FILE_TEMPLATE_IDENTIFIER) + if (schema.get(ServiceConstants.MDMS_SCHEMA_SECTION).equals(ServiceConstants.FILE_TEMPLATE_IDENTIFIER_POPULATION) && campaign.equals(campaignType) && type.equals(fileType.toString())) { Map schemaProperties = (Map) schema.get("schema"); properties = (Map) schemaProperties.get("Properties"); diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java new file mode 100644 index 00000000000..85b87872658 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java @@ -0,0 +1,79 @@ +package org.egov.processor.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.processor.config.Configuration; +import org.egov.processor.web.models.mdmsV2.Mdms; +import org.egov.processor.web.models.mdmsV2.MdmsCriteriaReqV2; +import org.egov.processor.web.models.mdmsV2.MdmsCriteriaV2; +import org.egov.processor.web.models.mdmsV2.MdmsResponseV2; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; +import java.util.List; + +import static org.egov.processor.config.ServiceConstants.*; + +@Slf4j +@Component +public class MdmsV2Util { + + private RestTemplate restTemplate; + + private ObjectMapper objectMapper; + + private Configuration config; + + public MdmsV2Util(RestTemplate restTemplate, ObjectMapper objectMapper, Configuration config) + { + this.restTemplate = restTemplate; + this.objectMapper = objectMapper; + this.config = config; + } + + public List fetchMdmsV2Data(RequestInfo requestInfo, String tenantId, String schemaCode, String uniqueIdentifier) + { + StringBuilder uri = getMdmsV2Uri(); + MdmsCriteriaReqV2 mdmsCriteriaReqV2 = getMdmsV2Request(requestInfo, tenantId, schemaCode, uniqueIdentifier); + MdmsResponseV2 mdmsResponseV2 = null; + try { + mdmsResponseV2 = restTemplate.postForObject(uri.toString(), mdmsCriteriaReqV2, MdmsResponseV2.class); + } catch (Exception e) { + log.error(ERROR_WHILE_FETCHING_FROM_MDMS, e); + } + + if(ObjectUtils.isEmpty(mdmsResponseV2.getMdms())) + { + log.error(NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE + " - " + tenantId); + throw new CustomException(NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE, NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE); + } + + return mdmsResponseV2.getMdms(); + } + + private StringBuilder getMdmsV2Uri() + { + StringBuilder uri = new StringBuilder(); + return uri.append(config.getMdmsHost()).append(config.getMdmsV2EndPoint()); + } + + private MdmsCriteriaReqV2 getMdmsV2Request(RequestInfo requestInfo, String tenantId, String schemaCode, String uniqueIdentifier) + { + MdmsCriteriaV2 mdmsCriteriaV2 = MdmsCriteriaV2.builder() + .tenantId(tenantId) + .schemaCode(schemaCode) + .uniqueIdentifiers(Collections.singletonList(uniqueIdentifier)) + .limit(config.getDefaultLimit()) + .offset(config.getDefaultOffset()).build(); + + return MdmsCriteriaReqV2.builder() + .requestInfo(requestInfo) + .mdmsCriteriaV2(mdmsCriteriaV2).build(); + } + +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/ParsingUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java similarity index 59% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/ParsingUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java index b1902b49afb..ba10327e4c8 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/ParsingUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java @@ -1,38 +1,30 @@ package org.egov.processor.util; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.DecimalNode; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.poi.ss.usermodel.*; +import org.egov.processor.config.ServiceConstants; +import org.egov.processor.web.models.PlanConfiguration; +import org.egov.processor.web.models.ResourceMapping; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.commons.io.FileUtils; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.DataFormatter; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; -import org.egov.processor.web.models.PlanConfiguration; -import org.egov.processor.web.models.ResourceMapping; -import org.egov.tracer.model.CustomException; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import lombok.extern.slf4j.Slf4j; +import static org.egov.processor.config.ServiceConstants.PROPERTIES; @Slf4j @Component @@ -63,6 +55,8 @@ public List fetchAttributeNamesFromJson(JsonNode jsonNode) } return columnNames; } + + public void validateColumnNames(List columnNamesList, PlanConfiguration planConfig, String fileStoreId ) { Set mappedFromSet = planConfig.getResourceMapping().stream() .filter(mapping -> Objects.equals(mapping.getFilestoreId(), fileStoreId)) @@ -99,12 +93,40 @@ public Map getAttributeNameIndexFromExcel(Sheet sheet) { String columnHeader = dataFormatter.formatCellValue(cell); columnIndexMap.put(columnHeader, i); } - List> sortedColumnList = new ArrayList<>(columnIndexMap.entrySet()); - Collections.sort(sortedColumnList, (o1, o2) -> (o1.getValue()).compareTo(o2.getValue())); - for (Map.Entry entry : sortedColumnList) { - sortedMap.put(entry.getKey(), entry.getValue()); + return columnIndexMap; + } + + /** + * Retrieves the mapped value from the feature JSON node using the mapped value for the given input, + * returning it as the appropriate data type. + * + * @param input The input value. + * @param feature The feature JSON node. + * @param mappedValues The mapped values. + * @return The value of the corresponding data type (Long, String, Boolean, etc.). + * @throws CustomException if the input value is not found in the feature JSON node or if the value type is unsupported. + */ + public Object extractMappedValueFromFeatureForAnInput(String input, JsonNode feature, Map mappedValues) { + // Get the value as a JsonNode, not a String + JsonNode mappedValueNode = feature.get(PROPERTIES).get(mappedValues.get(input)); + + // Check if the value exists in the JSON + if (!ObjectUtils.isEmpty(mappedValueNode)) { + + // Now return the value based on its actual type in the JsonNode + if (mappedValueNode instanceof DecimalNode) { + return ((DecimalNode) mappedValueNode).decimalValue(); // Returns BigDecimal + } else if (mappedValueNode.isBoolean()) { + return mappedValueNode.asBoolean(); + } else if (mappedValueNode.isTextual()) { + return mappedValueNode.asText(); + } else { + return null; + } + } + else { + return null; } - return sortedMap; } /** @@ -246,4 +268,128 @@ public File extractShapeFilesFromZip(PlanConfiguration planConfig, String fileSt return shpFile; } + /** + * Extracts the names of properties defined within the "numberProperties" and "stringProperties" arrays from admin schema + * + * @param rootNode The root JSON node from which to extract property names. + * @return A list of property names found in "numberProperties" and "stringProperties". + */ + public List extractPropertyNamesFromAdminSchema(JsonNode rootNode) { + List names = new ArrayList<>(); + + // Access the "properties" node directly from the root node + JsonNode propertiesNode = rootNode.path("properties"); + + // Extract names from "numberProperties" + JsonNode numberProperties = propertiesNode.path("numberProperties"); + if (numberProperties.isArray()) { + for (JsonNode property : numberProperties) { + String name = property.path("name").asText(null); + if (name != null) { + names.add(name); + } + } + } + + // Extract names from "stringProperties" + JsonNode stringProperties = propertiesNode.path("stringProperties"); + if (stringProperties.isArray()) { + for (JsonNode property : stringProperties) { + String name = property.path("name").asText(null); + if (name != null) { + names.add(name); + } + } + } + + return names; + } + + + /** + * Checks if a given row is empty. + * + * A row is considered empty if it is null or if all of its cells are empty or of type BLANK. + * + * @param row the Row to check + * @return true if the row is empty, false otherwise + */ + public static boolean isRowEmpty(Row row) { + if (row == null) { + return true; + } + for (Cell cell : row) { + if (cell != null && cell.getCellType() != CellType.BLANK) { + return false; + } + } + return true; + } + + /** + * Retrieves the index value of the boundary code from the sorted column list based on the mapped values. + * + * @param indexValue The initial index value. + * @param sortedColumnList The sorted list of column names and indices. + * @param mappedValues The map containing mapped values. + * @return The index value of the boundary code. + */ + public Integer getIndexOfBoundaryCode(Integer indexValue, List> sortedColumnList,Map mappedValues) { + for (Map.Entry entry : sortedColumnList) { + if (entry.getKey().equals(mappedValues.get(ServiceConstants.BOUNDARY_CODE))) { + indexValue = entry.getValue(); + } + } + return indexValue; + } + + /** + * Sorts the column names and indices based on the provided map of column names and indices. + * + * @param mapOfColumnNameAndIndex The map containing column names and their corresponding indices. + * @return The sorted list of column names and indices. + */ + public List> sortColumnByIndex(Map mapOfColumnNameAndIndex) { + List> sortedColumnList = new ArrayList<>(mapOfColumnNameAndIndex.entrySet()); + Collections.sort(sortedColumnList, new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return o1.getValue().compareTo(o2.getValue()); + } + }); + return sortedColumnList; + } + + public void printRow(Sheet sheet, Row row) { + System.out.print("Row -> "); + for (Cell cell : row) { + int columnIndex = cell.getColumnIndex(); + switch (cell.getCellType()) { + case STRING: + System.out.print(cell.getStringCellValue() + "\t"); + break; + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + System.out.print(cell.getDateCellValue() + "\t"); + } else { + System.out.print(cell.getNumericCellValue() + "\t"); + } + break; + case BOOLEAN: + System.out.print(cell.getBooleanCellValue() + "\t"); + break; + case FORMULA: + System.out.print(cell.getCellFormula() + "\t"); + break; + case BLANK: + System.out.print("\t"); + break; + default: + System.out.print("\t"); + break; + } + } + System.out.println(); // Move to the next line after printing the row + } + } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/PlanConfigurationUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanConfigurationUtil.java similarity index 85% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/PlanConfigurationUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/PlanConfigurationUtil.java index 26a11dcf976..55a8651ec56 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/PlanConfigurationUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanConfigurationUtil.java @@ -1,19 +1,17 @@ package org.egov.processor.util; import com.fasterxml.jackson.databind.ObjectMapper; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; import lombok.extern.slf4j.Slf4j; - import org.egov.processor.config.Configuration; import org.egov.processor.repository.ServiceRequestRepository; -import org.egov.processor.web.models.PlanConfiguration; -import org.egov.processor.web.models.PlanConfigurationResponse; -import org.egov.processor.web.models.PlanConfigurationSearchRequest; +import org.egov.processor.web.models.*; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + import static org.egov.processor.config.ServiceConstants.ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE; @Component @@ -54,4 +52,8 @@ public List search(PlanConfigurationSearchRequest planConfigu else return planConfigurationList; } + + public void orderPlanConfigurationOperations(PlanConfigurationRequest planConfigurationRequest) { + planConfigurationRequest.getPlanConfiguration().getOperations().sort(Comparator.comparingInt(Operation::getExecutionOrder)); + } } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/PlanUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java similarity index 70% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/util/PlanUtil.java rename to health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java index d44aaa2a59f..97c9810e0c5 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/util/PlanUtil.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java @@ -1,33 +1,26 @@ package org.egov.processor.util; -import static org.egov.processor.config.ServiceConstants.ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE_FOR_LOCALITY; -import static org.egov.processor.config.ServiceConstants.PROPERTIES; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.models.Workflow; import org.egov.processor.config.Configuration; import org.egov.processor.config.ServiceConstants; -import org.egov.processor.repository.ServiceRequestRepository; import org.egov.processor.kafka.Producer; -import org.egov.processor.web.models.Activity; -import org.egov.processor.web.models.Plan; -import org.egov.processor.web.models.PlanConfiguration; -import org.egov.processor.web.models.PlanConfigurationRequest; -import org.egov.processor.web.models.PlanConfigurationResponse; -import org.egov.processor.web.models.PlanRequest; -import org.egov.processor.web.models.Resource; +import org.egov.processor.repository.ServiceRequestRepository; +import org.egov.processor.web.PlanResponse; +import org.egov.processor.web.PlanSearchRequest; +import org.egov.processor.web.models.*; import org.egov.tracer.model.CustomException; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Map; +import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; +import static org.egov.processor.config.ServiceConstants.*; @Component @Slf4j @@ -38,11 +31,14 @@ public class PlanUtil { private Producer producer; - public PlanUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, Producer producer) { + private ObjectMapper mapper; + + public PlanUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, Producer producer, ObjectMapper mapper) { this.serviceRequestRepository = serviceRequestRepository; this.config = config; this.producer = producer; - } + this.mapper = mapper; + } /** * Creates a plan configuration request, builds a plan request from it, and pushes it to the messaging system for further processing. @@ -55,7 +51,7 @@ public PlanUtil(ServiceRequestRepository serviceRequestRepository, Configuration public void create(PlanConfigurationRequest planConfigurationRequest, JsonNode feature, Map resultMap, Map mappedValues) { PlanRequest planRequest = buildPlanRequest(planConfigurationRequest, feature, resultMap, mappedValues); - try { + try { producer.push(config.getResourceMicroplanCreateTopic(), planRequest); } catch (Exception e) { log.error(ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE_FOR_LOCALITY + planRequest.getPlan().getLocality(), e); @@ -80,7 +76,8 @@ private PlanRequest buildPlanRequest(PlanConfigurationRequest planConfigurationR .requestInfo(planConfigurationRequest.getRequestInfo()) .plan(Plan.builder() .tenantId(planConfig.getTenantId()) - .executionPlanId(planConfig.getExecutionPlanId()) + .planConfigurationId(planConfig.getId()) + .campaignId(planConfig.getCampaignId()) .locality(getBoundaryCodeValue(ServiceConstants.BOUNDARY_CODE, feature, mappedValues)) .resources(resultMap.entrySet().stream().map(result -> { @@ -91,6 +88,8 @@ private PlanRequest buildPlanRequest(PlanConfigurationRequest planConfigurationR }).collect(Collectors.toList())) .activities(new ArrayList()) .targets(new ArrayList()) + .workflow(Workflow.builder().action(WORKFLOW_ACTION_INITIATE).build()) + .isRequestFromResourceEstimationConsumer(true) .build()) .build(); @@ -124,9 +123,42 @@ public void update(PlanConfigurationRequest planConfigurationRequest) { try { producer.push(config.getResourceUpdatePlanConfigConsumerTopic(), planConfigurationRequest); - log.info("Plan Config updated because of Invalid data."); + log.info("Plan Config updated after processing."); } catch (Exception e) { log.error(ServiceConstants.ERROR_WHILE_UPDATING_PLAN_CONFIG); } } + + + public PlanResponse search(PlanSearchRequest planSearchRequest) { + + PlanResponse planResponse = null; + try { + Object response = serviceRequestRepository.fetchResult(getPlanSearchUri(), planSearchRequest); + planResponse = mapper.convertValue(response, PlanResponse.class); + } catch (Exception e) { + log.error(ServiceConstants.ERROR_WHILE_SEARCHING_PLAN); + } + + if (CollectionUtils.isEmpty(planResponse.getPlan())) { + throw new CustomException(NO_PLAN_FOUND_FOR_GIVEN_DETAILS_CODE, NO_PLAN_FOUND_FOR_GIVEN_DETAILS_MESSAGE); + } + + return planResponse; + } + + private StringBuilder getPlanSearchUri() { + return new StringBuilder().append(config.getPlanConfigHost()).append(config.getPlanSearchEndPoint()); + } + + public void setFileStoreIdForPopulationTemplate(PlanConfigurationRequest planConfigurationRequest, String fileStoreId) { + planConfigurationRequest.getPlanConfiguration().getFiles().stream() + .filter(file -> FILE_TEMPLATE_IDENTIFIER_POPULATION.equals(file.getTemplateIdentifier())) + .findFirst() + .ifPresent(file -> file.setFilestoreId(fileStoreId)); + + planConfigurationRequest.getPlanConfiguration().setWorkflow(null); + } + + } diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanResponse.java new file mode 100644 index 00000000000..5f4fc7b1882 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanResponse.java @@ -0,0 +1,42 @@ +package org.egov.processor.web; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.egov.processor.web.models.Plan; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +/** + * PlanSearchResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("Plan") + @Valid + private List plan = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; + + @JsonProperty("StatusCount") + @Valid + private Map statusCount = null; + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanSearchCriteria.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanSearchCriteria.java new file mode 100644 index 00000000000..eec1a370b39 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanSearchCriteria.java @@ -0,0 +1,57 @@ +package org.egov.processor.web; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Set; + +/** + * PlanSearchCriteria + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanSearchCriteria { + + @JsonProperty("ids") + private Set ids = null; + + @JsonProperty("tenantId") + @NotNull + private String tenantId = null; + + @JsonProperty("locality") + private List locality = null; + + @JsonProperty("campaignId") + private String campaignId = null; + + @JsonProperty("planConfigurationId") + private String planConfigurationId = null; + + @JsonProperty("status") + private String status = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("jurisdiction") + @Valid + private List jurisdiction = null; + + @JsonProperty("offset") + private Integer offset = null; + + @JsonProperty("limit") + private Integer limit = null; + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanSearchRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanSearchRequest.java new file mode 100644 index 00000000000..23e4c7eb9bc --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/PlanSearchRequest.java @@ -0,0 +1,30 @@ +package org.egov.processor.web; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * PlanSearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PlanSearchRequest { + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("PlanSearchCriteria") + @Valid + private PlanSearchCriteria planSearchCriteria = null; + + +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/controllers/FileController.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/controllers/FileController.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/controllers/FileController.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/controllers/FileController.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Activity.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Activity.java similarity index 99% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Activity.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Activity.java index fa9e1193e90..6c093ba63a2 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Activity.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Activity.java @@ -1,15 +1,17 @@ package org.egov.processor.web.models; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import java.util.List; + import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; /** * Activity diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Assumption.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Assumption.java new file mode 100644 index 00000000000..5f1d262fea7 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Assumption.java @@ -0,0 +1,58 @@ +package org.egov.processor.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import jakarta.validation.Valid; +import java.math.BigDecimal; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Digits; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Data; +import lombok.Builder; + +/** + * Assumption + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Assumption { + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("key") + @NotNull + @Size(min = 1, max = 256) + private String key = null; + + @JsonProperty("value") + @NotNull + @Valid + @DecimalMin(value = "0.01", inclusive = true, message = "The Assumption value must be greater than 0") + @DecimalMax(value = "9999999999.99", inclusive = true, message = "The assumption value must not exceed 10 digits in total, including up to 2 decimal places.") + @Digits(integer = 10, fraction = 2, message = "The Assumption value must have up to 10 digits and up to 2 decimal points") + private BigDecimal value = null; + + @JsonProperty("source") + @NotNull(message = "Source cannot be null. Please specify a valid source.") + private Source source = null; + + @JsonProperty("category") + @NotNull + @Size(min = 2, max = 64) + private String category = null; + + @JsonProperty("active") + @NotNull + private Boolean active = true; + +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Condition.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Condition.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Condition.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Condition.java index 51d3511c068..c63749d5bab 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Condition.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Condition.java @@ -3,11 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; /** * Condition diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/File.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/File.java similarity index 81% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/File.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/File.java index fe6b6c1abab..25a62e8ac83 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/File.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/File.java @@ -1,17 +1,20 @@ package org.egov.processor.web.models; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; +import org.apache.kafka.common.protocol.types.Field; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; + +import java.util.Arrays; /** * File @@ -30,7 +33,7 @@ public class File { @JsonProperty("filestoreId") @NotNull @Size(min = 1, max = 128) - @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Filestore Id must contain alphanumeric characters and may include some special characters") + @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Filestore Id must not contain only special characters") private String filestoreId = null; @JsonProperty("inputFileType") @@ -40,7 +43,7 @@ public class File { @JsonProperty("templateIdentifier") @NotNull @Size(min = 2, max = 128) - @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Name must contain alphanumeric characters and may include some special characters") + @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Name must not contain only special characters") private String templateIdentifier = null; @JsonProperty("active") @@ -71,12 +74,10 @@ public String toString() { @JsonCreator public static InputFileTypeEnum fromValue(String text) { - for (InputFileTypeEnum b : InputFileTypeEnum.values()) { - if (String.valueOf(b.value).equals(text)) { - return b; - } - } - return null; + return Arrays.stream(InputFileTypeEnum.values()) + .filter(b -> String.valueOf(b.value).equals(text)) + .findFirst() + .orElse(null); // Return null if no matching enum value is found } } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Locale.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Locale.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Locale.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Locale.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/LocaleResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/LocaleResponse.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/LocaleResponse.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/LocaleResponse.java diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/MetricDetail.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/MetricDetail.java new file mode 100644 index 00000000000..b21b1a62560 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/MetricDetail.java @@ -0,0 +1,75 @@ +package org.egov.processor.web.models; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Digits; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.Arrays; + +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MetricDetail { + + @JsonProperty("value") + @NotNull + @DecimalMin(value = "0.01", inclusive = true, message = "Metric value must be greater than 0") + @DecimalMax(value = "999.99", inclusive = true, message = "Metric value must be less than 1000") + @Digits(integer = 3, fraction = 2, message = "Metric value must have up to 3 digits and up to 2 decimal points") + private BigDecimal metricValue = null; + + @JsonProperty("comparator") + @NotNull + private MetricComparatorEnum metricComparator = null; + + @JsonProperty("unit") + @NotNull + @Size(min = 1, max = 128) + private String metricUnit = null; + + public enum MetricComparatorEnum { + GREATER_THAN(">"), + + LESS_THAN("<"), + + GREATER_THAN_OR_EQUAL_TO(">="), + + LESS_THAN_OR_EQUAL_TO("<="), + + EQUAL("="); + + private final String symbol; + + MetricComparatorEnum(String symbol) { + this.symbol = symbol; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(symbol); + } + + @JsonCreator + public static MetricComparatorEnum fromValue(String text) { + return Arrays.stream(MetricComparatorEnum.values()) + .filter(b -> String.valueOf(b.symbol).equals(text)) + .findFirst() + .orElse(null); // Return null if no matching enum value is found + } + } + +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Operation.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Operation.java similarity index 70% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Operation.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Operation.java index 25b5b189fa0..5194da66b18 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Operation.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Operation.java @@ -1,17 +1,19 @@ package org.egov.processor.web.models; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; +import jakarta.validation.Valid; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; + +import java.util.Arrays; /** * Operation @@ -46,6 +48,22 @@ public class Operation { @Size(min = 1, max = 64) private String output = null; + @JsonProperty("showOnEstimationDashboard") + @NotNull + private Boolean showOnEstimationDashboard = true; + + @JsonProperty("source") + @NotNull(message = "Source cannot be null. Please specify a valid source.") + private Source source = null; + + @JsonProperty("category") + @NotNull + @Size(min = 2, max = 64) + private String category = null; + + @JsonProperty("executionOrder") + private Integer executionOrder = null; + @JsonProperty("active") @NotNull private Boolean active = true; @@ -80,12 +98,10 @@ public String toString() { @JsonCreator public static OperatorEnum fromValue(String text) { - for (OperatorEnum b : OperatorEnum.values()) { - if (String.valueOf(b.value).equals(text)) { - return b; - } - } - return null; + return Arrays.stream(OperatorEnum.values()) + .filter(b -> String.valueOf(b.value).equals(text)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Unexpected value '" + text + "'")); } } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Plan.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Plan.java similarity index 63% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Plan.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Plan.java index a9d4f3139b5..8b2f1803f57 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Plan.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Plan.java @@ -1,17 +1,20 @@ package org.egov.processor.web.models; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; import org.springframework.validation.annotation.Validated; +import java.util.List; + /** * Plan */ @@ -34,30 +37,51 @@ public class Plan { @Size(min = 1, max = 64) private String locality = null; - @JsonProperty("executionPlanId") + @JsonProperty("campaignId") @Size(max = 64) - private String executionPlanId = null; + private String campaignId = null; @JsonProperty("planConfigurationId") @Size(max = 64) private String planConfigurationId = null; + @JsonProperty("status") + @Size(max = 64) + private String status = null; + + @JsonProperty("assignee") + @Size(max = 64) + private String assignee = null; + @JsonProperty("additionalDetails") private Object additionalDetails = null; @JsonProperty("activities") @Valid - private List activities = null; + private List activities; @JsonProperty("resources") @Valid - private List resources = null; + private List resources; @JsonProperty("targets") @Valid - private List targets = null; + private List targets; @JsonProperty("auditDetails") private AuditDetails auditDetails = null; + @JsonIgnore + private String boundaryAncestralPath = null; + + @JsonIgnore + private boolean isRequestFromResourceEstimationConsumer; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfiguration.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfiguration.java similarity index 68% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfiguration.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfiguration.java index e02d7e49b70..218dbaecbea 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfiguration.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfiguration.java @@ -1,14 +1,15 @@ package org.egov.processor.web.models; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotEmpty; import java.util.ArrayList; import java.util.List; + import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Pattern; import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; @@ -36,54 +37,43 @@ public class PlanConfiguration { @JsonProperty("name") @NotNull - @Size(min = 2, max = 128) - @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Name must not contain only special characters") + @Size(min = 3, max = 128) private String name = null; - @JsonProperty("executionPlanId") + @JsonProperty("campaignId") @NotNull @Size(min = 2, max = 64) - @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Execution Plan Id must not contain only special characters") - private String executionPlanId = null; + @Pattern(regexp = "^(?!\\p{Punct}+$).*$", message = "Campaign Id must not contain only special characters") + private String campaignId = null; @JsonProperty("status") - @NotNull - private StatusEnum status = null; + private String status = null; @JsonProperty("files") - @NotNull - @NotEmpty @Valid private List files = new ArrayList<>(); @JsonProperty("assumptions") - @NotNull - @NotEmpty @Valid private List assumptions = new ArrayList<>(); @JsonProperty("operations") - @NotNull - @NotEmpty @Valid private List operations = new ArrayList<>(); @JsonProperty("resourceMapping") - @NotNull - @NotEmpty @Valid private List resourceMapping = new ArrayList<>(); @JsonProperty("auditDetails") private @Valid AuditDetails auditDetails; - /** - * The status used in the Plan Configuration - */ - public enum StatusEnum { - DRAFT , - GENERATED, - INVALID_DATA - } + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + } diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationRequest.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationRequest.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationRequest.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationResponse.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationResponse.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationResponse.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchCriteria.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchCriteria.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchCriteria.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchCriteria.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchRequest.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchRequest.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanConfigurationSearchRequest.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanRequest.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/PlanRequest.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/PlanRequest.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Resource.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Resource.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java index 5fea454bd5d..693ba12ef32 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Resource.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java @@ -1,14 +1,14 @@ package org.egov.processor.web.models; import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import java.math.BigDecimal; +import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; /** * Resource diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/ResourceMapping.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/ResourceMapping.java similarity index 99% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/ResourceMapping.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/ResourceMapping.java index c53868d1033..ee9e1869c0c 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/ResourceMapping.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/ResourceMapping.java @@ -1,15 +1,16 @@ package org.egov.processor.web.models; import com.fasterxml.jackson.annotation.JsonProperty; + import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; /** * ResourceMapping diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Source.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Source.java new file mode 100644 index 00000000000..3d726b35f9f --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Source.java @@ -0,0 +1,5 @@ +package org.egov.processor.web.models; + +public enum Source { + MDMS, CUSTOM, VEHICLE; +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Target.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Target.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Target.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/Target.java index 8d7298939f6..6855fccb123 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/Target.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Target.java @@ -3,11 +3,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; import jakarta.validation.constraints.Size; +import org.springframework.validation.annotation.Validated; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; import lombok.NoArgsConstructor; -import org.springframework.validation.annotation.Validated; +import lombok.Data; +import lombok.Builder; /** * Target diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/boundary/BoundarySearchResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/boundary/BoundarySearchResponse.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/boundary/BoundarySearchResponse.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/boundary/BoundarySearchResponse.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/boundary/EnrichedBoundary.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/boundary/EnrichedBoundary.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/boundary/EnrichedBoundary.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/boundary/EnrichedBoundary.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/boundary/HierarchyRelation.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/boundary/HierarchyRelation.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/boundary/HierarchyRelation.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/boundary/HierarchyRelation.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/AdditionalDetails.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/AdditionalDetails.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/AdditionalDetails.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/AdditionalDetails.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Boundary.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Boundary.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Boundary.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Boundary.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Campaign.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Campaign.java similarity index 94% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Campaign.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Campaign.java index 352b29b693b..51c390a2af5 100644 --- a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Campaign.java +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Campaign.java @@ -1,12 +1,6 @@ package org.egov.processor.web.models.campaignManager; -import java.util.List; - -import org.egov.common.contract.models.AuditDetails; -import org.springframework.validation.annotation.Validated; - import com.fasterxml.jackson.annotation.JsonProperty; - import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -14,6 +8,10 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import java.util.List; @Validated @Data @@ -37,7 +35,13 @@ public class Campaign { @JsonProperty("action") @Size(min = 1, max = 64) private String action; - + + @JsonProperty("isActive") + private boolean isActive; + + @JsonProperty("parentId") + private String parentId; + @JsonProperty("campaignNumber") @Valid private String campaignNumber; diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignCondition.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignCondition.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignCondition.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignCondition.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignDetails.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignDetails.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignDetails.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignDetails.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignRequest.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignRequest.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignRequest.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResources.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResources.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResources.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResources.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResponse.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResponse.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignResponse.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignSearchRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignSearchRequest.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CampaignSearchRequest.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CampaignSearchRequest.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CycleConfigureDate.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CycleConfigureDate.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CycleConfigureDate.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CycleConfigureDate.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CycleData.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CycleData.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/CycleData.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/CycleData.java diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/DeliveryRule.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/DeliveryRule.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/DeliveryRule.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/DeliveryRule.java diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/MicroplanDetails.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/MicroplanDetails.java new file mode 100644 index 00000000000..4bbb44b118b --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/MicroplanDetails.java @@ -0,0 +1,32 @@ +package org.egov.processor.web.models.campaignManager; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Validated +public class MicroplanDetails { + + @JsonProperty("tenantId") + @NotNull + private String tenantId; + + @JsonProperty("campaignId") + @NotNull + private String campaignId; + + @JsonProperty("planConfigurationId") + @NotNull + private String planConfigurationId; + + @JsonProperty("resourceFilestoreId") + private String resourceFilestoreId; +} \ No newline at end of file diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/MicroplanDetailsRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/MicroplanDetailsRequest.java new file mode 100644 index 00000000000..4789ef42573 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/MicroplanDetailsRequest.java @@ -0,0 +1,26 @@ +package org.egov.processor.web.models.campaignManager; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MicroplanDetailsRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("MicroplanDetails") + @Valid + private MicroplanDetails microplanDetails = null; +} diff --git a/health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Product.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Product.java similarity index 100% rename from health-services/resource-estimation-service/src/main/java/org/egov/processor/web/models/campaignManager/Product.java rename to health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/Product.java diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/ResourceDetails.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/ResourceDetails.java new file mode 100644 index 00000000000..b35c0343469 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/ResourceDetails.java @@ -0,0 +1,48 @@ +package org.egov.processor.web.models.campaignManager; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResourceDetails { + + @JsonProperty("type") + @NotNull + @Size(min = 1, max = 64) + private String type; + + @JsonProperty("hierarchyType") + @NotNull + @Size(min = 1, max = 64) + private String hierarchyType; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId; + + @JsonProperty("fileStoreId") + @NotNull + private String fileStoreId; + + @JsonProperty("action") + @Size(min = 1, max = 64) + private String action; + + @JsonProperty("campaignId") + private String campaignId; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/ResourceDetailsRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/ResourceDetailsRequest.java new file mode 100644 index 00000000000..265d4fcdc0e --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/campaignManager/ResourceDetailsRequest.java @@ -0,0 +1,30 @@ +package org.egov.processor.web.models.campaignManager; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * CensusRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResourceDetailsRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("ResourceDetails") + @Valid + private ResourceDetails resourceDetails = null; + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/AdditionalField.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/AdditionalField.java new file mode 100644 index 00000000000..972ca77bb4b --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/AdditionalField.java @@ -0,0 +1,48 @@ +package org.egov.processor.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; + +/** + * AdditionalField + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AdditionalField { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("key") + @Valid + @NotNull + private String key = null; + + @JsonProperty("value") + @Valid + @NotNull + private BigDecimal value = null; + + @JsonProperty("showOnUi") + private Boolean showOnUi = Boolean.TRUE; + + @JsonProperty("editable") + private Boolean editable = Boolean.TRUE; + + @JsonProperty("order") + private Integer order = null; +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java new file mode 100644 index 00000000000..f895f6c75f1 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java @@ -0,0 +1,138 @@ +package org.egov.processor.web.models.census; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.Workflow; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; +import java.util.List; + + +/** + * Census + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Census { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("tenantId") + @NotNull + private String tenantId = null; + + @JsonProperty("hierarchyType") + @NotNull + private String hierarchyType = null; + + @JsonProperty("boundaryCode") + @NotNull + private String boundaryCode = null; + + @JsonProperty("assignee") + @Size(max = 64) + private String assignee = null; + + @JsonProperty("status") + @Size(max = 64) + private String status = null; + + @JsonProperty("type") + @NotNull + private TypeEnum type = null; + + @JsonProperty("totalPopulation") + @NotNull + private BigDecimal totalPopulation = null; + + @JsonProperty("populationByDemographics") + @Valid + private List populationByDemographics = null; + + @JsonProperty("effectiveFrom") + private Long effectiveFrom = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("source") + @NotNull + private String source = null; + + @JsonIgnore + private List boundaryAncestralPath = null; + + @JsonIgnore + private boolean partnerAssignmentValidationEnabled; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("workflow") + @Valid + private Workflow workflow; + + @JsonIgnore + private List assigneeJurisdiction; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("additionalFields") + @Valid + private List additionalFields = null; + + @JsonProperty("auditDetails") + private @Valid AuditDetails auditDetails; + + /** + * Gets or Sets type + */ + public enum TypeEnum { + PEOPLE("people"), + ANIMALS("animals"), + PLANTS("plants"), + OTHER("other"); + + private String value; + + TypeEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String text) { + for (TypeEnum b : TypeEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + } + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusRequest.java new file mode 100644 index 00000000000..08a9c3bdbce --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusRequest.java @@ -0,0 +1,31 @@ +package org.egov.processor.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * CensusRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("Census") + @Valid + private Census census = null; + + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusResponse.java new file mode 100644 index 00000000000..7e6c0b77440 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusResponse.java @@ -0,0 +1,41 @@ +package org.egov.processor.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +/** + * CensusResponse + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusResponse { + + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("Census") + @Valid + private List census = null; + + @JsonProperty("TotalCount") + @Valid + private Integer totalCount = null; + + @JsonProperty("StatusCount") + @Valid + private Map statusCount = null; + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusSearchCriteria.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusSearchCriteria.java new file mode 100644 index 00000000000..9ed59d11d63 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusSearchCriteria.java @@ -0,0 +1,71 @@ +package org.egov.processor.web.models.census; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * CensusSearchCriteria + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusSearchCriteria { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("ids") + private Set ids = null; + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + private String tenantId = null; + + @JsonProperty("areaCodes") + private List areaCodes = null; + + @JsonProperty("status") + private String status = null; + + @JsonProperty("assignee") + private String assignee = null; + + @JsonProperty("source") + private String source = null; + + @JsonProperty("facilityAssigned") + private Boolean facilityAssigned = null; + + @JsonProperty("jurisdiction") + private List jurisdiction = null; + + @JsonProperty("effectiveTo") + private Long effectiveTo = null; + + @JsonProperty("limit") + private Integer limit = null; + + @JsonProperty("offset") + private Integer offset = null; + + public CensusSearchCriteria addAreaCodesItem(String areaCodesItem) { + if (this.areaCodes == null) { + this.areaCodes = new ArrayList<>(); + } + this.areaCodes.add(areaCodesItem); + return this; + } + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusSearchRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusSearchRequest.java new file mode 100644 index 00000000000..ef7439bc368 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/CensusSearchRequest.java @@ -0,0 +1,31 @@ +package org.egov.processor.web.models.census; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; + +/** + * CensusSearchRequest + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CensusSearchRequest { + + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("CensusSearchCriteria") + @Valid + private CensusSearchCriteria censusSearchCriteria = null; + + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/PopulationByDemographic.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/PopulationByDemographic.java new file mode 100644 index 00000000000..52ab230781b --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/PopulationByDemographic.java @@ -0,0 +1,69 @@ +package org.egov.processor.web.models.census; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.validation.annotation.Validated; + +/** + * PopulationByDemographic + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PopulationByDemographic { + + @JsonProperty("id") + @Valid + @Size(min = 2, max = 64) + private String id = null; + + @JsonProperty("demographicVariable") + private DemographicVariableEnum demographicVariable = null; + + @JsonProperty("populationDistribution") + private Object populationDistribution = null; + + /** + * Gets or Sets demographicVariable + */ + public enum DemographicVariableEnum { + AGE("age"), + + GENDER("gender"), + + ETHNICITY("ethnicity"); + + private String value; + + DemographicVariableEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static DemographicVariableEnum fromValue(String text) { + for (DemographicVariableEnum b : DemographicVariableEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + } + +} diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/Mdms.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/Mdms.java new file mode 100644 index 00000000000..b591caf0929 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/Mdms.java @@ -0,0 +1,55 @@ +package org.egov.processor.web.models.mdmsV2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * Mdms + */ +@Validated +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Mdms { + + @JsonProperty("id") + @Size(min = 2, max = 64) + private String id; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 128) + private String tenantId = null; + + @JsonProperty("schemaCode") + @NotNull + @Size(min = 2, max = 128) + private String schemaCode = null; + + @JsonProperty("uniqueIdentifier") + @Size(min = 2, max = 128) + private String uniqueIdentifier = null; + + @JsonProperty("data") + @NotNull + private JsonNode data = null; + + @JsonProperty("isActive") + private Boolean isActive = true; + + @JsonProperty("auditDetails") + @Valid + private AuditDetails auditDetails = null; + +} \ No newline at end of file diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsCriteriaReqV2.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsCriteriaReqV2.java new file mode 100644 index 00000000000..65c7123d5ca --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsCriteriaReqV2.java @@ -0,0 +1,26 @@ +package org.egov.processor.web.models.mdmsV2; + +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.request.RequestInfo; + +import javax.validation.Valid; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class MdmsCriteriaReqV2 { + @JsonProperty("RequestInfo") + @Valid + private RequestInfo requestInfo; + + @JsonProperty("MdmsCriteria") + @Valid + private MdmsCriteriaV2 mdmsCriteriaV2; +} \ No newline at end of file diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsCriteriaV2.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsCriteriaV2.java new file mode 100644 index 00000000000..c7832894ebc --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsCriteriaV2.java @@ -0,0 +1,62 @@ +package org.egov.processor.web.models.mdmsV2; + +import com.fasterxml.jackson.annotation.JsonIgnore; +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.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Data +@Validated +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +public class MdmsCriteriaV2 { + + @JsonProperty("tenantId") + @Size(min = 1, max = 100) + @NotNull + private String tenantId; + + @JsonProperty("ids") + private Set ids; + + @JsonProperty("uniqueIdentifier") + @Size(min = 1, max = 64) + private String uniqueIdentifier; + + @JsonProperty("uniqueIdentifiers") + private List uniqueIdentifiers; + + @JsonProperty("schemaCode") + private String schemaCode; + + @JsonProperty("filters") + private Map filterMap; + + @JsonProperty("isActive") + private Boolean isActive; + + @JsonIgnore + private Map schemaCodeFilterMap; + + @JsonIgnore + private Set uniqueIdentifiersForRefVerification; + + @JsonProperty("offset") + private Integer offset; + + @JsonProperty("limit") + private Integer limit; + +} \ No newline at end of file diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsResponseV2.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsResponseV2.java new file mode 100644 index 00000000000..2b570e66f08 --- /dev/null +++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MdmsResponseV2.java @@ -0,0 +1,28 @@ +package org.egov.processor.web.models.mdmsV2; + +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 javax.validation.Valid; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) + +public class MdmsResponseV2 { + @JsonProperty("ResponseInfo") + @Valid + private ResponseInfo responseInfo = null; + + @JsonProperty("mdms") + @Valid + private List mdms = null; +} \ No newline at end of file diff --git a/health-services/resource-estimation-service/src/main/resources/application.properties b/health-services/resource-generator/src/main/resources/application.properties similarity index 68% rename from health-services/resource-estimation-service/src/main/resources/application.properties rename to health-services/resource-generator/src/main/resources/application.properties index 7cdfe225f6d..45e36a74e5c 100644 --- a/health-services/resource-estimation-service/src/main/resources/application.properties +++ b/health-services/resource-generator/src/main/resources/application.properties @@ -1,5 +1,5 @@ -server.contextPath=/resource-estimation-service -server.servlet.context-path=/resource-estimation-service +server.contextPath=/resource-generator +server.servlet.context-path=/resource-generator server.port=8083 app.timezone=UTC @@ -47,6 +47,7 @@ kafka.producer.config.buffer_memory_config=33554432 #mdms urls egov.mdms.host=https://unified-dev.digit.org egov.mdms.search.endpoint=/egov-mdms-service/v1/_search +egov.mdms.search.v2.endpoint=/mdms-v2/v2/_search #plan config egov.plan.config.host=https://unified-dev.digit.org @@ -65,19 +66,43 @@ plan.config.consumer.kafka.update.topic=plan-config-update-topic #Plan Create egov.plan.create.endpoint=/plan-service/plan/_create +egov.plan.search.endpoint=/plan-service/plan/_search #Campaign Manager egov.project.factory.search.endpoint=/project-factory/v1/project-type/search egov.project.factory.update.endpoint=/project-factory/v1/project-type/update +egov.project.factory.data.create.endpoint=/project-factory/v1/data/_create +egov.project.factory.fetch.from.microplan.endpoint=/project-factory/v1/project-type/fetch-from-microplan egov.project.factory.host=https://unified-dev.digit.org #egov.project.factory.host=http://localhost:8090 +integrate.with.admin.console=false + +#Kafka topics for creating or updating records in dependent microservices resource.microplan.create.topic=resource-microplan-create-topic resource.update.plan.config.consumer.topic=resource-plan-config-update-topic -integrate.with.admin.console=true +resource.census.create.topic=resource-census-create-topic egov.boundary.service.host=https://unified-dev.digit.org #egov.boundary.service.host=http://localhost:8091 egov.boundary.relationship.search.endpoint=/boundary-service/boundary-relationships/_search?includeChildren=true&tenantId={tenantId}&hierarchyType={hierarchyType} egov.locale.service.host=https://unified-qa.digit.org -egov.locale.search.endpoint=/localization/messages/v1/_search?module={module}&locale={locale}&tenantId={tenantId} \ No newline at end of file +egov.locale.search.endpoint=/localization/messages/v1/_search?module={module}&locale={locale}&tenantId={tenantId} + +#trigger statuses +plan.config.trigger.plan.estimates.status=RESOURCE_ESTIMATION_IN_PROGRESS +plan.config.trigger.census.records.status=EXECUTION_TO_BE_DONE +plan.config.trigger.plan.facility.mappings.status=EXECUTION_TO_BE_DONE +plan.config.update.plan.estimates.into.output.file.status=RESOURCE_ESTIMATIONS_APPROVED + +# Pagination config +resource.default.offset=0 +resource.default.limit=10 + +# Census +egov.census.host=https://unified-dev.digit.org +egov.census.search.endpoint=/census-service/_search + +census.additional.field.override.keys=HCM_ADMIN_CONSOLE_BOUNDARY_CODE +census.additional.field.prefix.append.keys=HCM_ADMIN_CONSOLE_TOTAL_POPULATION,HCM_ADMIN_CONSOLE_TARGET_POPULATION,HCM_ADMIN_CONSOLE_TARGET_POPULATION_AGE_3TO11,HCM_ADMIN_CONSOLE_TARGET_POPULATION_AGE_12TO59 +census.additional.field.show.on.ui.false.keys=HCM_ADMIN_CONSOLE_TARGET_LAT_OPT,HCM_ADMIN_CONSOLE_TARGET_LONG_OPT \ No newline at end of file diff --git a/health-services/resource-estimation-service/src/main/resources/db/Dockerfile b/health-services/resource-generator/src/main/resources/db/Dockerfile similarity index 83% rename from health-services/resource-estimation-service/src/main/resources/db/Dockerfile rename to health-services/resource-generator/src/main/resources/db/Dockerfile index 60fc07ce69f..f5241a8f861 100644 --- a/health-services/resource-estimation-service/src/main/resources/db/Dockerfile +++ b/health-services/resource-generator/src/main/resources/db/Dockerfile @@ -1,4 +1,4 @@ -FROM egovio/flyway:4.1.2 +FROM egovio/flyway:10.7.1 COPY ./migration/main /flyway/sql diff --git a/health-services/resource-generator/src/main/resources/db/migrate.sh b/health-services/resource-generator/src/main/resources/db/migrate.sh new file mode 100644 index 00000000000..c58d6f91e3f --- /dev/null +++ b/health-services/resource-generator/src/main/resources/db/migrate.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true migrate \ No newline at end of file