diff --git a/health-services/muster-roll/CHANGELOG.md b/health-services/muster-roll/CHANGELOG.md new file mode 100644 index 0000000000..e5a50979d1 --- /dev/null +++ b/health-services/muster-roll/CHANGELOG.md @@ -0,0 +1,6 @@ + +All notable changes to this module will be documented in this file. + +## 0.1.0 - 2023-04-17 + +- Base version \ No newline at end of file diff --git a/health-services/muster-roll/LOCALSETUP.md b/health-services/muster-roll/LOCALSETUP.md new file mode 100644 index 0000000000..56cd272bd2 --- /dev/null +++ b/health-services/muster-roll/LOCALSETUP.md @@ -0,0 +1,42 @@ +# Local Setup + +To set up the muster roll service in your local system, clone the git repo(https://github.com/egovernments/DIGIT-Works). + +## Dependencies + +- MDMS +- IDGen +- Workflow service +- Individual + + +### Infra Dependency + +- [X] Postgres DB +- [ ] Redis +- [ ] Elasticsearch +- [X] Kafka + +## Running Locally + +To run the service locally, you need to port forward below services. + +```bash +function kgpt(){kubectl get pods -n egov --selector=app=$1 --no-headers=true | head -n1 | awk '{print $1}'} + +kubectl port-forward -n egov $(kgpt egov-user) 8085:8080 +kubectl port-forward -n egov $(kgpt egov-idgen) 8086:8080 +kubectl port-forward -n egov $(kgpt egov-mdms-service) 8087:8080 +kubectl port-forward -n egov $(kgpt egov-workflow) 8088:8080 +kubectl port-forward -n works $(kgpt project-management-service) 8089:8080 +``` + +Update below listed properties in `application.properties` before running the project: + +```ini +egov.idgen.hostname = http://127.0.0.1:8086 + +# can use non port forwarded environment host as well +egov.mdms.host = http://127.0.0.1:8087 +egov.workflow.host = http://127.0.0.1:8088 +``` diff --git a/health-services/muster-roll/Muster-Roll-Service-1.0.0.yaml b/health-services/muster-roll/Muster-Roll-Service-1.0.0.yaml new file mode 100644 index 0000000000..4dbfff8c37 --- /dev/null +++ b/health-services/muster-roll/Muster-Roll-Service-1.0.0.yaml @@ -0,0 +1,285 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Muster Roll Service + description: '' + +paths: + /muster-roll/v1/_estimate: + post: + tags: + - Muster Roll + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollRequest' + responses: + '202': + description: 'Muster Roll created' + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollResponse' + + /muster-roll/v1/_create: + post: + tags: + - Muster Roll + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollRequest' + responses: + '202': + description: 'Muster Roll created' + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollResponse' + + /muster-roll/v1/_update: + post: + tags: + - Muster Roll + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollRequest' + responses: + '202': + description: 'Muster Roll updated' + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollResponse' + + /muster-roll/v1/_search: + post: + tags: + - Muster Roll + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RequestInfoWrapper' + parameters: + - $ref: 'https://raw.githubusercontent.com/egovernments/DIGIT-OSS/master/core-services/docs/common-contract.yml#/components/parameters/tenantId' + - name: ids + description: Ids of the muster roll + in: query + schema: + type: array + items: + type: string + - name: musterRollNumber + description: (Custom-formatted) Muster roll number + in: query + schema: + type: string + - name: registerId + description: Register Id + in: query + schema: + type: string + - name: fromDate + description: Return registers with any overlap in the given time period + in: query + schema: + type: number + - name: toDate + description: Return registers with any overlap in the given time period + in: query + schema: + type: number + - name: status + description: Status of the muster roll. This can't be the only query param. It should be paired with some other search param. + in: query + schema: + type: string + - name: musterRollStatus + description: Workflow Status of the muster roll. This can't be the only query param. It should be paired with some other search param. + in: query + schema: + type: string + - name: referenceId + description: Id of the entity to which register is associated. Example ContractId + in: query + schema: + type: string + - name: serviceCode + description: Service to which register is associated. + in: query + schema: + type: string + responses: + '200': + description: 'Search results' + content: + application/json: + schema: + $ref: '#/components/schemas/MusterRollResponse' + +components: + schemas: + MusterRoll: + type: object + properties: + id: + type: string + format: uuid + description: Unique identifer of the roll + readOnly: true + tenantId: + type: string + example: pb.amritsar + musterRollNumber: + type: string + description: Custom-formatted system generated unique identifier + readOnly: true + registerId: + type: string + description: Reference to the register id upon which the muster roll is being generated. + status: + type: string + enum: + - ACTIVE + - INACTIVE + - CANCELLED + musterRollStatus: + type: string + description: It will store the state of the workflow. + example: CREATED + startDate: + type: number + format: timestamp + example: 1665497225000 + description: Timestamp of the start date in milliseconds. In case of weekly registers, there will be additional checks for the starting date like muster roll start on every Monday. + endDate: + type: number + format: timestamp + example: 1665497271000 + description: Timestamp of the end date in milliseconds. In case of mandatory weekly muster rolls, it is set automatically. + referenceId: + type: string + example: idGen generated value + description: Id of the entity to which register is associated. Example ContractId + maxLength: 256 + serviceCode: + type: string + example: WORKS-CONTRACT + description: Service to which register is associated. + maxLength: 64 + individualEntries: + type: array + items: + $ref: '#/components/schemas/IndividualEntry' + additionalDetails: + type: object + description: This is an updatable field from the user's input. + required: + - tenantId + - registerId + - startDate + + IndividualEntry: + type: object + readOnly: true + properties: + Id: + type: string + description: Id of the IndividualEntry + individualId: + type: string + description: Reference of the attendee from the Individual Registry. + actualTotalAttendance: + type: number + readOnly: true + description: It is the computed sum of attendance in the given time period of the muster roll. This will be stored in the muster roll service's db. It will be computed only when the muster is created. So the computation will happen with CREATE actions only. + modifiedTotalAttendance: + type: number + readOnly: true + description: It is the modified sum of attendance in the given time period of the muster roll. This will be stored in the muster roll service's db. It will be stored when the totalAttendance is edited. So the updation will happen with EDIT actions only. + attendanceEntries: + type: array + readOnly: true + items: + $ref: '#/components/schemas/AttendanceEntry' + additionalDetails: + type: object + description: This can be used to store skillset that is a requirement for Mukta. This is an updatable field from the user's input. + + AttendanceEntry: + type: object + readOnly: true + description: This computed data will also be stored as part of the muster roll db. + properties: + Id: + type: string + description: Id of the AttendanceEntry + time: + type: number + readOnly: true + format: timestamp + description: The timestamp for a given time period against which the attendance is marked. + attendance: + type: number + example: 0 + description: It is the computed attendance value based on the entry and exit events fetched from the Attendance service. The muster roll service will still allow the user to override the value of this attendance. + additionalDetails: + type: object + description: This is an updatable field from the user's input. + + AttendanceLog: + $ref: 'https://github.com/egovernments/DIGIT-Works/blob/develop/backend/attendance-service/Attendance-Service-1.0.0.yaml#/components/schemas/AttendanceLog' + + AttendanceLogResponse: + $ref: 'https://github.com/egovernments/DIGIT-Works/blob/develop/backend/attendance-service/Attendance-Service-1.0.0.yaml#/components/schemas/AttendanceLogResponse' + + AttendanceRegister: + $ref: 'https://github.com/egovernments/DIGIT-Works/blob/develop/backend/attendance-service/Attendance-Service-1.0.0.yaml#/components/schemas/AttendanceRegister' + + AttendanceRegisterResponse: + $ref: 'https://github.com/egovernments/DIGIT-Works/blob/develop/backend/attendance-service/Attendance-Service-1.0.0.yaml#/components/schemas/AttendanceRegisterResponse' + + MusterRollRequest: + type: object + properties: + requestInfo: + $ref: 'https://raw.githubusercontent.com/egovernments/DIGIT-OSS/master/core-services/docs/common-contract.yml#/components/schemas/RequestHeader' + musterRoll: + $ref: '#/components/schemas/MusterRoll' + workflow: + type: object + properties: + action: + type: string + description: 'Action of the workflow to be performned on the request' + comment: + type: string + description: 'comment for the workflow action to be performed' + assignees: + type: array + items: + type: string + description: ' uuid of the users in the system to assign workflow to the specific user intead of a all the users with the gien role.' + required: + - action + + MusterRollResponse: + type: object + properties: + responseInfo: + $ref: 'https://raw.githubusercontent.com/egovernments/DIGIT-OSS/master/core-services/docs/common-contract.yml#/components/schemas/ResponseHeader' + musterRolls: + type: array + items: + $ref: '#/components/schemas/MusterRoll' + + RequestInfoWrapper: + type: object + properties: + requestInfo: + $ref: 'https://raw.githubusercontent.com/egovernments/DIGIT-OSS/master/core-services/docs/common-contract.yml#/components/schemas/RequestHeader' diff --git a/health-services/muster-roll/README.md b/health-services/muster-roll/README.md new file mode 100644 index 0000000000..2af45c966b --- /dev/null +++ b/health-services/muster-roll/README.md @@ -0,0 +1,27 @@ +# Muster Roll Service + +This is a calculator service dependent on the Attendance service. Attendance logs basic attendance. Muster computes aggregates based on the attendance and business logic. Muster roll will have to be customised depending on the implementation. + +### Service Dependencies + +- DIGIT backbone services +- Idgen +- Persister +- Indexer +- Workflow +- User +- Attendance + +## Service Details +- Estimate wages of a wage seeker based on his/her attendance logs +- Create a muster roll with a list of wage seekers +- Update a muster roll with modified aggregate attendance +- Search for a muster roll based on some defined parameters. For more info, please see Swagger contract. + +### API Specs +https://github.com/egovernments/DIGIT-Specs/blob/master/Domain%20Services/Works/Muster-Roll-Service-v1.0.0.yaml + +### Postman Collection +https://raw.githubusercontent.com/egovernments/DIGIT-Works/master/backend/muster-roll/src/main/resources/Muster%20Roll%20Service.postman_collection.json + + diff --git a/health-services/muster-roll/docs/db-diagram.txt b/health-services/muster-roll/docs/db-diagram.txt new file mode 100644 index 0000000000..eebb831ae9 --- /dev/null +++ b/health-services/muster-roll/docs/db-diagram.txt @@ -0,0 +1,49 @@ +Table muster_roll { + id varchar(256) [not null, pk] + tenant_id varchar(64) [not null] + musterroll_number varchar(128) [not null] + attendance_register_id varchar(256) [not null] + start_date bigint [not null] + end_date bigint [not null] + musterroll_status varchar(64) [not null] + status varchar(64) [not null] + additionaldetails jsonb + createdby varchar(256) [not null] + lastmodifiedby varchar(256) + createdtime bigint + lastmodifiedtime bigint + reference_id varchar(256) + service_code varchar(64) +} + +Table eg_wms_attendance_summary { + id varchar(256) [not null, pk] + individual_id varchar(256) [not null] + muster_roll_id varchar(256) [not null] + musterroll_number varchar(128) [not null] + actual_total_attendance numeric + additionaldetails jsonb + createdby varchar(256) [not null] + lastmodifiedby varchar(256) + createdtime bigint + lastmodifiedtime bigint + modified_total_attendance numeric +} + +Table eg_wms_attendance_entries { + id varchar(256) [not null, pk] + attendance_summary_id varchar(256) [not null] + individual_id varchar(256) [not null] + musterroll_number varchar(128) [not null] + date_of_attendance bigint + attendance_value numeric + additionaldetails jsonb + createdby varchar(256) [not null] + lastmodifiedby varchar(256) + createdtime bigint + lastmodifiedtime bigint +} + +Ref: "muster_roll"."id" < "eg_wms_attendance_summary"."muster_roll_id" + +Ref: "eg_wms_attendance_summary"."id" < "eg_wms_attendance_entries"."attendance_summary_id" \ No newline at end of file diff --git a/health-services/muster-roll/pom.xml b/health-services/muster-roll/pom.xml new file mode 100644 index 0000000000..5955f073a6 --- /dev/null +++ b/health-services/muster-roll/pom.xml @@ -0,0 +1,136 @@ + + 4.0.0 + org.egov + muster-roll + jar + muster-roll + 1.0.1 + + 17 + ${java.version} + ${java.version} + 42.4.1 + 2.17.1 + 1.18.22 + + + 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-validation + 3.2.3 + + + org.springframework.boot + spring-boot-starter-web + + + 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 + + + net.minidev + json-smart + 2.5.0 + + + io.swagger + swagger-core + 1.5.18 + + + + org.egov.services + tracer + 2.9.0-SNAPSHOT + + + org.egov + mdms-client + 2.9.0-SNAPSHOT + + + org.egov.services + digit-models + 1.0.0-SNAPSHOT + + + org.projectlombok + lombok + true + + + org.egov.works + works-services-common + 1.0.0-SNAPSHOT + + + org.egov.common + health-services-models + 1.0.12-SNAPSHOT + compile + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + + 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/muster-roll/src/main/java/org/egov/MusterRollMain.java b/health-services/muster-roll/src/main/java/org/egov/MusterRollMain.java new file mode 100644 index 0000000000..39a6e8d4f1 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/MusterRollMain.java @@ -0,0 +1,20 @@ +package org.egov; + + +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 = {"org.egov", "org.egov.web.controllers", "org.egov.config"}) +public class MusterRollMain { + + + public static void main(String[] args) { + SpringApplication.run(MusterRollMain.class, args); + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/config/MainConfiguration.java b/health-services/muster-roll/src/main/java/org/egov/config/MainConfiguration.java new file mode 100644 index 0000000000..438b68f904 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/config/MainConfiguration.java @@ -0,0 +1,39 @@ +package org.egov.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +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 jakarta.annotation.PostConstruct; +import java.util.TimeZone; + + +@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/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java b/health-services/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java new file mode 100644 index 0000000000..1c84630a17 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java @@ -0,0 +1,135 @@ +package org.egov.config; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +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 jakarta.annotation.PostConstruct; +import java.util.TimeZone; + +@Component +@Data +@Import({TracerConfiguration.class}) +@NoArgsConstructor +@AllArgsConstructor +public class MusterRollServiceConfiguration { + + @Value("${app.timezone}") + private String timeZone; + + //MDMS + @Value("${egov.mdms.host}") + private String mdmsHost; + @Value("${egov.mdms.search.endpoint}") + private String mdmsEndPoint; + @Value("${egov.mdms.v2.host}") + private String mdmsV2Host; + @Value("${egov.mdms.v2.search.endpoint}") + private String mdmsV2EndPoint; + + //Idgen Config + @Value("${egov.idgen.host}") + private String idGenHost; + @Value("${egov.idgen.path}") + private String idGenPath; + + //Idgen name + @Value("${egov.idgen.musterroll.number.name}") + private String idgenMusterRollNumberName; + + //Workflow config + @Value("${musterroll.workflow.module.name}") + private String musterRollWFModuleName; + @Value("${musterroll.workflow.business.service}") + private String musterRollWFBusinessService; + @Value("${egov.workflow.host}") + private String wfHost; + @Value("${egov.workflow.transition.path}") + private String wfTransitionPath; + @Value("${egov.workflow.businessservice.search.path}") + private String wfBusinessServiceSearchPath; + @Value("${egov.workflow.processinstance.search.path}") + private String wfProcessInstanceSearchPath; + + //Topic + @Value("${musterroll.kafka.create.topic}") + private String saveMusterRollTopic; + @Value("${musterroll.kafka.update.topic}") + private String updateMusterRollTopic; + @Value("${musterroll.kafka.calculate.topic}") + private String calculateMusterRollTopic; + + //search config + @Value("${musterroll.default.offset}") + private Integer musterDefaultOffset; + @Value("${musterroll.default.limit}") + private Integer musterDefaultLimit; + @Value("${musterroll.search.max.limit}") + private Integer musterMaxLimit; + @Value("${muster.restricted.search.roles}") + private String restrictedSearchRoles; + + //Attendance service + @Value("${works.attendance.log.host}") + private String attendanceLogHost; + @Value("${works.attendance.log.search.endpoint}") + private String attendanceLogEndpoint; + @Value("${works.attendance.register.search.endpoint}") + private String attendanceRegisterEndpoint; + @Value("${works.attendance.register.search.limit}") + private String attendanceRegisterSearchLimit; + + //Contract Service + @Value("${works.contract.host}") + private String contractServiceHost; + @Value("${works.contract.endpoint}") + private String contractServiceEndpoint; + + //Organisation Service + @Value("${works.organisation.host}") + private String organisationServiceHost; + @Value("${works.organisation.endpoint}") + private String organisationServiceEndpoint; + + //Localization Service + @Value("${egov.localization.host}") + private String localizationServiceHost; + @Value("${egov.localization.search.endpoint}") + private String localizationServiceEndpoint; + + //Notification Topic + @Value("${kafka.topics.notification.sms}") + private String smsNotificationTopic; + + //Expense Service + @Value("${works.expense.calculator.host}") + private String expenseCalculatorServiceHost; + @Value("${works.expense.calculator.endpoint}") + private String expenseCalculatorServiceEndpoint; + + //Individual service + @Value("${works.individual.host}") + private String individualHost; + @Value("${works.individual.search.endpoint}") + private String individualSearchEndpoint; + + //Bankaccounts service + @Value("${works.bankaccounts.host}") + private String bankaccountsHost; + @Value("${works.bankaccounts.search.endpoint}") + private String bankaccountsSearchEndpoint; + + //contract service code + @Value("${works.contract.service.code}") + private String contractServiceCode; + + @PostConstruct + public void initialize() { + TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/kafka/MusterRollProducer.java b/health-services/muster-roll/src/main/java/org/egov/kafka/MusterRollProducer.java new file mode 100644 index 0000000000..2e8a8d8f37 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/kafka/MusterRollProducer.java @@ -0,0 +1,24 @@ +package org.egov.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 MusterRollProducer { + + private final CustomKafkaTemplate kafkaTemplate; + + @Autowired + public MusterRollProducer(CustomKafkaTemplate kafkaTemplate) { + this.kafkaTemplate = kafkaTemplate; + } + + public void push(String topic, Object value) { + kafkaTemplate.send(topic, value); + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/repository/IdGenRepository.java b/health-services/muster-roll/src/main/java/org/egov/repository/IdGenRepository.java new file mode 100644 index 0000000000..e98029a612 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/repository/IdGenRepository.java @@ -0,0 +1,66 @@ +package org.egov.repository; + +import org.egov.common.contract.idgen.IdGenerationRequest; +import org.egov.common.contract.idgen.IdGenerationResponse; +import org.egov.common.contract.idgen.IdRequest; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +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; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Repository +public class IdGenRepository { + + + private final RestTemplate restTemplate; + + private final MusterRollServiceConfiguration config; + + @Autowired + public IdGenRepository(RestTemplate restTemplate, MusterRollServiceConfiguration config) { + this.restTemplate = restTemplate; + this.config = config; + } + + + /** + * Call iDgen to generateIds + * + * @param requestInfo The rquestInfo of the request + * @param tenantId The tenantiD of the service request + * @param name Name of the foramt + * @param format Format of the ids + * @param count Total Number of idGen ids required + * @return + */ + public IdGenerationResponse getId(RequestInfo requestInfo, String tenantId, String name, String format, int count) { + + List reqList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + reqList.add(IdRequest.builder().idName(name).format(format).tenantId(tenantId).build()); + } + IdGenerationRequest req = IdGenerationRequest.builder().idRequests(reqList).requestInfo(requestInfo).build(); + IdGenerationResponse response = null; + try { + response = restTemplate.postForObject(config.getIdGenHost() + config.getIdGenPath(), req, IdGenerationResponse.class); + } catch (HttpClientErrorException e) { + throw new ServiceCallException(e.getResponseBodyAsString()); + } catch (Exception e) { + Map map = new HashMap<>(); + map.put(e.getCause().getClass().getName(), e.getMessage()); + throw new CustomException(map); + } + return response; + } + + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/repository/MusterRollRepository.java b/health-services/muster-roll/src/main/java/org/egov/repository/MusterRollRepository.java new file mode 100644 index 0000000000..795e1599cd --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/repository/MusterRollRepository.java @@ -0,0 +1,52 @@ +package org.egov.repository; + +import org.egov.repository.querybuilder.MusterRollQueryBuilder; +import org.egov.repository.rowmapper.MusterRollRowMapper; +import org.egov.web.models.MusterRoll; +import org.egov.web.models.MusterRollSearchCriteria; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@Repository +public class MusterRollRepository { + + private final MusterRollRowMapper rowMapper; + + private final MusterRollQueryBuilder queryBuilder; + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public MusterRollRepository(MusterRollRowMapper rowMapper, MusterRollQueryBuilder queryBuilder, JdbcTemplate jdbcTemplate) { + this.rowMapper = rowMapper; + this.queryBuilder = queryBuilder; + this.jdbcTemplate = jdbcTemplate; + } + + + /** + * Fetch the record from DB based on the search criteria + * @param searchCriteria + * @return + */ + public List getMusterRoll(MusterRollSearchCriteria searchCriteria,List registerIds) { + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getMusterSearchQuery(searchCriteria, preparedStmtList, registerIds,false); + return jdbcTemplate.query(query, rowMapper, preparedStmtList.toArray()); + } + + /** + * Fetch the record count from DB based on the search criteria + * @param searchCriteria + * @return + */ + public Integer getMusterRollCount(MusterRollSearchCriteria searchCriteria,List registerIds) { + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getSearchCountQueryString(searchCriteria, preparedStmtList, registerIds); + return jdbcTemplate.queryForObject(query, preparedStmtList.toArray(), Integer.class); + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/repository/ServiceRequestRepository.java b/health-services/muster-roll/src/main/java/org/egov/repository/ServiceRequestRepository.java new file mode 100644 index 0000000000..400b0b825b --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/repository/ServiceRequestRepository.java @@ -0,0 +1,45 @@ +package org.egov.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; + +@Repository +@Slf4j +public class ServiceRequestRepository { + + private ObjectMapper mapper; + + private RestTemplate restTemplate; + + + @Autowired + 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 threw an Exception: ", e); + throw new ServiceCallException(e.getResponseBodyAsString()); + } catch (Exception e) { + log.error("Exception while fetching from searcher: ", e); + } + + return response; + } +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/java/org/egov/repository/querybuilder/MusterRollQueryBuilder.java b/health-services/muster-roll/src/main/java/org/egov/repository/querybuilder/MusterRollQueryBuilder.java new file mode 100644 index 0000000000..6a1058a3c8 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/repository/querybuilder/MusterRollQueryBuilder.java @@ -0,0 +1,191 @@ +package org.egov.repository.querybuilder; + +import org.apache.commons.lang3.StringUtils; +import org.egov.util.MusterRollServiceUtil; +import org.egov.web.models.MusterRollSearchCriteria; +import org.egov.works.services.common.models.expense.Pagination; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; + +@Component +public class MusterRollQueryBuilder { + + private final MusterRollServiceUtil musterRollServiceUtil; + + + private static final String FETCH_MUSTER_ROLL_QUERY = "SELECT muster.id,muster.tenant_id,muster.musterroll_number,muster.attendance_register_id,muster.status,muster.musterroll_status,muster.start_date,muster.end_date,muster.createdby,muster.lastmodifiedby,muster.createdtime,muster.lastmodifiedtime,muster.additionaldetails,muster.reference_id,muster.service_code,"+ + "ind.id AS summaryId,ind.muster_roll_id AS indMusterId,ind.individual_id AS IndividualId,ind.actual_total_attendance AS actualTotalAttendance,ind.additionaldetails AS indAddlDetails,ind.createdby AS indCreatedBy,ind.lastmodifiedby AS indModifiedBy,ind.createdtime AS indCreatedTime,ind.lastmodifiedtime AS indModifiedTime,ind.modified_total_attendance AS modifiedTotalAttendance,"+ + "attn.id AS attendanceId,attn.attendance_summary_id AS attnSummaryId,attn.date_of_attendance AS AttnDate,attn.attendance_value AS attendance,attn.additionaldetails AS attnAddlDetails,attn.createdby AS attnCreatedBy,attn.lastmodifiedby AS attnModifiedBy,attn.createdtime AS attnCreatedTime,attn.lastmodifiedtime AS attnModifiedTime "+ + "FROM eg_wms_muster_roll AS muster " + + "LEFT JOIN " + + "eg_wms_attendance_summary AS ind " + + "ON (muster.id=ind.muster_roll_id) " + + "LEFT JOIN " + + "eg_wms_attendance_entries AS attn " + + "ON (attn.attendance_summary_id=ind.id) "; + + private static final String MUSTER_ROLL_COUNT_QUERY = "SELECT distinct(muster.id) " + + "FROM eg_wms_muster_roll AS muster "; + + private static final String PAGINATION_WRAPPER = "SELECT * FROM " + + "(SELECT *, DENSE_RANK() OVER (ORDER BY {sortBy} {orderBy}) offset_ FROM " + + "({})" + + " result) result_offset " + + "WHERE offset_ > ? AND offset_ <= ?"; + + private static final String COUNT_WRAPPER = " SELECT COUNT(*) FROM ({INTERNAL_QUERY}) AS count "; + + @Autowired + public MusterRollQueryBuilder(MusterRollServiceUtil musterRollServiceUtil) { + this.musterRollServiceUtil = musterRollServiceUtil; + } + + + public String getMusterSearchQuery(MusterRollSearchCriteria searchCriteria, List preparedStmtList, List registerIds, boolean isCountRequired) { + StringBuilder queryBuilder = prepareSearchQuery(searchCriteria, preparedStmtList, registerIds, isCountRequired); + + //if the search is only based on tenantId and also registerIds is not part of search, add limit and offset at query level + //tenant id based search by JE or ME + if ((registerIds == null || registerIds.isEmpty()) && musterRollServiceUtil.isTenantBasedSearch(searchCriteria) && Boolean.FALSE.equals(isCountRequired)) { + return addLimitAndOffset(queryBuilder, searchCriteria, preparedStmtList); + } + + return queryBuilder.toString(); + } + private StringBuilder prepareSearchQuery(MusterRollSearchCriteria searchCriteria, List preparedStmtList, List registerIds, boolean isCountRequired) { + StringBuilder queryBuilder = new StringBuilder(FETCH_MUSTER_ROLL_QUERY); + + if (isCountRequired) { + queryBuilder = new StringBuilder(MUSTER_ROLL_COUNT_QUERY); + } + List ids = searchCriteria.getIds(); + if (ids != null && !ids.isEmpty()) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.id IN (").append(createQuery(ids)).append(")"); + addToPreparedStatement(preparedStmtList, ids); + } + + if (StringUtils.isNotBlank(searchCriteria.getTenantId())) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.tenant_id LIKE ? "); + preparedStmtList.add(searchCriteria.getTenantId()+"%"); + } + + if (StringUtils.isNotBlank(searchCriteria.getMusterRollNumber())) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.musterroll_number=? "); + preparedStmtList.add(searchCriteria.getMusterRollNumber()); + } + + if (StringUtils.isNotBlank(searchCriteria.getRegisterId())) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.attendance_register_id=? "); + preparedStmtList.add(searchCriteria.getRegisterId()); + } else if (registerIds != null && !registerIds.isEmpty()) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.attendance_register_id IN (").append(createQuery(registerIds)).append(")"); + addToPreparedStatement(preparedStmtList, registerIds); + } + + if (searchCriteria.getFromDate() != null) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.start_date>=? "); + preparedStmtList.add(searchCriteria.getFromDate()); + } + + if (searchCriteria.getToDate() != null) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.end_date<=? "); + preparedStmtList.add(searchCriteria.getToDate()); + } + + if (searchCriteria.getReferenceId() != null) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.reference_id=? "); + preparedStmtList.add(searchCriteria.getReferenceId()); + } + + if (searchCriteria.getServiceCode() != null) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.service_code=? "); + preparedStmtList.add(searchCriteria.getServiceCode()); + } + + if (searchCriteria.getStatus() != null && StringUtils.isNotBlank(searchCriteria.getStatus().toString())) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.status=? "); + preparedStmtList.add(searchCriteria.getStatus().toString()); + } + + if (StringUtils.isNotBlank(searchCriteria.getMusterRollStatus())) { + addClauseIfRequired(preparedStmtList, queryBuilder); + queryBuilder.append(" muster.musterroll_status=? "); + preparedStmtList.add(searchCriteria.getMusterRollStatus()); + } + return queryBuilder; + } + + + private static void addClauseIfRequired(List values, StringBuilder queryString) { + if (values.isEmpty()) + queryString.append(" WHERE "); + else { + queryString.append(" AND"); + } + } + + private String createQuery(Collection ids) { + StringBuilder builder = new StringBuilder(); + int length = ids.size(); + for (int i = 0; i < length; i++) { + builder.append(" ? "); + if (i != length - 1) builder.append(","); + } + return builder.toString(); + } + + private void addToPreparedStatement(List preparedStmtList, Collection ids) { + preparedStmtList.addAll(ids); + } + + private String addLimitAndOffset(StringBuilder queryBuilder, MusterRollSearchCriteria criteria, List preparedStmtList) { + String paginatedQuery = addOrderByClause(criteria.getSortBy(), criteria.getOrder()); + String finalQuery = paginatedQuery.replace("{}", queryBuilder.toString()); + preparedStmtList.add(criteria.getOffset()); + preparedStmtList.add(criteria.getLimit() + criteria.getOffset()); + + return finalQuery; + } + + private String addOrderByClause(String sortBy, Pagination.OrderEnum order) { + + String paginationWrapper = PAGINATION_WRAPPER; + + if ( !StringUtils.isEmpty(sortBy)) { + paginationWrapper=paginationWrapper.replace("{sortBy}", sortBy); + } + else{ + paginationWrapper=paginationWrapper.replace("{sortBy}", "createdtime"); + } + + if (order != null && Pagination.OrderEnum.fromValue(order.toString()) != null) { + paginationWrapper=paginationWrapper.replace("{orderBy}", order.name()); + } + else{ + paginationWrapper=paginationWrapper.replace("{orderBy}", Pagination.OrderEnum.DESC.name()); + } + + return paginationWrapper; + } + + public String getSearchCountQueryString(MusterRollSearchCriteria criteria, List preparedStmtList, List registerIds) { + String query = getMusterSearchQuery(criteria, preparedStmtList, registerIds, true); + if (query != null) + return COUNT_WRAPPER.replace("{INTERNAL_QUERY}", query); + else + return query; + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/repository/rowmapper/MusterRollRowMapper.java b/health-services/muster-roll/src/main/java/org/egov/repository/rowmapper/MusterRollRowMapper.java new file mode 100644 index 0000000000..bc8f1afedb --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/repository/rowmapper/MusterRollRowMapper.java @@ -0,0 +1,165 @@ +package org.egov.repository.rowmapper; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.models.AuditDetails; +import org.egov.tracer.model.CustomException; +import org.egov.web.models.AttendanceEntry; +import org.egov.web.models.IndividualEntry; +import org.egov.web.models.MusterRoll; +import org.egov.works.services.common.models.musterroll.Status; +import org.postgresql.util.PGobject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Repository; + +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Repository +public class MusterRollRowMapper implements ResultSetExtractor> { + + private final ObjectMapper mapper; + + @Autowired + public MusterRollRowMapper(ObjectMapper mapper) { + this.mapper = mapper; + } + + @Override + public List extractData(ResultSet rs) throws SQLException, DataAccessException { + Map musterRollMap = new LinkedHashMap<>(); + Map individualMap = new LinkedHashMap<>(); + while (rs.next()) { + String id = rs.getString("id"); + String tenantId = rs.getString("tenant_id"); + String musterRollNumber = rs.getString("musterroll_number"); + String registerId = rs.getString("attendance_register_id"); + String status = rs.getString("status"); + String musterRollStatus = rs.getString("musterroll_status"); + BigDecimal startDate = rs.getBigDecimal("start_date"); + BigDecimal endDate = rs.getBigDecimal("end_date"); + String referenceId = rs.getString("reference_id"); + String serviceCode = rs.getString("service_code"); + + String createdby = rs.getString("createdby"); + String lastmodifiedby = rs.getString("lastmodifiedby"); + Long createdtime = rs.getLong("createdtime"); + Long lastmodifiedtime = rs.getLong("lastmodifiedtime"); + + AuditDetails auditDetails = AuditDetails.builder().createdBy(createdby).createdTime(createdtime) + .lastModifiedBy(lastmodifiedby).lastModifiedTime(lastmodifiedtime) + .build(); + + JsonNode additionalDetails = getAdditionalDetail("additionaldetails", rs); + + MusterRoll musterRoll = MusterRoll.builder().id(id).tenantId(tenantId).musterRollNumber(musterRollNumber) + .registerId(registerId).status(Status.fromValue(status)).musterRollStatus(musterRollStatus).startDate(startDate) + .endDate(endDate).referenceId(referenceId).serviceCode(serviceCode) + .additionalDetails(additionalDetails).auditDetails(auditDetails).build(); + + if (!musterRollMap.containsKey(id)) { + musterRollMap.put(id, musterRoll); + } + addIndividualChildrenToProperty(rs, musterRollMap.get(id),individualMap); + } + return new ArrayList<>(musterRollMap.values()); + } + + private void addIndividualChildrenToProperty(ResultSet rs, MusterRoll musterRoll,Map individualMap) + throws SQLException { + addIndividualEntry(rs, musterRoll,individualMap); + } + + private void addIndividualEntry(ResultSet rs, MusterRoll musterRoll,Map individualMap) throws SQLException { + boolean isExist = true; + String id = rs.getString("summaryId"); + String musterId = rs.getString("indMusterId"); + String individualId = rs.getString("IndividualId"); + BigDecimal actualTotalAttendance = rs.getBigDecimal("actualTotalAttendance"); + BigDecimal modifiedtotalAttendance = rs.getBigDecimal("modifiedTotalAttendance"); + + String createdby = rs.getString("indCreatedBy"); + String lastmodifiedby = rs.getString("indModifiedBy"); + Long createdtime = rs.getLong("indCreatedTime"); + Long lastmodifiedtime = rs.getLong("indModifiedTime"); + + if (StringUtils.isNotBlank(musterId) && musterId.equalsIgnoreCase(musterRoll.getId())) { + + JsonNode additionalDetails = getAdditionalDetail("indAddlDetails", rs); + AuditDetails auditDetails = AuditDetails.builder().createdBy(createdby).createdTime(createdtime) + .lastModifiedBy(lastmodifiedby).lastModifiedTime(lastmodifiedtime) + .build(); + + IndividualEntry individualEntry = IndividualEntry.builder().id(id).individualId(individualId) + .actualTotalAttendance(actualTotalAttendance).modifiedTotalAttendance(modifiedtotalAttendance).additionalDetails(additionalDetails).auditDetails(auditDetails).build(); + + if (!individualMap.containsKey(id)) { + individualMap.put(id, individualEntry); + isExist = false; + } + addAttendanceChildrenToProperty(rs, individualMap.get(id)); + + if (!isExist) { + musterRoll.addIndividualEntriesItem(individualEntry); + } + + } + } + + private void addAttendanceChildrenToProperty(ResultSet rs, IndividualEntry individualEntry) + throws SQLException { + addAttendanceEntry(rs, individualEntry); + } + + private void addAttendanceEntry(ResultSet rs, IndividualEntry individualEntry) throws SQLException { + String id = rs.getString("attendanceId"); + String summaryId = rs.getString("attnSummaryId"); + BigDecimal date = rs.getBigDecimal("AttnDate"); + BigDecimal attendance = rs.getBigDecimal("attendance"); + + String createdby = rs.getString("attnCreatedBy"); + String lastmodifiedby = rs.getString("attnModifiedBy"); + Long createdtime = rs.getLong("attnCreatedTime"); + Long lastmodifiedtime = rs.getLong("attnModifiedTime"); + + if (StringUtils.isNotBlank(summaryId) && summaryId.equalsIgnoreCase(individualEntry.getId())) { + + JsonNode additionalDetails = getAdditionalDetail("attnAddlDetails", rs); + AuditDetails auditDetails = AuditDetails.builder().createdBy(createdby).createdTime(createdtime) + .lastModifiedBy(lastmodifiedby).lastModifiedTime(lastmodifiedtime) + .build(); + + AttendanceEntry attendanceEntry = AttendanceEntry.builder().id(id).time(date) + .attendance(attendance).additionalDetails(additionalDetails).auditDetails(auditDetails).build(); + + individualEntry.addAttendanceEntriesItem(attendanceEntry); + + } + } + + private JsonNode getAdditionalDetail(String columnName, ResultSet rs) throws SQLException { + JsonNode additionalDetails = null; + try { + PGobject obj = (PGobject) rs.getObject(columnName); + if (obj != null) { + additionalDetails = mapper.readTree(obj.getValue()); + } + } catch (IOException e) { + throw new CustomException("PARSING ERROR", "Failed to parse additionalDetail object"); + } + if (additionalDetails.isEmpty()) + additionalDetails = null; + return additionalDetails; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/service/CalculationService.java b/health-services/muster-roll/src/main/java/org/egov/service/CalculationService.java new file mode 100644 index 0000000000..2439dd0c9a --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/service/CalculationService.java @@ -0,0 +1,615 @@ +package org.egov.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.RequestInfoWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.models.individual.Individual; +import org.egov.common.models.individual.IndividualBulkResponse; +import org.egov.common.models.individual.IndividualSearch; +import org.egov.common.models.individual.IndividualSearchRequest; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.tracer.model.CustomException; +import org.egov.util.MdmsUtil; +import org.egov.util.MusterRollServiceUtil; +import org.egov.web.models.*; +import org.egov.works.services.common.models.bankaccounts.*; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +import static org.egov.util.MusterRollServiceConstants.*; + +@Service +@Slf4j +public class CalculationService { + + private final RestTemplate restTemplate; + + private final MusterRollServiceConfiguration config; + + private final MdmsUtil mdmsUtils; + + private final MusterRollServiceUtil musterRollServiceUtil; + + private final ObjectMapper mapper; + + private int halfDayNumHours; + private int fullDayNumHours; + private boolean isRoundOffHours; + + @Autowired + public CalculationService(RestTemplate restTemplate, MusterRollServiceConfiguration config, MdmsUtil mdmsUtils, MusterRollServiceUtil musterRollServiceUtil, ObjectMapper mapper) { + this.restTemplate = restTemplate; + this.config = config; + this.mdmsUtils = mdmsUtils; + this.musterRollServiceUtil = musterRollServiceUtil; + this.mapper = mapper; + } + + + /** + * Calculate the per day attendance and attendance aggregate for each individual for create muster roll + * @param musterRollRequest + * @param isCreate + * + */ + public void createAttendance(MusterRollRequest musterRollRequest, boolean isCreate) { + + //fetch MDMS data for muster - attendance hours and skill level + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + String tenantId = musterRoll.getTenantId(); + Object mdmsData = mdmsUtils.mDMSCallMuster(musterRollRequest, tenantId); + Object mdmsV2Data = mdmsUtils.mDMSV2CallMuster(musterRollRequest, tenantId); + + + //fetch the log events for all individuals in a muster roll + List attendanceLogList = fetchAttendanceLogsAndHours(musterRollRequest,mdmsData); + Map> individualEntryAttendanceMap = populateAttendanceLogEvents(attendanceLogList,ENTRY_EVENT); + Map> individualExitAttendanceMap = populateAttendanceLogEvents(attendanceLogList,EXIT_EVENT); + + log.info("CalculationService::createAttendance::From MDMS::HALF_DAY_NUM_HOURS::"+halfDayNumHours+"::FULL_DAY_NUM_HOURS::"+fullDayNumHours+"::isRoundOffHours::"+isRoundOffHours); + AuditDetails auditDetails = musterRoll.getAuditDetails(); + LocalDate startDate = Instant.ofEpochMilli(musterRoll.getStartDate().longValue()).atZone(ZoneId.of(config.getTimeZone())).toLocalDate(); + LocalDate endDate = Instant.ofEpochMilli(musterRoll.getEndDate().longValue()).atZone(ZoneId.of(config.getTimeZone())).toLocalDate(); + + log.debug("CalculationService::calculateAttendance::startDate::"+startDate+"::endDate::"+endDate); + + //calculate attendance aggregate and per day per individual attendance + List individualEntries = new ArrayList<>(); + List individualEntriesFromRequest = musterRoll.getIndividualEntries(); + + //Collect unique individuals from attendance logs + Set attendeesWithLogs = new HashSet<>(); + for(String individualId: individualExitAttendanceMap.keySet()) { + attendeesWithLogs.add(individualId); + } + //Fetch Absentees by comparing original enrolment against attendance register - fix for PFM-3184 + List absenteesList = fetchAbsentees(attendeesWithLogs, musterRoll, musterRollRequest.getRequestInfo()); + //Add absentees to the response first. These attendees have 0 as attendance + individualEntries.addAll(absenteesList); + + // fetch individual details from individual service and account details from bank account service + List individualIds = new ArrayList<>(); + individualIds.addAll(individualExitAttendanceMap.keySet()); + //Add all absentee individualIds as well + individualIds.addAll(absenteesList.stream().map(entry-> entry.getIndividualId()).collect(Collectors.toSet())); + List individuals = fetchIndividualDetails(individualIds, musterRollRequest.getRequestInfo(),musterRoll.getTenantId(),musterRoll); + List bankAccounts = fetchBankaccountDetails(individualIds, musterRollRequest.getRequestInfo(),musterRoll.getTenantId()); + + for (Map.Entry> entry : individualExitAttendanceMap.entrySet()) { + IndividualEntry individualEntry = new IndividualEntry(); + if (isCreate) { + individualEntry.setId(UUID.randomUUID().toString()); + } + individualEntry.setIndividualId(entry.getKey()); + List exitTimestampList = entry.getValue(); + List entryTimestampList = individualEntryAttendanceMap.get(entry.getKey()); + List attendanceEntries = new ArrayList<>(); + LocalDate date = startDate; + BigDecimal totalAttendance = new BigDecimal("0.0"); + while (date.isBefore(endDate) || date.isEqual(endDate)) { + AttendanceEntry attendanceEntry = new AttendanceEntry(); + String entryAttendanceLogId = null; + String exitAttendanceLogId = null; + if (isCreate) { + attendanceEntry.setId(UUID.randomUUID().toString()); + } + attendanceEntry.setTime(new BigDecimal(date.atStartOfDay(ZoneId.of(config.getTimeZone())).toInstant().toEpochMilli())); + + //check if the individual's entry attendance is logged between startDate and endDate + LocalDateTime entryTimestamp = null; + for (LocalDateTime dateTime : entryTimestampList) { + if (date.isEqual(dateTime.toLocalDate())) { + entryTimestamp = dateTime; + //populate the attendanceLogId for estimate + if (!isCreate) { + entryAttendanceLogId = getAttendanceLogId(attendanceLogList,individualEntry.getIndividualId(),entryTimestamp,ENTRY_EVENT); + } + break; + } + } + + //check if the individual's exit attendance is logged between startDate and endDate + LocalDateTime exitTimestamp = null; + for (LocalDateTime dateTime : exitTimestampList) { + if (date.isEqual(dateTime.toLocalDate())) { + exitTimestamp = dateTime; + //populate the attendanceLogId for estimate + if (!isCreate) { + exitAttendanceLogId = getAttendanceLogId(attendanceLogList,individualEntry.getIndividualId(),exitTimestamp,EXIT_EVENT); + } + break; + } + } + + totalAttendance = getTotalAttendance(totalAttendance, attendanceEntry, entryTimestamp,exitTimestamp); + + date = date.plusDays(1); + attendanceEntry.setAuditDetails(auditDetails); + // set attendanceLogId in additionalDetails for attendanceEntry + if (!isCreate && StringUtils.isNotBlank(entryAttendanceLogId) && StringUtils.isNotBlank(exitAttendanceLogId)) { + musterRollServiceUtil.populateAdditionalDetailsAttendanceEntry(attendanceEntry,entryAttendanceLogId,exitAttendanceLogId); + } + attendanceEntries.add(attendanceEntry); + } + + individualEntry.setAttendanceEntries(attendanceEntries); + log.debug("CalculationService::createAttendance::Attendance Entry::size::"+attendanceEntries.size()); + individualEntry.setAuditDetails(auditDetails); + individualEntry.setActualTotalAttendance(totalAttendance); + //Set individual details in additionalDetails + /** if (!CollectionUtils.isEmpty(individuals)) { + Individual individual = individuals.stream() + .filter(ind -> ind.getId().equalsIgnoreCase(individualEntry.getIndividualId())) + .findFirst().orElse(null); + BankAccount bankAccount = bankAccounts.stream() + .filter(account -> account.getReferenceId().equalsIgnoreCase(individualEntry.getIndividualId())) + .findFirst().orElse(null); + + if (individual != null) { + setAdditionalDetails(individualEntry,individualEntriesFromRequest,mdmsData,individual,bankAccount,isCreate); + } else { + log.info("CalculationService::createAttendance::No match found in individual and bank account service for the individual id from attendance log - "+individualEntry.getIndividualId()); + } + + } **/ + + individualEntries.add(individualEntry); + } + + // Loop through and set individual and bank account details + for (IndividualEntry entry : individualEntries) { + + // Set individual details in additionalDetails + if (!CollectionUtils.isEmpty(individuals) /* && !CollectionUtils.isEmpty(bankAccounts) */) { + Individual individual = individuals.stream() + .filter(ind -> ind.getId().equalsIgnoreCase(entry.getIndividualId())).findFirst() + .orElse(null); + BankAccount bankAccount = bankAccounts.stream() + .filter(account -> account.getReferenceId().equalsIgnoreCase(entry.getIndividualId())) + .findFirst().orElse(null); + + if (individual != null /* && bankAccount != null */) { + setAdditionalDetails(entry, individualEntriesFromRequest, mdmsV2Data, individual, + bankAccount, isCreate); + } else { + log.info( + "CalculationService::createAttendance::No match found in individual and bank account service for the individual id from attendance log - " + + entry.getIndividualId()); + } + + } + } + + musterRoll.setIndividualEntries(individualEntries); + log.debug("CalculationService::createAttendance::Individuals::size::"+musterRoll.getIndividualEntries().size()); + + } + + + /** + * //Fix for PFM-3184. Attendance register only contains info about people who attended. Absentees are left out. This method + * fetches all the unique individuals added to a register and identifies wage seekers who never attended a single day of work + * in a given time period. Adds their entries with attendnce of 0 and returns it to the UI. + * @param attendeesWithLogs + * @param musterRoll + * @param requestInfo + * @return + */ + private List fetchAbsentees(Set attendeesWithLogs, MusterRoll musterRoll, RequestInfo requestInfo){ + List absentees = new ArrayList<>(); + // Get all individuals who were originally registered to the register + AttendanceRegisterResponse response = musterRollServiceUtil.fetchAttendanceRegister(musterRoll, requestInfo); + List registers = response.getAttendanceRegister(); + if(registers!=null && !registers.isEmpty()) { + AttendanceRegister register = registers.get(0); + //Get all attendees of the register + getAllAttendees(musterRoll,register,attendeesWithLogs,absentees); + + }//End of if + return absentees; + } + + private void getAllAttendees(MusterRoll musterRoll, AttendanceRegister register, Set attendeesWithLogs, List absentees) { + List entries = register.getAttendees(); + Set allAttendees = null; + + if(entries!=null && !entries.isEmpty()) { + allAttendees = entries.stream().map(IndividualEntry::getIndividualId).collect(Collectors.toSet()); + //Remove all attendees who have marked some sort of attendance. This leaves the once who registered but never marked a day's work + allAttendees.removeAll(attendeesWithLogs); + //Add these absentees to a list with zero as attendance days + for(String individual: allAttendees) { + for(IndividualEntry entry: entries) { + if(entry.getIndividualId().equals(individual)) { + entry.setActualTotalAttendance(new BigDecimal(0)); + absentees.add(entry); } + } + }//End of for + } + else { + log.error("No attendees enrolled in register " + musterRoll.getRegisterId()); + } + } + /** + * Calculate the total attendance for the individual + * @param totalAttendance + * @param attendanceEntry + * @param entryTimestamp + * @param exitTimestamp + * + */ + private BigDecimal getTotalAttendance(BigDecimal totalAttendance, AttendanceEntry attendanceEntry, LocalDateTime entryTimestamp, LocalDateTime exitTimestamp) { + //set attendance if present else set as 0 + if (entryTimestamp != null && exitTimestamp != null) { + int workHours = exitTimestamp.getHour() - entryTimestamp.getHour(); + + /** IF isRoundOffHours is true, + * FullDayAttendance is given if the workHours is more than halfDayNumHours (even if it is less than fullDayNumHours). + * HalfDayAttendance is given if the workHours is less than halfDayNumHours. eg., 6hrs will be considered as fullDayAttendance, 2hrs as halfDayAttendance + */ + attendanceEntry.setAttendance(new BigDecimal("0.5")); + if (isRoundOffHours && workHours > halfDayNumHours) { + attendanceEntry.setAttendance(new BigDecimal("1.0")); + } + + if (!isRoundOffHours && workHours >= fullDayNumHours) { + attendanceEntry.setAttendance(new BigDecimal("1.0")); + } + + if (workHours == 0) { + attendanceEntry.setAttendance(new BigDecimal("0.0")); + } + + } else { + attendanceEntry.setAttendance(new BigDecimal("0.0")); + } + + //calculate totalAttendance + totalAttendance = totalAttendance.add(attendanceEntry.getAttendance()); + return totalAttendance; + } + + + /** + * Re-calculates the per day attendance and attendance aggregate for each individual on update + * @param musterRollRequest + * + */ + public void updateAttendance(MusterRollRequest musterRollRequest, Object mdmsData) { + + //fetch MDMS data for muster - attendance hours and skill level + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + + //fetch the log events for all individuals in a muster roll + List attendanceLogList = fetchAttendanceLogsAndHours(musterRollRequest,mdmsData); + Map> individualEntryAttendanceMap = populateAttendanceLogEvents(attendanceLogList,ENTRY_EVENT); + Map> individualExitAttendanceMap = populateAttendanceLogEvents(attendanceLogList,EXIT_EVENT); + + log.info("CalculationService::updateAttendance::From MDMS::HALF_DAY_NUM_HOURS::"+halfDayNumHours+"::FULL_DAY_NUM_HOURS::"+fullDayNumHours+"::isRoundOffHours::"+isRoundOffHours); + + //calculate attendance aggregate and per day per individual attendance + AuditDetails auditDetails = musterRoll.getAuditDetails(); + List individualEntries = musterRoll.getIndividualEntries(); + for (Map.Entry> entry : individualExitAttendanceMap.entrySet()) { + IndividualEntry individualEntry = individualEntries.stream() + .filter(individual -> individual.getIndividualId().equalsIgnoreCase(entry.getKey())) + .findFirst().get(); + + List exitTimestampList = entry.getValue(); + List entryTimestampList = individualEntryAttendanceMap.get(entry.getKey()); + List attendanceEntries = individualEntry.getAttendanceEntries(); + BigDecimal totalAttendance = new BigDecimal("0.0"); + for (AttendanceEntry attendanceEntry : attendanceEntries) { + LocalDate attendanceDate = Instant.ofEpochMilli(attendanceEntry.getTime().longValue()).atZone(ZoneId.of(config.getTimeZone())).toLocalDate(); + LocalDateTime entryTimestamp = entryTimestampList.stream() + .filter(dateTime -> dateTime.toLocalDate().isEqual(attendanceDate)) + .findFirst().orElse(null); + LocalDateTime exitTimestamp = exitTimestampList.stream() + .filter(dateTime -> dateTime.toLocalDate().isEqual(attendanceDate)) + .findFirst().orElse(null); + + totalAttendance = getTotalAttendance(totalAttendance, attendanceEntry, entryTimestamp,exitTimestamp ); + attendanceEntry.setAuditDetails(auditDetails); + + } + individualEntry.setAuditDetails(auditDetails); + individualEntry.setActualTotalAttendance(totalAttendance); + //During update on RESUBMIT ( if 'computeAttendance' is true), per day attendance and attendance aggregate will be recalculated + //so modifiedTotalAttendance will be reset as null as the musterRoll will go through verify action again. + individualEntry.setModifiedTotalAttendance(null); + } + log.debug("CalculationService::updateAttendance::Individuals::size::"+musterRoll.getIndividualEntries().size()); + } + + /** + * Fetch the attendance logs and the halfDayNumHours, fullDayNumHours from MDMS + * @param musterRollRequest + * @return List + */ + private List fetchAttendanceLogsAndHours(MusterRollRequest musterRollRequest, Object mdmsData) { + + //fetch the attendance log + List attendanceLogList = getAttendanceLogs(musterRollRequest.getMusterRoll(),musterRollRequest.getRequestInfo()); + + populateAttendanceHours(mdmsData); + + return attendanceLogList; + } + + /** + * The method returns a map with key as individualId and value as list of entry or exit timestamp + * @param attendanceLogList + * @param event + * @return + */ + + private Map> populateAttendanceLogEvents(List attendanceLogList, String event) { + //populate the map with key as individualId and value as the corresponding list of exit time + return attendanceLogList.stream() + .filter(attendanceLog -> attendanceLog.getType().equalsIgnoreCase(event)) + .collect(Collectors.groupingBy( + AttendanceLog::getIndividualId, //key + LinkedHashMap::new, // populate the map + Collectors.mapping(attendanceLog -> Instant.ofEpochMilli(attendanceLog.getTime().longValue()).atZone(ZoneId.of(config.getTimeZone())).toLocalDateTime(),Collectors.toList()) //value is the list of timestamp + )); + } + + /** + * Fetch the attendance log events for all individuals in an attendance register between the startDate and endDate + * This is fetched from the AttendanceLog service + * @param musterRoll + * @param requestInfo + * + */ + private List getAttendanceLogs(MusterRoll musterRoll, RequestInfo requestInfo){ + + /* UI sends the startDate and endDate. Set the toTime for attendanceLog search api as endDate+23h0m to + * fetch the logs till end of the endDate */ + BigDecimal fromTime = musterRoll.getStartDate(); + LocalDate endDate = Instant.ofEpochMilli(musterRoll.getEndDate().longValue()).atZone(ZoneId.of(config.getTimeZone())).toLocalDate(); + // set the endTime as endDate's date+23h0min + LocalDateTime endTime = endDate.atTime(23,0); + BigDecimal toTime = new BigDecimal(endTime.atZone(ZoneId.of(config.getTimeZone())).toInstant().toEpochMilli()); + + StringBuilder uri = new StringBuilder(); + uri.append(config.getAttendanceLogHost()).append(config.getAttendanceLogEndpoint()); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(uri.toString()) + .queryParam("tenantId",musterRoll.getTenantId()) + .queryParam("registerId",musterRoll.getRegisterId()) + .queryParam("fromTime",fromTime) + .queryParam("toTime",toTime) + .queryParam("status", Status.ACTIVE); + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + AttendanceLogResponse attendanceLogResponse = null; + + log.info("CalculationService::getAttendanceLogs::call attendance log search with tenantId::"+musterRoll.getTenantId() + +"::registerId::"+musterRoll.getRegisterId()+"::fromTime::"+musterRoll.getStartDate()+"::toTime::"+musterRoll.getEndDate()); + + try { + attendanceLogResponse = restTemplate.postForObject(uriBuilder.toUriString(),requestInfoWrapper,AttendanceLogResponse.class); + } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { + log.error("CalculationService::getAttendanceLogs::Error thrown from attendance log service::"+httpClientOrServerExc.getStatusCode()); + throw new CustomException("ATTENDANCE_LOG_SERVICE_EXCEPTION","Error thrown from attendance log service::"+httpClientOrServerExc.getStatusCode()); + } + + if (attendanceLogResponse == null || attendanceLogResponse.getAttendance() == null) { + StringBuilder exceptionMessage = new StringBuilder(); + exceptionMessage.append("No attendance log found for the register - "); + exceptionMessage.append(musterRoll.getRegisterId()); + exceptionMessage.append(" with startDate - "); + exceptionMessage.append(musterRoll.getStartDate()); + exceptionMessage.append(" and endDate - "); + exceptionMessage.append(musterRoll.getEndDate()); + throw new CustomException("ATTENDANCE_LOG_EMPTY",exceptionMessage.toString()); + } + + log.info("CalculationService::getAttendanceLogs::Attendance logs fetched successfully"); + return attendanceLogResponse.getAttendance(); + } + + /** + * Fetch the half day , full day hours from MDMS + * @param mdmsData + * + */ + private void populateAttendanceHours(Object mdmsData) { + + final String jsonPathForWorksMuster = "$.MdmsRes." + MDMS_COMMON_MASTERS_MODULE_NAME + "." + MASTER_MUSTER_ROLL + ".*"; + List> musterRes = null; + + try { + musterRes = JsonPath.read(mdmsData, jsonPathForWorksMuster); + + } catch (Exception e) { + log.error("CalculationService::populateAttendanceHours::Error parsing mdms response::"+e.getMessage()); + throw new CustomException("JSONPATH_ERROR", "Failed to parse mdms response"); + } + + if (!CollectionUtils.isEmpty(musterRes)) { + for (LinkedHashMap codeValueMap : musterRes) { + String code = codeValueMap.get("code"); + String value = codeValueMap.get("value"); + switch (code) { + case HALF_DAY_NUM_HOURS : + halfDayNumHours = Integer.parseInt(value); + break; + case FULL_DAY_NUM_HOURS : + fullDayNumHours =Integer.parseInt(value); + break; + case ROUND_OFF_HOURS : + isRoundOffHours = BooleanUtils.toBoolean(value); + break; + default: + } + + } + } + + } + + /** + * Set the additionalDetails of the individual + * @param individualEntry + * @param individualEntriesFromRequest + * @param mdmsData + */ + private void setAdditionalDetails(IndividualEntry individualEntry, List individualEntriesFromRequest, Object mdmsData, Individual matchedIndividual, BankAccount bankAccount, boolean isCreate) { + + String skillCode = null; + if (!CollectionUtils.isEmpty(individualEntriesFromRequest)) { + IndividualEntry individualEntryRequest = individualEntriesFromRequest.stream() + .filter(individual -> individual.getIndividualId().equalsIgnoreCase(individualEntry.getIndividualId())) + .findFirst().orElse(null); + try { + if (individualEntryRequest != null && individualEntryRequest.getAdditionalDetails() != null) { + JsonNode node = mapper.readTree(mapper.writeValueAsString(individualEntryRequest.getAdditionalDetails())); + skillCode = node.findValue("code").textValue(); + } + } catch (IOException e) { + log.error("CalculationService::setAdditionalDetails::Failed to parse additionalDetail object from request "+e); + throw new CustomException("PARSING ERROR", "Failed to parse additionalDetail object from request"); + } + } + + //Update the skill value based on the code from request or set as default skill + musterRollServiceUtil.populateAdditionalDetails(mdmsData, individualEntry, skillCode, matchedIndividual, bankAccount, isCreate); + } + + /** + * Fetch the individual details - Name, Father's name and Aadhar details from individual service + * @param ids + * + */ + private List fetchIndividualDetails(List ids,RequestInfo requestInfo, String tenantId, MusterRoll musterRoll){ + // fetch the individual details from individual service + StringBuilder uri = new StringBuilder(); + uri.append(config.getIndividualHost()).append(config.getIndividualSearchEndpoint()); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(uri.toString()) + .queryParam("limit",100) + .queryParam("offset",0) + .queryParam("tenantId",tenantId); + + IndividualSearch individualSearch = IndividualSearch.builder().id(ids).build(); + IndividualSearchRequest individualSearchRequest = IndividualSearchRequest.builder() + .requestInfo(requestInfo).individual(individualSearch).build(); + + IndividualBulkResponse response = null; + log.info("CalculationService::fetchIndividualDetails::call individual search with tenantId::"+tenantId + +"::individual ids::"+ids); + + try { + response = restTemplate.postForObject(uriBuilder.toUriString(),individualSearchRequest,IndividualBulkResponse.class); + } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { + log.error("CalculationService::fetchIndividualDetails::Error thrown from individual search service::"+httpClientOrServerExc.getStatusCode()); + throw new CustomException("INDIVIDUAL_SEARCH_SERVICE_EXCEPTION","Error thrown from individual search service::"+httpClientOrServerExc.getStatusCode()); + } + + if (response == null || CollectionUtils.isEmpty(response.getIndividual())) { + StringBuilder exceptionMessage = new StringBuilder(); + exceptionMessage.append("Indiviudal search returned empty response for registerId "); + exceptionMessage.append(musterRoll.getRegisterId()); + exceptionMessage.append(" with startDate - "); + exceptionMessage.append(musterRoll.getStartDate()); + exceptionMessage.append(" and endDate - "); + exceptionMessage.append(musterRoll.getEndDate()); + throw new CustomException("INDIVIDUAL_SEARCH_SERVICE_EMPTY",exceptionMessage.toString()); + } + + log.info("CalculationService::fetchIndividualDetails::Individual search fetched successfully"); + return response.getIndividual(); + + } + + /** + * Fetch the bank account details from bankAccount service + * @param ids + * + */ + private List fetchBankaccountDetails(List ids,RequestInfo requestInfo, String tenantId){ + // fetch the bank account details from bank account service + StringBuilder uri = new StringBuilder(); + uri.append(config.getBankaccountsHost()).append(config.getBankaccountsSearchEndpoint()); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(uri.toString()); + + BankAccountSearchCriteria bankAccountSearchCriteria = BankAccountSearchCriteria.builder().tenantId(tenantId) + .serviceCode("IND").referenceId(ids).build(); + Pagination pagination = Pagination.builder().limit(100d).build(); + BankAccountSearchRequest bankAccountSearchRequest = BankAccountSearchRequest.builder(). + requestInfo(requestInfo).bankAccountDetails(bankAccountSearchCriteria).pagination(pagination).build(); + + BankAccountResponse response = null; + log.info("CalculationService::fetchBankaccountDetails::call bankaccounts search with tenantId::"+tenantId + +"::individual ids::"+ids); + + try { + response = restTemplate.postForObject(uriBuilder.toUriString(),bankAccountSearchRequest,BankAccountResponse.class); + } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { + log.error("CalculationService::fetchBankaccountDetails::Error thrown from bankaccounts search service::"+httpClientOrServerExc.getStatusCode()); + throw new CustomException("BANKACCOUNTS_SEARCH_SERVICE_EXCEPTION","Error thrown from bankaccounts search service::"+httpClientOrServerExc.getStatusCode()); + } + + if (response == null || CollectionUtils.isEmpty(response.getBankAccounts())) { + return new ArrayList<>(); + } + + log.info("CalculationService::fetchBankaccountDetails::Individual search fetched successfully"); + return response.getBankAccounts(); + + } + + /** + * Fetches the id of the attendance log + * @return + */ + private String getAttendanceLogId(List attendanceLogList, String individualId, LocalDateTime timestamp, String type) { + AttendanceLog attendanceLog = null; + BigDecimal time = new BigDecimal(timestamp.atZone(ZoneId.of(config.getTimeZone())).toInstant().toEpochMilli()); + attendanceLog = attendanceLogList.stream() + .filter(attnLog -> attnLog.getIndividualId().equalsIgnoreCase(individualId) + && attnLog.getTime().compareTo(time) == 0 && attnLog.getType().equalsIgnoreCase(type)) + .findFirst().orElse(null); + return attendanceLog != null ? attendanceLog.getId() : ""; + } + +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/java/org/egov/service/EnrichmentService.java b/health-services/muster-roll/src/main/java/org/egov/service/EnrichmentService.java new file mode 100644 index 0000000000..50f5f08413 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/service/EnrichmentService.java @@ -0,0 +1,261 @@ +package org.egov.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.idgen.IdResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.models.Workflow; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.repository.IdGenRepository; +import org.egov.tracer.model.CustomException; +import org.egov.util.MusterRollServiceUtil; +import org.egov.web.models.*; +import org.egov.works.services.common.models.expense.Pagination; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.egov.util.MusterRollServiceConstants.ACTION_REJECT; + +@Service +@Slf4j +public class EnrichmentService { + + private final MusterRollServiceUtil musterRollServiceUtil; + + private final MusterRollServiceConfiguration config; + + private final IdGenRepository idGenRepository; + + private final ObjectMapper mapper; + + @Autowired + public EnrichmentService(MusterRollServiceUtil musterRollServiceUtil, MusterRollServiceConfiguration config, IdGenRepository idGenRepository, ObjectMapper mapper) { + this.musterRollServiceUtil = musterRollServiceUtil; + this.config = config; + this.idGenRepository = idGenRepository; + this.mapper = mapper; + } + + + /** + * Enrich muster roll on estimate + * @param musterRollRequest + * + */ + public void enrichMusterRollOnEstimate(MusterRollRequest musterRollRequest) { + log.info("EnrichmentService::enrichMusterRollOnEstimate"); + + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + + //Audit details + AuditDetails auditDetails = musterRollServiceUtil.getAuditDetails(requestInfo.getUserInfo().getUuid(), musterRoll, true); + musterRoll.setAuditDetails(auditDetails); + + //status + musterRoll.setStatus(Status.ACTIVE); + + //contract service code + musterRoll.setServiceCode(config.getContractServiceCode()); + + } + + /** + * Enrich muster roll on create + * @param musterRollRequest + * + */ + public void enrichMusterRollOnCreate(MusterRollRequest musterRollRequest) { + log.info("EnrichmentService::enrichMusterRollOnCreate"); + + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + + //Id + musterRoll.setId(UUID.randomUUID().toString()); + + //Audit details + AuditDetails auditDetails = musterRollServiceUtil.getAuditDetails(requestInfo.getUserInfo().getUuid(), musterRoll, true); + musterRoll.setAuditDetails(auditDetails); + + //musterRollNumber - Idgen + String rootTenantId = musterRoll.getTenantId(); + List musterNumbers = getIdList(requestInfo, rootTenantId + , config.getIdgenMusterRollNumberName(), "", 1); //idformat will be fetched by idGen service + if (musterNumbers != null && !musterNumbers.isEmpty()) { + String musterRollNumber = musterNumbers.get(0); + musterRoll.setMusterRollNumber(musterRollNumber); + } else { + throw new CustomException("MUSTER_NUMBER_NOT_GENERATED","Error occurred while generating muster roll numbers from IdGen service"); + } + + //status + musterRoll.setStatus(Status.ACTIVE); + + //contract service code + musterRoll.setServiceCode(config.getContractServiceCode()); + + } + + /** + * Enrich the muster roll on update + * @param musterRollRequest + */ + public void enrichMusterRollOnUpdate(MusterRollRequest musterRollRequest, MusterRoll existingMusterRoll, Object mdmsData) { + log.info("EnrichmentService::enrichMusterRollOnUpdate"); + + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + Workflow workflow = musterRollRequest.getWorkflow(); + + log.info("EnrichmentService::enrichMusterRollOnUpdate::Workflow action is "+workflow.getAction()); + + + //update totalAttendance and skill details if modified + List individualEntries = existingMusterRoll.getIndividualEntries(); + List modifiedIndividualEntries = musterRoll.getIndividualEntries(); + if (!CollectionUtils.isEmpty(modifiedIndividualEntries)) { + for (IndividualEntry individualEntry : individualEntries) { + for (IndividualEntry modifiedIndividualEntry : modifiedIndividualEntries) { + if (modifiedIndividualEntry.getId().equalsIgnoreCase(individualEntry.getId())) { + //update the total attendance + if (modifiedIndividualEntry.getModifiedTotalAttendance() != null) { + individualEntry.setModifiedTotalAttendance(modifiedIndividualEntry.getModifiedTotalAttendance()); + } + if (modifiedIndividualEntry.getAdditionalDetails() != null) { + try { + JsonNode node = mapper.readTree(mapper.writeValueAsString(modifiedIndividualEntry.getAdditionalDetails())); + if (node.findValue("code") != null && StringUtils.isNotBlank(node.findValue("code").textValue())) { + String skillCode = node.findValue("code").textValue(); + //Update the skill value based on the code from request + musterRollServiceUtil.updateAdditionalDetails(mdmsData,individualEntry,skillCode); + } + } catch (IOException e) { + log.info("EnrichmentService::enrichMusterRollOnUpdate::Failed to parse additionalDetail object from request"+e); + throw new CustomException("PARSING ERROR", "Failed to parse additionalDetail object from request on update"); + } + + } + break; + } + } + } + } + + musterRoll = existingMusterRoll; + musterRollRequest.setMusterRoll(existingMusterRoll); + + //AuditDetails + musterRoll.setAuditDetails(existingMusterRoll.getAuditDetails()); + AuditDetails auditDetails = musterRollServiceUtil.getAuditDetails(requestInfo.getUserInfo().getUuid(), musterRoll, false); + musterRoll.setAuditDetails(auditDetails); + + //populate auditDetails in IndividualEntry and AttendanceEntry + populateAuditDetailsIndividualEntry(musterRoll); + + if (workflow.getAction().equals(ACTION_REJECT)) { + enrichUpdateMusterWorkFlowForActionReject(musterRollRequest); + } + + } + + /** + * If the workflow action is 'REJECT' then assignee will be updated + * with user id that is having role as 'ORG_STAFF' or 'ORG_ADMIN' (i.e the 'auditDetails.createdBy') + * + * @param request + */ + private void enrichUpdateMusterWorkFlowForActionReject(MusterRollRequest request) { + Workflow workflow = request.getWorkflow(); + AuditDetails auditDetails = request.getMusterRoll().getAuditDetails(); + if (auditDetails != null && StringUtils.isNotBlank(auditDetails.getCreatedBy())) { + List updatedAssignees = new ArrayList<>(); + updatedAssignees.add(auditDetails.getCreatedBy()); + workflow.setAssignes(updatedAssignees); + } + } + + /** + * Enrich MusterRollSearchCriteria with default offset and limit + * + * @param searchCriteria + */ + public void enrichSearchRequest(MusterRollSearchCriteria searchCriteria) { + + if (searchCriteria.getLimit() == null) + searchCriteria.setLimit(config.getMusterDefaultLimit()); + + if (searchCriteria.getOffset() == null) + searchCriteria.setOffset(config.getMusterDefaultOffset()); + + if (searchCriteria.getLimit() != null && searchCriteria.getLimit() > config.getMusterMaxLimit()) + searchCriteria.setLimit(config.getMusterMaxLimit()); + + if (searchCriteria.getSortBy() == null) + searchCriteria.setSortBy("createdTime"); + + if (searchCriteria.getOrder() == null) + searchCriteria.setOrder(Pagination.OrderEnum.DESC); + } + + /** + * Populate audit details for individualEntry + * + * @param musterRoll + */ + private void populateAuditDetailsIndividualEntry(MusterRoll musterRoll) { + if (musterRoll.getIndividualEntries() != null) { + for (IndividualEntry individualEntry : musterRoll.getIndividualEntries()) { + individualEntry.setAuditDetails(musterRoll.getAuditDetails()); + populateAuditDetailsAttendanceEntry(individualEntry); + } + } + + } + + /** + * Populate audit details for attendanceEntry + * + * @param individualEntry + */ + private void populateAuditDetailsAttendanceEntry(IndividualEntry individualEntry) { + if (individualEntry.getAttendanceEntries() != null) { + for (AttendanceEntry attendanceEntry : individualEntry.getAttendanceEntries()) { + attendanceEntry.setAuditDetails(individualEntry.getAuditDetails()); + } + } + + } + + /** + * Returns a list of numbers generated from idgen + * + * @param requestInfo RequestInfo from the request + * @param tenantId tenantId of the city + * @param idKey code of the field defined in application properties for which ids are generated for + * @param idformat format in which ids are to be generated + * @param count Number of ids to be generated + * @return List of ids generated using idGen service + */ + private List getIdList(RequestInfo requestInfo, String tenantId, String idKey, + String idformat, int count) { + List idResponses = idGenRepository.getId(requestInfo, tenantId, idKey, idformat, count).getIdResponses(); + + if (CollectionUtils.isEmpty(idResponses)) + throw new CustomException("IDGEN ERROR", "No ids returned from idgen Service"); + + return idResponses.stream() + .map(IdResponse::getId).collect(Collectors.toList()); + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/service/MusterRollService.java b/health-services/muster-roll/src/main/java/org/egov/service/MusterRollService.java new file mode 100644 index 0000000000..34ff246e74 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/service/MusterRollService.java @@ -0,0 +1,337 @@ +package org.egov.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.contract.models.RequestInfoWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.request.Role; +import org.egov.common.contract.response.ResponseInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.kafka.MusterRollProducer; +import org.egov.repository.MusterRollRepository; +import org.egov.tracer.model.CustomException; +import org.egov.util.MdmsUtil; +import org.egov.util.MusterRollServiceUtil; +import org.egov.util.ResponseInfoCreator; +import org.egov.validator.MusterRollValidator; + +import org.egov.web.models.AttendanceRegister; +import org.egov.web.models.AttendanceRegisterResponse; +import org.egov.web.models.MusterRoll; +import org.egov.web.models.MusterRollRequest; +import org.egov.web.models.MusterRollResponse; +import org.egov.web.models.MusterRollSearchCriteria; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static org.egov.util.MusterRollServiceConstants.STATUS_APPROVED; + +@Service +@Slf4j +public class MusterRollService { + + private final MusterRollValidator musterRollValidator; + + private final EnrichmentService enrichmentService; + + private final CalculationService calculationService; + + private final WorkflowService workflowService; + + private final NotificationService notificationService; + + private final MusterRollProducer musterRollProducer; + + private final MusterRollServiceConfiguration serviceConfiguration; + + private final MusterRollRepository musterRollRepository; + + private final ObjectMapper mapper; + + private final MdmsUtil mdmsUtils; + + private final MusterRollServiceUtil musterRollServiceUtil; + + private final MusterRollServiceConfiguration config; + + private final RestTemplate restTemplate; + + private final ResponseInfoCreator responseInfoCreator; + + private static final String COMPUTE_ATTENDENSE = "computeAttendance"; + + @Autowired + public MusterRollService(CalculationService calculationService, MusterRollValidator musterRollValidator, EnrichmentService enrichmentService, WorkflowService workflowService, NotificationService notificationService, MusterRollProducer musterRollProducer, MusterRollServiceConfiguration serviceConfiguration, MusterRollRepository musterRollRepository, ObjectMapper mapper, RestTemplate restTemplate, MdmsUtil mdmsUtils, MusterRollServiceUtil musterRollServiceUtil, MusterRollServiceConfiguration config, ResponseInfoCreator responseInfoCreator) { + this.calculationService = calculationService; + this.musterRollValidator = musterRollValidator; + this.enrichmentService = enrichmentService; + this.workflowService = workflowService; + this.notificationService = notificationService; + this.musterRollProducer = musterRollProducer; + this.serviceConfiguration = serviceConfiguration; + this.musterRollRepository = musterRollRepository; + this.mapper = mapper; + this.restTemplate = restTemplate; + this.mdmsUtils = mdmsUtils; + this.musterRollServiceUtil = musterRollServiceUtil; + this.config = config; + this.responseInfoCreator = responseInfoCreator; + } + + /** + * Calculates the per day attendance , attendance aggregate from startDate to endDate + * and provides it as an estimate. + * Note: This will NOT create muster roll and NOT store the details + * + * @param musterRollRequest + * @return + */ + public MusterRollRequest estimateMusterRoll(MusterRollRequest musterRollRequest) { + log.info("MusterRollService::estimateMusterRoll"); + + musterRollValidator.validateEstimateMusterRoll(musterRollRequest); + enrichmentService.enrichMusterRollOnEstimate(musterRollRequest); + calculationService.createAttendance(musterRollRequest,false); + return musterRollRequest; + } + + + /** + * Calculates the per day attendance , attendance aggregate from startDate to endDate for all the + * individuals of the provided attendance register. + * Creates muster roll and stores the details. + * + * @param musterRollRequest + * @return + */ + public MusterRollRequest createMusterRoll(MusterRollRequest musterRollRequest) { + log.info("MusterRollService::createMusterRoll"); + + musterRollValidator.validateCreateMusterRoll(musterRollRequest); + checkMusterRollExists(musterRollRequest.getMusterRoll()); + enrichmentService.enrichMusterRollOnCreate(musterRollRequest); + calculationService.createAttendance(musterRollRequest,true); + workflowService.updateWorkflowStatus(musterRollRequest); + + musterRollProducer.push(serviceConfiguration.getSaveMusterRollTopic(), musterRollRequest); + return musterRollRequest; + } + + /** + * Search muster roll based on the given search criteria - tenantId, musterId, musterRollNumber, startDate, endDate, status + * and musterRollStatus + * + * @param requestInfoWrapper + * @param searchCriteria + * @return + */ + public MusterRollResponse searchMusterRolls(RequestInfoWrapper requestInfoWrapper, MusterRollSearchCriteria searchCriteria) { + log.info("MusterRollService::searchMusterRolls"); + + musterRollValidator.validateSearchMuster(requestInfoWrapper,searchCriteria); + enrichmentService.enrichSearchRequest(searchCriteria); + + List roles = requestInfoWrapper.getRequestInfo().getUserInfo().getRoles(); + boolean isFilterRequired = false; + if (config.getRestrictedSearchRoles() != null && !config.getRestrictedSearchRoles().isEmpty()) { + List restrictedRoles = Arrays.asList(config.getRestrictedSearchRoles().split(",")); + isFilterRequired = roles.stream() + .anyMatch(role -> restrictedRoles.contains(role.getCode())); + } + + //Fetch the attendance registers that belong to the user and then fetch the musters that belongs to the user + List registerIds = new ArrayList<>(); + if (isFilterRequired) { + registerIds = fetchAttendanceRegistersOfUser(requestInfoWrapper.getRequestInfo(),searchCriteria); + } + + List musterRollList = musterRollRepository.getMusterRoll(searchCriteria,registerIds); + int count = musterRollRepository.getMusterRollCount(searchCriteria,registerIds); + List filteredMusterRollList = musterRollList; + + //apply the limit and offset + if (filteredMusterRollList != null && !musterRollServiceUtil.isTenantBasedSearch(searchCriteria)) { + filteredMusterRollList = applyLimitAndOffset(searchCriteria,filteredMusterRollList); + } + + //populate response + ResponseInfo responseInfo = responseInfoCreator.createResponseInfoFromRequestInfo(requestInfoWrapper.getRequestInfo(), true); + return MusterRollResponse.builder().responseInfo(responseInfo).musterRolls(filteredMusterRollList) + .count(count).build(); + } + + /** + * Updates the totalAttendance, skill details (if modified) and re-calculates the attendance (if 'computeAttendance' is true) + * + * @param musterRollRequest + * @return + */ + public MusterRollRequest updateMusterRoll(MusterRollRequest musterRollRequest) { + log.info("MusterRollService::updateMusterRoll"); + + musterRollValidator.validateUpdateMusterRoll(musterRollRequest); + //If 'computeAttendance' flag is true, re-calculate the attendance from attendanceLogs and update + boolean isComputeAttendance = isComputeAttendance(musterRollRequest.getMusterRoll()); + + //check if the user is enrolled in the attendance register for resubmit + MusterRoll existingMusterRoll = fetchExistingMusterRoll(musterRollRequest.getMusterRoll()); + log.info("MusterRollService::updateMusterRoll::update request for musterRollNumber::"+existingMusterRoll.getMusterRollNumber()); + + //fetch MDMS data for muster - skill level + String tenantId = existingMusterRoll.getTenantId(); + Object mdmsData = mdmsUtils.mDMSCallMuster(musterRollRequest, tenantId); + Object mdmsV2Data = mdmsUtils.mDMSV2CallMuster(musterRollRequest, tenantId); + + + //fetch the update additionalDetails from the request and persist it for verification + if (!isComputeAttendance) { + Object additionalDetails = musterRollRequest.getMusterRoll().getAdditionalDetails(); + existingMusterRoll.setAdditionalDetails(additionalDetails); + } + + enrichmentService.enrichMusterRollOnUpdate(musterRollRequest,existingMusterRoll,mdmsV2Data); + if (isComputeAttendance) { + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + musterRollValidator.isValidUser(existingMusterRoll, requestInfo); + calculationService.updateAttendance(musterRollRequest,mdmsData); + } + workflowService.updateWorkflowStatus(musterRollRequest); + musterRollProducer.push(serviceConfiguration.getUpdateMusterRollTopic(), musterRollRequest); + + try { + notificationService.sendNotificationToCBO(musterRollRequest); + }catch (Exception e){ + log.error("Exception while sending notification: " + e); + } + + //If the musterroll is in 'APPROVED' status, push the musterRoll to calculate topic to be processed by expense-calculator service + if (StringUtils.isNotBlank(musterRollRequest.getMusterRoll().getMusterRollStatus()) && STATUS_APPROVED.equalsIgnoreCase(musterRollRequest.getMusterRoll().getMusterRollStatus())) { + musterRollProducer.push(serviceConfiguration.getCalculateMusterRollTopic(), musterRollRequest); + } + + return musterRollRequest; + } + + /** + * Check if the muster roll already exists for the same registerId, startDate and endDate to avoid duplicate muster creation + * @param musterRoll + */ + private void checkMusterRollExists(MusterRoll musterRoll) { + MusterRollSearchCriteria searchCriteria = MusterRollSearchCriteria.builder().tenantId(musterRoll.getTenantId()) + .registerId(musterRoll.getRegisterId()).fromDate(musterRoll.getStartDate()) + .toDate(musterRoll.getEndDate()).build(); + List musterRolls = musterRollRepository.getMusterRoll(searchCriteria,null); + if (!CollectionUtils.isEmpty(musterRolls)) { + StringBuilder exceptionMessage = new StringBuilder(); + exceptionMessage.append("Muster roll already exists for the register - "); + exceptionMessage.append(musterRoll.getRegisterId()); + exceptionMessage.append(" with startDate - "); + exceptionMessage.append(musterRoll.getStartDate()); + exceptionMessage.append(" and endDate - "); + exceptionMessage.append(musterRoll.getEndDate()); + throw new CustomException("DUPLICATE_MUSTER_ROLL",exceptionMessage.toString()); + } + } + + /** + * Fetch the existing muster roll from DB else throw error + * @param musterRoll + * @return + */ + private MusterRoll fetchExistingMusterRoll(MusterRoll musterRoll) { + List ids = new ArrayList<>(); + ids.add(musterRoll.getId()); + MusterRollSearchCriteria searchCriteria = MusterRollSearchCriteria.builder().ids(ids).tenantId(musterRoll.getTenantId()).build(); + List musterRolls = musterRollRepository.getMusterRoll(searchCriteria,null); + if (CollectionUtils.isEmpty(musterRolls)) { + throw new CustomException("NO_MATCH_FOUND","Invalid Muster roll id - "+musterRoll.getId()); + } + return musterRolls.get(0); + } + + /** + * Check if the 'computeAttendance' flag is true + * @param musterRoll + * @return + */ + private boolean isComputeAttendance (MusterRoll musterRoll) { + if (musterRoll.getAdditionalDetails() != null) { + try { + JsonNode node = mapper.readTree(mapper.writeValueAsString(musterRoll.getAdditionalDetails())); + if (node.findValue(COMPUTE_ATTENDENSE) != null && StringUtils.isNotBlank(node.findValue(COMPUTE_ATTENDENSE).textValue())) { + String value = node.findValue(COMPUTE_ATTENDENSE).textValue(); + return BooleanUtils.toBoolean(value); + } + } catch (IOException e) { + log.info("MusterRollService::isComputeAttendance::Failed to parse additionalDetail object from request"+e); + throw new CustomException("PARSING ERROR", "Failed to parse additionalDetail object from request on update"); + } + } + return false; + } + + /** + * Fetch the registerIds that the user belongs to ( if role is org_staff or org_admin) + * @param requestInfo + * @return + */ + private List fetchAttendanceRegistersOfUser(RequestInfo requestInfo, MusterRollSearchCriteria searchCriteria) { + String id = requestInfo.getUserInfo().getUuid(); + + StringBuilder uri = new StringBuilder(); + uri.append(config.getAttendanceLogHost()).append(config.getAttendanceRegisterEndpoint()); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(uri.toString()) + .queryParam("tenantId",searchCriteria.getTenantId()) + .queryParam("status", Status.ACTIVE) + .queryParam("limit", config.getAttendanceRegisterSearchLimit()); + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + + AttendanceRegisterResponse attendanceRegisterResponse = null; + log.info("MusterRollService::fetchAttendanceRegistersOfUser::call attendance register search with tenantId::"+searchCriteria.getTenantId() + +"::for user::"+id); + + try { + attendanceRegisterResponse = restTemplate.postForObject(uriBuilder.toUriString(),requestInfoWrapper,AttendanceRegisterResponse.class); + } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { + log.error("MusterRollService::fetchAttendanceRegistersOfUser::Error thrown from attendance register service::"+httpClientOrServerExc.getStatusCode()); + throw new CustomException("ATTENDANCE_REGISTER_SERVICE_EXCEPTION","Error thrown from attendance register service::"+httpClientOrServerExc.getStatusCode()); + } + + if (attendanceRegisterResponse == null || CollectionUtils.isEmpty(attendanceRegisterResponse.getAttendanceRegister())) { + throw new CustomException("NO_DATA_FOUND","No Attendance registers found for the user. So no muster created for the register"); + } + + List attendanceRegisters = attendanceRegisterResponse.getAttendanceRegister(); + return attendanceRegisters.stream() + .map(AttendanceRegister::getId) + .collect(Collectors.toList()); + } + + /** + * Applies the limit and offset + * @param searchCriteria + * @param musterRollList + * @return + */ + private List applyLimitAndOffset(MusterRollSearchCriteria searchCriteria, List musterRollList) { + return musterRollList.stream() + .skip(searchCriteria.getOffset()) // offset + .limit(searchCriteria.getLimit()) // limit + .collect(Collectors.toList()); + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/service/NotificationService.java b/health-services/muster-roll/src/main/java/org/egov/service/NotificationService.java new file mode 100644 index 0000000000..3766b940d2 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/service/NotificationService.java @@ -0,0 +1,88 @@ +package org.egov.service; + + +import digit.models.coremodels.SMSRequest; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.kafka.MusterRollProducer; +import org.egov.util.LocalizationUtil; +import org.egov.util.NotificationUtil; +import org.egov.web.models.MusterRollRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + +import static org.egov.util.MusterRollServiceConstants.*; + +@Service +@Slf4j +public class NotificationService { + + private final MusterRollProducer musterRollProducer; + + private final NotificationUtil notificationUtil; + + private final LocalizationUtil localizationUtil; + + private final MusterRollServiceConfiguration config; + + @Autowired + public NotificationService(MusterRollProducer musterRollProducer, NotificationUtil notificationUtil, LocalizationUtil localizationUtil, MusterRollServiceConfiguration config) { + this.musterRollProducer = musterRollProducer; + this.notificationUtil = notificationUtil; + this.localizationUtil = localizationUtil; + this.config = config; + } + + /** + * Sends sms notification to CBO based on action. + * @param musterRollRequest + */ + public void sendNotificationToCBO(MusterRollRequest musterRollRequest){ + String action = musterRollRequest.getWorkflow().getAction(); + if(action.equalsIgnoreCase(WF_SEND_BACK_TO_CBO_CODE) || action.equalsIgnoreCase(WF_APPROVE_CODE)) { + Map cboDetails = notificationUtil.getCBOContactPersonDetails(musterRollRequest); + String amount = notificationUtil.getExpenseAmount(musterRollRequest); + + String message = null; + String contactMobileNumber = cboDetails.get(CONTACT_MOBILE_NUMBER); + if (musterRollRequest.getWorkflow().getAction().equalsIgnoreCase(WF_SEND_BACK_TO_CBO_CODE)) { + message = getMessage(musterRollRequest, CBO_NOTIFICATION_FOR_CORRECTION_LOCALIZATION_CODE); + } else if (musterRollRequest.getWorkflow().getAction().equalsIgnoreCase(WF_APPROVE_CODE)) { + message = getMessage(musterRollRequest, CBO_NOTIFICATION_OF_APPROVAL_LOCALIZATION_CODE); + } + musterRollRequest.getMusterRoll().getMusterRollNumber(); + + String customizedMessage = buildMessageReplaceVariables(message, musterRollRequest.getMusterRoll().getMusterRollNumber(), amount); + SMSRequest smsRequest = SMSRequest.builder().mobileNumber(contactMobileNumber).message(customizedMessage).build(); + + musterRollProducer.push(config.getSmsNotificationTopic(), smsRequest); + } + } + + /** + * Gets the message from localization + * @param musterRollRequest + * @param msgCode + * @return + */ + public String getMessage(MusterRollRequest musterRollRequest, String msgCode){ + String tenantId = musterRollRequest.getMusterRoll().getTenantId(); + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + String locale = "en_IN"; + if(requestInfo.getMsgId().split("\\|").length > 1) + locale = requestInfo.getMsgId().split("\\|")[1]; + Map> localizedMessageMap = localizationUtil.getLocalisedMessages(requestInfo, tenantId, + locale, MUSTER_ROLL_MODULE_CODE); + return localizedMessageMap.get(locale + "|" + tenantId).get(msgCode); + } + + public String buildMessageReplaceVariables(String message, String musterRollName, String amount){ + message = message.replace("{musterrollID}", musterRollName) + .replace("{amount}", amount); + return message; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/service/WorkflowService.java b/health-services/muster-roll/src/main/java/org/egov/service/WorkflowService.java new file mode 100644 index 0000000000..37a95f01db --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/service/WorkflowService.java @@ -0,0 +1,187 @@ +package org.egov.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.contract.models.RequestInfoWrapper; +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.config.MusterRollServiceConfiguration; +import org.egov.repository.ServiceRequestRepository; +import org.egov.tracer.model.CustomException; +import org.egov.web.models.MusterRoll; +import org.egov.web.models.MusterRollRequest; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@Service +public class WorkflowService { + + private final MusterRollServiceConfiguration serviceConfiguration; + + private final ServiceRequestRepository repository; + + private final ObjectMapper mapper; + + @Autowired + public WorkflowService(MusterRollServiceConfiguration serviceConfiguration, ServiceRequestRepository repository, ObjectMapper mapper) { + this.serviceConfiguration = serviceConfiguration; + this.repository = repository; + this.mapper = mapper; + } + + /* Call the workflow service with the given action and update the status + * return the updated status of the application + * + */ + public String updateWorkflowStatus(MusterRollRequest musterRollRequest) { + ProcessInstance processInstance = getProcessInstanceForMusterRoll(musterRollRequest); + ProcessInstanceRequest workflowRequest = new ProcessInstanceRequest(musterRollRequest.getRequestInfo(), Collections.singletonList(processInstance)); + ProcessInstance processInstanceResponse = callWorkFlow(workflowRequest); + musterRollRequest.getMusterRoll().setMusterRollStatus(processInstanceResponse.getState().getState()); + //musterRollStatus + musterRollRequest.getMusterRoll().setStatus(Status.fromValue(processInstanceResponse.getState().getApplicationStatus())); + // Fetch currentProcessInstance from workflow process search for inbox config + musterRollRequest.getMusterRoll().setProcessInstance(processInstanceResponse); + return processInstanceResponse.getState().getApplicationStatus(); + } + + private ProcessInstance getProcessInstanceForMusterRoll(MusterRollRequest request) { + + MusterRoll musterRoll = request.getMusterRoll(); + Workflow workflow = request.getWorkflow(); + + ProcessInstance processInstance = new ProcessInstance(); + processInstance.setBusinessId(musterRoll.getMusterRollNumber()); + processInstance.setAction(request.getWorkflow().getAction()); + processInstance.setModuleName(serviceConfiguration.getMusterRollWFModuleName()); + processInstance.setTenantId(musterRoll.getTenantId()); + processInstance.setBusinessService(serviceConfiguration.getMusterRollWFBusinessService()); + processInstance.setComment(workflow.getComments()); + + if (!CollectionUtils.isEmpty(workflow.getAssignes())) { + List users = new ArrayList<>(); + + workflow.getAssignes().forEach(uuid -> { + User user = new User(); + user.setUuid(uuid); + users.add(user); + }); + + processInstance.setAssignes(users); + } + + if (!CollectionUtils.isEmpty(workflow.getDocuments())) { + processInstance.setDocuments(workflow.getDocuments()); + } + + return processInstance; + } + + /** + * Method to integrate with workflow + *

+ * take the ProcessInstanceRequest as paramerter to call wf-service + *

+ * and return wf-response to sets the resultant status + */ + private ProcessInstance callWorkFlow(ProcessInstanceRequest workflowReq) { + + ProcessInstanceResponse response = null; + StringBuilder url = new StringBuilder(serviceConfiguration.getWfHost().concat(serviceConfiguration.getWfTransitionPath())); + Object optional = repository.fetchResult(url, workflowReq); + response = mapper.convertValue(optional, ProcessInstanceResponse.class); + return response.getProcessInstances().get(0); + } + + /* + * Should return the applicable BusinessService for the given request + * + */ + public BusinessService getBusinessService(MusterRollRequest musterRollRequest) { + String tenantId = musterRollRequest.getMusterRoll().getTenantId(); + StringBuilder url = getSearchURLWithParams(tenantId, serviceConfiguration.getMusterRollWFBusinessService()); + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + Object result = repository.fetchResult(url, requestInfo); + BusinessServiceResponse response = null; + try { + response = mapper.convertValue(result, BusinessServiceResponse.class); + } catch (IllegalArgumentException e) { + throw new CustomException("PARSING ERROR", "Failed to parse response of workflow business service search"); + } + + if (CollectionUtils.isEmpty(response.getBusinessServices())) + throw new CustomException("BUSINESSSERVICE_DOESN'T_EXIST", "The businessService : " + serviceConfiguration.getMusterRollWFBusinessService() + " doesn't exist"); + + return response.getBusinessServices().get(0); + } + + /** + * Creates url for search based on given tenantId and businessservices + * + * @param tenantId The tenantId for which url is generated + * @param businessService The businessService for which url is generated + * @return The search url + */ + + private StringBuilder getSearchURLWithParams(String tenantId, String businessService) { + + StringBuilder url = new StringBuilder(serviceConfiguration.getWfHost()); + url.append(serviceConfiguration.getWfBusinessServiceSearchPath()); + url.append("?tenantId="); + url.append(tenantId); + url.append("&businessServices="); + url.append(businessService); + return url; + } + + /* + * Should return the applicable processInstance for the given request + * + */ + public ProcessInstance getProcessInstance(MusterRollRequest musterRollRequest) { + String tenantId = musterRollRequest.getMusterRoll().getTenantId(); + String businessId = musterRollRequest.getMusterRoll().getMusterRollNumber(); + StringBuilder url = getProcessSearchURLWithParams(tenantId, businessId); + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(musterRollRequest.getRequestInfo()).build(); + Object result = repository.fetchResult(url, requestInfoWrapper); + ProcessInstanceResponse response = null; + try { + response = mapper.convertValue(result, ProcessInstanceResponse.class); + } catch (IllegalArgumentException e) { + throw new CustomException("PARSING ERROR", "Failed to parse response of workflow business service search"); + } + + if (CollectionUtils.isEmpty(response.getProcessInstances())) + throw new CustomException("PROCESSINSTANCE_DOESN'T_EXIST", "The businessId : " + businessId + " doesn't exist"); + + return response.getProcessInstances().get(0); + } + + /** + * Creates url for search based on given tenantId and businessId + * + * @param tenantId The tenantId for which url is generated + * @param businessId The businessId for which url is generated + * @return The search url + */ + + private StringBuilder getProcessSearchURLWithParams(String tenantId, String businessId) { + + StringBuilder url = new StringBuilder(serviceConfiguration.getWfHost()); + url.append(serviceConfiguration.getWfProcessInstanceSearchPath()); + url.append("?tenantId="); + url.append(tenantId); + url.append("&businessIds="); + url.append(businessId); + url.append("&history=false"); + return url; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/util/IdgenUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/IdgenUtil.java new file mode 100644 index 0000000000..7e0967e841 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/IdgenUtil.java @@ -0,0 +1,56 @@ +package org.egov.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.contract.idgen.IdGenerationRequest; +import org.egov.common.contract.idgen.IdGenerationResponse; +import org.egov.common.contract.idgen.IdResponse; +import org.egov.common.contract.idgen.IdRequest; +import org.egov.common.contract.request.RequestInfo; +import org.egov.repository.ServiceRequestRepository; +import org.egov.tracer.model.CustomException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Component +public class IdgenUtil { + + @Value("${egov.idgen.host}") + private String idGenHost; + + @Value("${egov.idgen.path}") + private String idGenPath; + + private final ObjectMapper mapper; + + private final ServiceRequestRepository restRepo; + + @Autowired + public IdgenUtil(ObjectMapper mapper, ServiceRequestRepository restRepo) { + this.mapper = mapper; + this.restRepo = restRepo; + } + + public List getIdList(RequestInfo requestInfo, String tenantId, String idName, String idformat, Integer count) { + List reqList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + reqList.add(IdRequest.builder().idName(idName).format(idformat).tenantId(tenantId).build()); + } + + IdGenerationRequest request = IdGenerationRequest.builder().idRequests(reqList).requestInfo(requestInfo).build(); + StringBuilder uri = new StringBuilder(idGenHost).append(idGenPath); + IdGenerationResponse response = mapper.convertValue(restRepo.fetchResult(uri, request), IdGenerationResponse.class); + + List idResponses = response.getIdResponses(); + + if (CollectionUtils.isEmpty(idResponses)) + throw new CustomException("IDGEN ERROR", "No ids returned from idgen Service"); + + return idResponses.stream().map(IdResponse::getId).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/java/org/egov/util/LocalizationUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/LocalizationUtil.java new file mode 100644 index 0000000000..7b428fc1a4 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/LocalizationUtil.java @@ -0,0 +1,71 @@ +package org.egov.util; + +import com.jayway.jsonpath.JsonPath; +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.models.RequestInfoWrapper; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.repository.ServiceRequestRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.util.MusterRollServiceConstants.MUSTER_ROLL_MODULE_CODE; + +@Component +@Slf4j +public class LocalizationUtil { + + private final MusterRollServiceConfiguration config; + + private final ServiceRequestRepository restRepo; + + @Autowired + public LocalizationUtil(MusterRollServiceConfiguration config, ServiceRequestRepository restRepo) { + this.config = config; + this.restRepo = restRepo; + } + + + /** + * Creates a cache for localization that gets refreshed at every call. + * + * @param requestInfo + * @param rootTenantId + * @param locale + * @param module + * @return + */ + public Map> getLocalisedMessages(RequestInfo requestInfo, String rootTenantId, String locale, String module) { + Map> localizedMessageMap = new HashMap<>(); + Map mapOfCodesAndMessages = new HashMap<>(); + StringBuilder uri = new StringBuilder(); + RequestInfoWrapper requestInfoWrapper = new RequestInfoWrapper(); + requestInfoWrapper.setRequestInfo(requestInfo); + uri.append(config.getLocalizationServiceHost()) + .append(config.getLocalizationServiceEndpoint()).append("?tenantId=" + rootTenantId) + .append("&module=" + module).append("&locale=" + locale); + List codes = null; + List messages = null; + Object result = null; + try { + result = restRepo.fetchResult(uri, requestInfoWrapper); + codes = JsonPath.read(result, MusterRollServiceConstants.MUSTER_ROLL_LOCALIZATION_CODE_JSONPATH); + messages = JsonPath.read(result, MusterRollServiceConstants.MUSTER_ROLL_LOCALIZATION_MESSAGE_JSONPATH); + } catch (Exception e) { + log.error("Exception while fetching from localization: " + e); + } + if (null != result) { + for (int i = 0; i < codes.size(); i++) { + mapOfCodesAndMessages.put(codes.get(i), messages.get(i)); + } + localizedMessageMap.put(locale + "|" + rootTenantId, mapOfCodesAndMessages); + } + + return localizedMessageMap; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/util/MdmsUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/MdmsUtil.java new file mode 100644 index 0000000000..354aad7026 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/MdmsUtil.java @@ -0,0 +1,170 @@ +package org.egov.util; + +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.mdms.model.MasterDetail; +import org.egov.mdms.model.MdmsCriteria; +import org.egov.mdms.model.MdmsCriteriaReq; +import org.egov.mdms.model.ModuleDetail; +import org.egov.repository.ServiceRequestRepository; +import org.egov.web.models.MusterRollRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import static org.egov.util.MusterRollServiceConstants.*; + +@Slf4j +@Component +public class MdmsUtil { + + private final MusterRollServiceConfiguration config; + + private final ServiceRequestRepository serviceRequestRepository; + + @Autowired + public MdmsUtil(MusterRollServiceConfiguration config, ServiceRequestRepository serviceRequestRepository) { + this.config = config; + this.serviceRequestRepository = serviceRequestRepository; + } + + /** + * Calls MDMS service to fetch works master data + * + * @param request + * @param tenantId + * @return + */ + public Object mDMSCall(MusterRollRequest request, String tenantId) { + RequestInfo requestInfo = request.getRequestInfo(); + MdmsCriteriaReq mdmsCriteriaReq = getMDMSRequest(requestInfo, tenantId); + return serviceRequestRepository.fetchResult(getMdmsV2SearchUrl(), mdmsCriteriaReq); + } + + /** + * Calls MDMS service to fetch muster roll data + * + * @param request + * @param tenantId + * @return + */ + public Object mDMSCallMuster(MusterRollRequest request, String tenantId) { + RequestInfo requestInfo = request.getRequestInfo(); + MdmsCriteriaReq mdmsCriteriaReq = getMDMSRequestMuster(requestInfo, tenantId); + return serviceRequestRepository.fetchResult(getMdmsV2SearchUrl(), mdmsCriteriaReq); + } + + public Object mDMSV2CallMuster(MusterRollRequest request, String tenantId) { + RequestInfo requestInfo = request.getRequestInfo(); + MdmsCriteriaReq mdmsCriteriaReq = getMDMSRequestMusterV2(requestInfo, tenantId); + return serviceRequestRepository.fetchResult(getMdmsV2SearchUrl(), mdmsCriteriaReq); + } + + public MdmsCriteriaReq getMDMSRequestMusterV2(RequestInfo requestInfo, String tenantId) { + ModuleDetail musterRollModuleDetail = getMusterRollModuleRequestDataV2(); + + List moduleDetails = new LinkedList<>(); + moduleDetails.add(musterRollModuleDetail); + + MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId) + .build(); + return MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria) + .requestInfo(requestInfo).build(); + } + + /** + * Returns mdms search criteria based on the tenantId + * + * @param requestInfo + * @param tenantId + * @param request + * @return + */ + public MdmsCriteriaReq getMDMSRequest(RequestInfo requestInfo, String tenantId) { + + ModuleDetail tenantModuleDetail = getTenantModuleRequestData(); + + List moduleDetails = new LinkedList<>(); + moduleDetails.add(tenantModuleDetail); + + MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId) + .build(); + + return MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria) + .requestInfo(requestInfo).build(); + } + + /** + * Returns mdms search criteria based on the tenantId + * + * @param requestInfo + * @param tenantId + * @param request + * @return + */ + public MdmsCriteriaReq getMDMSRequestMuster(RequestInfo requestInfo, String tenantId) { + + ModuleDetail musterRollModuleDetail = getMusterRollModuleRequestData(); + + List moduleDetails = new LinkedList<>(); + moduleDetails.add(musterRollModuleDetail); + + MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId) + .build(); + + return MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria) + .requestInfo(requestInfo).build(); + } + + private ModuleDetail getTenantModuleRequestData() { + List musterRollTenantMasterDetails = new ArrayList<>(); + + MasterDetail tenantMasterDetails = MasterDetail.builder().name(MASTER_TENANTS) + .filter(FILTER_CODE).build(); + + musterRollTenantMasterDetails.add(tenantMasterDetails); + + return ModuleDetail.builder().masterDetails(musterRollTenantMasterDetails) + .moduleName(MDMS_TENANT_MODULE_NAME).build(); + } + + private ModuleDetail getMusterRollModuleRequestData() { + + List musterRollMasterDetails = new ArrayList<>(); + + MasterDetail musterAttendanceMasterDetails = MasterDetail.builder().name(MASTER_MUSTER_ROLL).build(); + musterRollMasterDetails.add(musterAttendanceMasterDetails); + + MasterDetail musterWageSeekerSkillsMasterDetails = MasterDetail.builder().name(MASTER_WAGER_SEEKER_SKILLS).build(); + musterRollMasterDetails.add(musterWageSeekerSkillsMasterDetails); + + return ModuleDetail.builder().masterDetails(musterRollMasterDetails) + .moduleName(MDMS_COMMON_MASTERS_MODULE_NAME).build(); + } + + private ModuleDetail getMusterRollModuleRequestDataV2() { + + List musterRollMasterDetails = new ArrayList<>(); + MasterDetail musterWageSeekerSkillMasterDetails = MasterDetail.builder().name("SOR").filter("[?(@.sorType=='L')]").build(); + musterRollMasterDetails.add(musterWageSeekerSkillMasterDetails); + return ModuleDetail.builder().masterDetails(musterRollMasterDetails) + .moduleName("WORKS-SOR").build(); + } + + /** + * Returns the url for mdms search endpoint + * + * @return url for mdms search endpoint + */ + public StringBuilder getMdmsSearchUrl() { + return new StringBuilder().append(config.getMdmsHost()).append(config.getMdmsEndPoint()); + } + public StringBuilder getMdmsV2SearchUrl() { + return new StringBuilder().append(config.getMdmsV2Host()).append(config.getMdmsV2EndPoint()); + } + +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/java/org/egov/util/MusterRollServiceConstants.java b/health-services/muster-roll/src/main/java/org/egov/util/MusterRollServiceConstants.java new file mode 100644 index 0000000000..f35a0da604 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/MusterRollServiceConstants.java @@ -0,0 +1,37 @@ +package org.egov.util; + +public class MusterRollServiceConstants { + + public static final String ACTION_REJECT = "REJECT"; + public static final String MASTER_TENANTS = "tenants"; + public static final String FILTER_CODE = "$.*.code"; + public static final String MDMS_TENANT_MODULE_NAME = "tenant"; + public static final String MDMS_COMMON_MASTERS_MODULE_NAME = "common-masters"; + public static final String MASTER_MUSTER_ROLL = "MusterRoll"; + public static final String MASTER_WAGER_SEEKER_SKILLS = "WageSeekerSkills"; + public static final String HALF_DAY_NUM_HOURS = "HALF_DAY_NUM_HOURS"; + public static final String FULL_DAY_NUM_HOURS = "FULL_DAY_NUM_HOURS"; + public static final String ROUND_OFF_HOURS = "ROUND_OFF_HOURS"; + public static final String ENTRY_EVENT = "ENTRY"; + public static final String EXIT_EVENT = "EXIT"; + public static final String STATUS_APPROVED = "APPROVED"; + public static final String TENANT_ID = "tenantId"; + public static final String CONTRACT_NUMBER = "contractNumber"; + public static final String REQUEST_INFO = "RequestInfo"; + public static final String ORG_ID_PATH = "$.contracts.*.orgId"; + public static final String ID = "id"; + public static final String ORG_NAME_PATH = "$.organisations.*.name"; + public static final String CONTACT_NAME_PATH = "$.organisations.*.contactDetails.*.contactName"; + public static final String CONTACT_MOBILE_NUMBER_PATH = "$.organisations.*.contactDetails.*.contactMobileNumber"; + public static final String ORG_NAME = "orgName"; + public static final String CONTACT_NAME = "contactName"; + public static final String CONTACT_MOBILE_NUMBER = "contactMobileNumber"; + public static final String MUSTER_ROLL_MODULE_CODE = "rainmaker-common-masters"; + public static final String SEARCH_CRITERIA = "SearchCriteria"; + public static final String MUSTER_ROLL_LOCALIZATION_CODE_JSONPATH = "$.messages.*.code"; + public static final String MUSTER_ROLL_LOCALIZATION_MESSAGE_JSONPATH = "$.messages.*.message"; + public static final String WF_SEND_BACK_TO_CBO_CODE = "SENDBACKTOCBO"; + public static final String WF_APPROVE_CODE = "APPROVE"; + public static final String CBO_NOTIFICATION_FOR_CORRECTION_LOCALIZATION_CODE = "MUSTER_ROLL_CBO_NOTIFICATION_FOR_CORRECTION"; + public static final String CBO_NOTIFICATION_OF_APPROVAL_LOCALIZATION_CODE = "MUSTER_ROLL_CBO_NOTIFICATION_OF_APPROVAL"; +} diff --git a/health-services/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java new file mode 100644 index 0000000000..0d150e8e43 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java @@ -0,0 +1,269 @@ +package org.egov.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jayway.jsonpath.JsonPath; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.models.RequestInfoWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.models.individual.Identifier; +import org.egov.common.models.individual.Individual; +import org.egov.common.models.individual.Skill; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.tracer.model.CustomException; +import org.egov.web.models.*; +import org.egov.works.services.common.models.bankaccounts.BankAccount; +import org.egov.works.services.common.models.bankaccounts.BankAccountDetails; +import org.egov.works.services.common.models.musterroll.Status; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import static org.egov.util.MusterRollServiceConstants.*; + +@Component +@Slf4j +public class MusterRollServiceUtil { + + private final ObjectMapper mapper; + + private final RestTemplate restTemplate; + + private final MusterRollServiceConfiguration config; + private static final String SKILL_CODE = "skillCode"; + + @Autowired + public MusterRollServiceUtil(ObjectMapper mapper, RestTemplate restTemplate, MusterRollServiceConfiguration config) { + this.mapper = mapper; + this.restTemplate = restTemplate; + this.config = config; + } + + /** + * Method to return auditDetails for create/update flows + * + * @param by + * @param isCreate + * @return AuditDetails + */ + public AuditDetails getAuditDetails(String by, MusterRoll musterRoll, Boolean isCreate) { + Long time = System.currentTimeMillis(); + if (Boolean.TRUE.equals(isCreate)) + return AuditDetails.builder().createdBy(by).lastModifiedBy(by).createdTime(time).lastModifiedTime(time) + .build(); + else + return AuditDetails.builder().createdBy(musterRoll.getAuditDetails().getCreatedBy()).lastModifiedBy(by) + .createdTime(musterRoll.getAuditDetails().getCreatedTime()).lastModifiedTime(time).build(); + } + + /** + * Fetch the individual skill level from MDMS + * + * @param mdmsData + * @param individualEntry + * @param skillCode + * + */ + public void populateAdditionalDetails(Object mdmsData, IndividualEntry individualEntry, String skillCode, + Individual matchedIndividual, BankAccount bankAccount, boolean isCreate) { + final String jsonPathForWorksMuster = "$.MdmsRes." + "WORKS-SOR" + "." + + "SOR" + ".*"; + List> musterRes = null; + + try { + musterRes = JsonPath.read(mdmsData, jsonPathForWorksMuster); + + } catch (Exception e) { + log.error(e.getMessage()); + throw new CustomException("MusterRollServiceUtil::populateAdditionalDetails::JSONPATH_ERROR", + "Failed to parse mdms response"); + } + + String skillValue = ""; + if (skillCode != null && !CollectionUtils.isEmpty(musterRes)) { + for (LinkedHashMap codeValueMap : musterRes) { + if (codeValueMap.get("id").equalsIgnoreCase(skillCode)) { + skillValue = codeValueMap.get("name"); + break; + } + } + } + + // populate individual details for estimate and create + log.info("MusterRollServiceUtil::populateAdditionalDetails::start"); + JSONObject additionalDetails = new JSONObject(); + + additionalDetails.put("userId", matchedIndividual.getIndividualId()); + additionalDetails.put("userName", matchedIndividual.getName().getGivenName()); + additionalDetails.put("fatherName", matchedIndividual.getFatherName()); + additionalDetails.put("mobileNo", matchedIndividual.getMobileNumber()); + additionalDetails.put("gender",matchedIndividual.getGender()); + Identifier aadhaar = matchedIndividual.getIdentifiers().stream() + .filter(identifier -> identifier.getIdentifierType().contains("AADHAAR")).findFirst().orElse(null); + if (aadhaar != null) { + additionalDetails.put("aadharNumber", aadhaar.getIdentifierId()); + } + + // populate individual's skill details in create and update (user selected skill + // will be set in additionalDetails) + if (isCreate) { + additionalDetails.put(SKILL_CODE, skillCode); + additionalDetails.put("skillValue", skillValue); + } + + // populate list of skills of the individual in estimate additionalDetails + populateSkillsInEstimateDetails(isCreate,matchedIndividual,additionalDetails); + + if (bankAccount != null) { + List bankAccountDetails = bankAccount.getBankAccountDetails(); + if (!CollectionUtils.isEmpty(bankAccountDetails)) { + String accountNumber = bankAccountDetails.get(0).getAccountNumber(); + String ifscCode = bankAccountDetails.get(0).getBankBranchIdentifier().getCode(); + String accountHolderName = bankAccountDetails.get(0).getAccountHolderName(); + String accountType = bankAccountDetails.get(0).getAccountType(); + additionalDetails.put("bankDetails", accountNumber + "-" + ifscCode); + additionalDetails.put("accountHolderName", accountHolderName); + additionalDetails.put("accountType", accountType); + } + } + + try { + individualEntry.setAdditionalDetails(mapper.readValue(additionalDetails.toString(), Object.class)); + } catch (IOException e) { + throw new CustomException("MusterRollServiceUtil::populateAdditionalDetails::PARSING ERROR", + "Failed to set additionalDetail object"); + } + + } + private void populateSkillsInEstimateDetails(boolean isCreate,Individual matchedIndividual,JSONObject additionalDetails){ + if (!isCreate && !CollectionUtils.isEmpty(matchedIndividual.getSkills())) { + List skillList = new ArrayList<>(); + for (Skill skill : matchedIndividual.getSkills()) { + skillList.add(skill.getLevel()); + } + additionalDetails.put(SKILL_CODE, skillList); + } + } + + public void updateAdditionalDetails(Object mdmsData, IndividualEntry individualEntry, String skillCode) { + final String jsonPathForWorksMuster = "$.MdmsRes." + "WORKS-SOR" + "." + + "SOR" + ".*"; + List> musterRes = null; + + try { + musterRes = JsonPath.read(mdmsData, jsonPathForWorksMuster); + + } catch (Exception e) { + log.error(e.getMessage()); + throw new CustomException("MusterRollServiceUtil::updateAdditionalDetails::JSONPATH_ERROR", + "Failed to parse mdms response"); + } + + String skillValue = ""; + if (skillCode != null && !CollectionUtils.isEmpty(musterRes)) { + for (LinkedHashMap codeValueMap : musterRes) { + if (codeValueMap.get("id").equalsIgnoreCase(skillCode)) { + skillValue = codeValueMap.get("name"); + break; + } + } + } + + try { + JsonNode node = mapper.readTree(mapper.writeValueAsString(individualEntry.getAdditionalDetails())); + ((ObjectNode) node).put(SKILL_CODE, skillCode); + ((ObjectNode) node).put("skillValue", skillValue); + individualEntry.setAdditionalDetails(mapper.readValue(node.toString(), Object.class)); + + } catch (IOException e) { + log.info( + "MusterRollServiceUtil::updateAdditionalDetails::Failed to parse additionalDetail object from request" + + e); + throw new CustomException("PARSING ERROR", + "Failed to parse additionalDetail object from request on update"); + } + + } + + /** + * Sets the attendanceLogId in additionalDetails of the attendanceEntry + * + * @param attendanceEntry + * @param entryAttendanceLogId + * @param exitAttendanceLogId + */ + public void populateAdditionalDetailsAttendanceEntry(AttendanceEntry attendanceEntry, String entryAttendanceLogId, + String exitAttendanceLogId) { + JSONObject additionalDetails = new JSONObject(); + additionalDetails.put("entryAttendanceLogId", entryAttendanceLogId); + additionalDetails.put("exitAttendanceLogId", exitAttendanceLogId); + try { + attendanceEntry.setAdditionalDetails(mapper.readValue(additionalDetails.toString(), Object.class)); + } catch (IOException e) { + throw new CustomException("MusterRollServiceUtil::populateAdditionalDetailsAttendanceEntry::PARSING ERROR", + "Failed to set additionalDetail object"); + } + } + + /** + * Checks if the search is based only on tenantId + * + * @param searchCriteria + * @return + */ + public boolean isTenantBasedSearch(MusterRollSearchCriteria searchCriteria) { + return (searchCriteria.getIds() == null || searchCriteria.getIds().isEmpty()) + && StringUtils.isBlank(searchCriteria.getMusterRollNumber()) + && StringUtils.isBlank(searchCriteria.getRegisterId()) && searchCriteria.getFromDate() == null + && searchCriteria.getToDate() == null && searchCriteria.getStatus() == null + && StringUtils.isBlank(searchCriteria.getMusterRollStatus()) + && StringUtils.isNotBlank(searchCriteria.getTenantId()); + } + + public AttendanceRegisterResponse fetchAttendanceRegister(MusterRoll musterRoll, RequestInfo requestInfo) { + log.info("MusterRollValidator::Fetching attendance register with tenantId::" + musterRoll.getTenantId() + + " and register ID: " + musterRoll.getRegisterId()); + String id = requestInfo.getUserInfo().getUuid(); + + StringBuilder uri = new StringBuilder(); + uri.append(config.getAttendanceLogHost()).append(config.getAttendanceRegisterEndpoint()); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(uri.toString()) + .queryParam("tenantId", musterRoll.getTenantId()).queryParam("ids", musterRoll.getRegisterId()) + .queryParam("status", Status.ACTIVE); + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + + AttendanceRegisterResponse attendanceRegisterResponse = null; + + try { + attendanceRegisterResponse = restTemplate.postForObject(uriBuilder.toUriString(), requestInfoWrapper, + AttendanceRegisterResponse.class); + } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { + log.error("MusterRollValidator::Error thrown from attendance register service::" + + httpClientOrServerExc.getStatusCode()); + throw new CustomException("ATTENDANCE_REGISTER_SERVICE_EXCEPTION", + "Error thrown from attendance register service::" + httpClientOrServerExc.getStatusCode()); + } + + if (attendanceRegisterResponse == null + || CollectionUtils.isEmpty(attendanceRegisterResponse.getAttendanceRegister())) { + log.error("MusterRollValidator::User with id::" + id + " is not enrolled in the attendance register::" + + musterRoll.getRegisterId()); + throw new CustomException("ACCESS_EXCEPTION", + "User is not enrolled in the attendance register and not authorized to fetch it"); + } + return attendanceRegisterResponse; + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/util/NotificationUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/NotificationUtil.java new file mode 100644 index 0000000000..1afa015b48 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/NotificationUtil.java @@ -0,0 +1,163 @@ +package org.egov.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jayway.jsonpath.JsonPath; +import lombok.extern.slf4j.Slf4j; +import net.minidev.json.JSONArray; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.repository.ServiceRequestRepository; +import org.egov.tracer.model.CustomException; +import org.egov.web.models.MusterRollRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.util.MusterRollServiceConstants.*; + +@Component +@Slf4j +public class NotificationUtil { + + private final MusterRollServiceConfiguration config; + + private final ServiceRequestRepository restRepo; + + private final ObjectMapper mapper; + + @Autowired + public NotificationUtil(MusterRollServiceConfiguration config, ServiceRequestRepository restRepo, ObjectMapper mapper) { + this.config = config; + this.restRepo = restRepo; + this.mapper = mapper; + } + + public Map getCBOContactPersonDetails(MusterRollRequest musterRollRequest){ + String orgId = fetchOrgId(musterRollRequest); + return fetchCBODetails(musterRollRequest, orgId); + } + public String fetchOrgId(MusterRollRequest musterRollRequest){ + StringBuilder url = getOrgIdWithContractIdUrl(); + Object contractSearchRequest = getOrgIdWithContractIdRequest(musterRollRequest); + final Object contractRes = restRepo.fetchResult(url, contractSearchRequest); + String orgId; + try{ + JSONArray jsonArray = JsonPath.read(contractRes, ORG_ID_PATH); + orgId = jsonArray.get(0).toString(); + }catch (Exception e){ + throw new CustomException("PARSING_CONTRACT_ERROR", "Failed to parse response from contract"); + } + + return orgId; + } + + private StringBuilder getOrgIdWithContractIdUrl(){ + StringBuilder builder = new StringBuilder(config.getContractServiceHost()); + builder.append(config.getContractServiceEndpoint()); + return builder; + } + + private Object getOrgIdWithContractIdRequest(MusterRollRequest musterRollRequest){ + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + String contractNumber = musterRollRequest.getMusterRoll().getReferenceId(); + String tenantId = musterRollRequest.getMusterRoll().getTenantId(); + + // Create request object + ObjectNode contractSearchCriteriaNode = mapper.createObjectNode(); + + contractSearchCriteriaNode.putPOJO(REQUEST_INFO, requestInfo); + contractSearchCriteriaNode.put(CONTRACT_NUMBER, contractNumber); + contractSearchCriteriaNode.put(TENANT_ID, tenantId); + + return contractSearchCriteriaNode; + } + + private Map fetchCBODetails(MusterRollRequest musterRollRequest, String orgId){ + StringBuilder url = getCBODetailsFromOrgUrl(); + Object orgSearchRequest = getCBODetailsRequest(musterRollRequest, orgId); + + log.info("Organisation search request -> {}", orgSearchRequest); + Object orgRes = restRepo.fetchResult(url, orgSearchRequest); + + Map orgDetails = new HashMap<>(); + List orgName = null; + List contactName = null; + List contactMobileNumber = null; + + try{ + orgName = JsonPath.read(orgRes, ORG_NAME_PATH); + contactName = JsonPath.read(orgRes, CONTACT_NAME_PATH); + contactMobileNumber = JsonPath.read(orgRes, CONTACT_MOBILE_NUMBER_PATH); + } catch (Exception e){ + throw new CustomException("PARSING_ORG_ERROR", "Failed to parse response from organisation"); + } + + orgDetails.put(ORG_NAME, orgName.get(0)); + orgDetails.put(CONTACT_NAME, contactName.get(0)); + orgDetails.put(CONTACT_MOBILE_NUMBER,contactMobileNumber.get(0)); + + return orgDetails; + } + + private StringBuilder getCBODetailsFromOrgUrl(){ + StringBuilder builder = new StringBuilder(config.getOrganisationServiceHost()); + builder.append(config.getOrganisationServiceEndpoint()); + return builder; + } + + private Object getCBODetailsRequest(MusterRollRequest musterRollRequest, String orgId){ + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + String tenantId = musterRollRequest.getMusterRoll().getTenantId(); + + ObjectNode orgSearchRequestNode = mapper.createObjectNode(); + ArrayNode ids = mapper.createArrayNode(); + ids.add(orgId); + + ObjectNode orgObjNode = mapper.createObjectNode(); + orgObjNode.put(TENANT_ID, tenantId); + orgObjNode.putPOJO(ID, ids); + + orgSearchRequestNode.putPOJO(REQUEST_INFO, requestInfo); + orgSearchRequestNode.putPOJO(SEARCH_CRITERIA,orgObjNode); + + return orgSearchRequestNode; + } + + public String getExpenseAmount(MusterRollRequest musterRollRequest){ + StringBuilder url = getExpenseUrl(); + Object expenseSearchRequest = getExpenseRequest(musterRollRequest); + final Object expenseRes = restRepo.fetchResult(url, expenseSearchRequest); + Integer amount = null; + try { + amount = JsonPath.read(expenseRes, "$.calculation.totalAmount"); + }catch (Exception e){ + throw new CustomException("EXPENSE_PARSING_ERROR", "Error while parsing expense object"); + } + return amount.toString(); + } + public StringBuilder getExpenseUrl(){ + return new StringBuilder(config.getExpenseCalculatorServiceHost()) + .append(config.getExpenseCalculatorServiceEndpoint()); + } + + public Object getExpenseRequest(MusterRollRequest musterRollRequest){ + ObjectNode expenseRequestObjNode = mapper.createObjectNode(); + ArrayNode musterRollIds = mapper.createArrayNode(); + ObjectNode criteriaObjNode = mapper.createObjectNode(); + + musterRollIds.add(musterRollRequest.getMusterRoll().getId()); + + criteriaObjNode.putPOJO("musterRollId", musterRollIds); + criteriaObjNode.put("tenantId", musterRollRequest.getMusterRoll().getTenantId()); + + expenseRequestObjNode.putPOJO(REQUEST_INFO, musterRollRequest.getRequestInfo()); + expenseRequestObjNode.putPOJO("criteria", criteriaObjNode); + return expenseRequestObjNode; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/util/ResponseInfoCreator.java b/health-services/muster-roll/src/main/java/org/egov/util/ResponseInfoCreator.java new file mode 100644 index 0000000000..e5f3751ce6 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/ResponseInfoCreator.java @@ -0,0 +1,26 @@ +package org.egov.util; + +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class ResponseInfoCreator { + + 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 resMsgId = "uief87324"; // TODO : Hard-coded + final String msgId = requestInfo != null ? requestInfo.getMsgId() : ""; + final String responseStatus = Boolean.TRUE.equals(success) ? "successful" : "failed"; + + return ResponseInfo.builder().apiId(apiId).ver(ver).ts(ts).resMsgId(resMsgId).msgId(msgId).resMsgId(resMsgId) + .status(responseStatus).build(); + } +} diff --git a/health-services/muster-roll/src/main/java/org/egov/util/UrlShortenerUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/UrlShortenerUtil.java new file mode 100644 index 0000000000..66a1080de0 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/UrlShortenerUtil.java @@ -0,0 +1,44 @@ +package org.egov.util; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; + +@Slf4j +@Component +public class UrlShortenerUtil { + + private final RestTemplate restTemplate; + + @Value("${egov.url.shortner.host}") + private String urlShortnerHost; + + @Value("${egov.url.shortner.endpoint}") + private String urShortnerPath; + + @Autowired + public UrlShortenerUtil(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public String getShortenedUrl(String url) { + + HashMap body = new HashMap<>(); + body.put("url", url); + StringBuilder builder = new StringBuilder(urlShortnerHost); + builder.append(urShortnerPath); + String res = restTemplate.postForObject(builder.toString(), body, String.class); + + if (StringUtils.isEmpty(res)) { + log.error("URL_SHORTENING_ERROR", "Unable to shorten url: " + url); + return url; + } else return res; + } + + +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/java/org/egov/util/UserUtil.java b/health-services/muster-roll/src/main/java/org/egov/util/UserUtil.java new file mode 100644 index 0000000000..ee51c870e8 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/util/UserUtil.java @@ -0,0 +1,143 @@ +package org.egov.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.contract.request.Role; +import org.egov.common.contract.request.User; +import org.egov.common.contract.user.UserDetailResponse; +import org.egov.repository.ServiceRequestRepository; +import org.egov.tracer.model.CustomException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; + +@Component +public class UserUtil { + + + private ObjectMapper mapper; + + private ServiceRequestRepository serviceRequestRepository; + + @Value("${egov.user.create.path}") + private String userCreateEndpoint; + + @Value("${egov.user.search.path}") + private String userSearchEndpoint; + + @Value("${egov.user.update.path}") + private String userUpdateEndpoint; + private static final String LAST_MODIFIED_DATE = "lastModifiedDate"; + private static final String PWD_EXPIRY_DATE = "pwdExpiryDate"; + + @Autowired + public UserUtil(ObjectMapper mapper, ServiceRequestRepository serviceRequestRepository) { + this.mapper = mapper; + this.serviceRequestRepository = serviceRequestRepository; + } + + /** + * Returns UserDetailResponse by calling user service with given uri and object + * + * @param userRequest Request object for user service + * @param uri The address of the endpoint + * @return Response from user service as parsed as userDetailResponse + */ + + public UserDetailResponse userCall(Object userRequest, StringBuilder uri) { + String dobFormat = null; + if (uri.toString().contains(userSearchEndpoint) || uri.toString().contains(userUpdateEndpoint)) + dobFormat = "yyyy-MM-dd"; + else if (uri.toString().contains(userCreateEndpoint)) + dobFormat = "dd/MM/yyyy"; + try { + LinkedHashMap responseMap = (LinkedHashMap) serviceRequestRepository.fetchResult(uri, userRequest); + parseResponse(responseMap, dobFormat); + return mapper.convertValue(responseMap, UserDetailResponse.class); + } catch (IllegalArgumentException e) { + throw new CustomException("IllegalArgumentException", "ObjectMapper not able to convertValue in userCall"); + } + } + + + /** + * Parses date formats to long for all users in responseMap + * + * @param responeMap LinkedHashMap got from user api response + */ + + public void parseResponse(LinkedHashMap responeMap, String dobFormat) { + List users = (List) responeMap.get("user"); + String format1 = "dd-MM-yyyy HH:mm:ss"; + if (users != null) { + users.forEach(map -> { + map.put("createdDate", dateTolong((String) map.get("createdDate"), format1)); + if ((String) map.get(LAST_MODIFIED_DATE) != null) + map.put(LAST_MODIFIED_DATE, dateTolong((String) map.get(LAST_MODIFIED_DATE), format1)); + if ((String) map.get("dob") != null) + map.put("dob", dateTolong((String) map.get("dob"), dobFormat)); + if ((String) map.get(PWD_EXPIRY_DATE) != null) + map.put(PWD_EXPIRY_DATE, dateTolong((String) map.get(PWD_EXPIRY_DATE), format1)); + } + ); + } + } + + /** + * Converts date to long + * + * @param date date to be parsed + * @param format Format of the date + * @return Long value of date + */ + private Long dateTolong(String date, String format) { + SimpleDateFormat f = new SimpleDateFormat(format); + Date d = null; + try { + d = f.parse(date); + } catch (ParseException e) { + throw new CustomException("INVALID_DATE_FORMAT", "Failed to parse date format in user"); + } + return d.getTime(); + } + + /** + * enriches the userInfo with statelevel tenantId and other fields + * + * @param mobileNumber + * @param tenantId + * @param userInfo + */ + public void addUserDefaultFields(String mobileNumber, String tenantId, User userInfo) { + Role role = getCitizenRole(tenantId); + userInfo.setRoles(Collections.singletonList(role)); + userInfo.setType("CITIZEN"); + userInfo.setUserName(mobileNumber); + userInfo.setTenantId(getStateLevelTenant(tenantId)); + } + + /** + * Returns role object for citizen + * + * @param tenantId + * @return + */ + private Role getCitizenRole(String tenantId) { + Role role = new Role(); + role.setCode("CITIZEN"); + role.setName("Citizen"); + role.setTenantId(getStateLevelTenant(tenantId)); + return role; + } + + public String getStateLevelTenant(String tenantId) { + return tenantId.split("\\.")[0]; + } + +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/java/org/egov/validator/MusterRollValidator.java b/health-services/muster-roll/src/main/java/org/egov/validator/MusterRollValidator.java new file mode 100644 index 0000000000..8a5d84e45e --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/validator/MusterRollValidator.java @@ -0,0 +1,299 @@ +package org.egov.validator; + +import com.jayway.jsonpath.JsonPath; +import org.egov.common.contract.models.RequestInfoWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.egov.common.contract.models.Workflow; +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.tracer.model.CustomException; +import org.egov.util.MdmsUtil; +import org.egov.web.models.*; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.math.BigDecimal; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.util.MusterRollServiceConstants.*; + +@Component +@Slf4j +public class MusterRollValidator { + + private final MusterRollServiceConfiguration serviceConfiguration; + + private final MdmsUtil mdmsUtils; + + private final RestTemplate restTemplate; + + private static final String TENANT_ID = "TENANT_ID"; + private static final String MUSTER_ROLL = "MUSTER_ROLL"; + private static final String MUSTER_ROLL_IS_MANADATORY = "Muster roll is mandatory"; + private static final String TENANT_ID_IS_MANADATORY = "TenantId is mandatory"; + + @Autowired + public MusterRollValidator(MusterRollServiceConfiguration serviceConfiguration, MdmsUtil mdmsUtils, RestTemplate restTemplate) { + this.serviceConfiguration = serviceConfiguration; + this.mdmsUtils = mdmsUtils; + this.restTemplate = restTemplate; + } + + /** + * Validate muster roll in estimate service + * @param musterRollRequest + */ + public void validateEstimateMusterRoll(MusterRollRequest musterRollRequest){ + log.info("MusterRollValidator::validateEstimateMusterRoll"); + + Map errorMap = new HashMap<>(); + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + + validateRequestInfo(requestInfo); + validateEstimateMusterRollRequest(musterRoll); + + //split the tenantId and validate tenantId + String tenantId = musterRoll.getTenantId(); + Object mdmsData = mdmsUtils.mDMSCall(musterRollRequest, tenantId); + validateMDMSData(musterRoll, mdmsData, errorMap); + + if (!errorMap.isEmpty()){ + throw new CustomException(errorMap); + } + + } + + /** + * Validate muster roll in create service + * @param musterRollRequest + */ + public void validateCreateMusterRoll(MusterRollRequest musterRollRequest) { + log.info("MusterRollValidator::validateCreateMusterRoll"); + + Map errorMap = new HashMap<>(); + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + Workflow workflow = musterRollRequest.getWorkflow(); + + validateRequestInfo(requestInfo); + validateCreateMusterRollRequest(musterRoll); + validateWorkFlow(workflow, errorMap); + + //split the tenantId and validate tenantId + String tenantId = musterRoll.getTenantId(); + Object mdmsData = mdmsUtils.mDMSCall(musterRollRequest, tenantId); + validateMDMSData(musterRoll, mdmsData, errorMap); + + //check if the user is enrolled in the attendance register + isValidUser(musterRoll, requestInfo); + + if (!errorMap.isEmpty()){ + throw new CustomException(errorMap); + } + + } + + /** + * Validate muster roll in update service + * @param musterRollRequest + */ + public void validateUpdateMusterRoll(MusterRollRequest musterRollRequest) { + log.info("MusterRollValidator::validateUpdateMusterRoll"); + + Map errorMap = new HashMap<>(); + MusterRoll musterRoll = musterRollRequest.getMusterRoll(); + RequestInfo requestInfo = musterRollRequest.getRequestInfo(); + Workflow workflow = musterRollRequest.getWorkflow(); + + validateRequestInfo(requestInfo); + validateWorkFlow(workflow, errorMap); + validateUpdateMusterRollRequest(musterRoll); + + //split the tenantId and validate tenantId + String tenantId = musterRoll.getTenantId(); + Object mdmsData = mdmsUtils.mDMSCall(musterRollRequest, tenantId); + validateMDMSData(musterRoll, mdmsData, errorMap); + + if (!errorMap.isEmpty()){ + throw new CustomException(errorMap); + } + + } + + /** + * Validate muster roll in search service + * @param requestInfoWrapper + * @param searchCriteria + */ + public void validateSearchMuster(RequestInfoWrapper requestInfoWrapper, MusterRollSearchCriteria searchCriteria) { + log.info("MusterRollValidator::validateSearchMuster"); + + if (searchCriteria == null || requestInfoWrapper == null || requestInfoWrapper.getRequestInfo() == null) { + throw new CustomException("MUSTER_ROLL_SEARCH_CRITERIA_REQUEST", "Muster roll search criteria request is mandatory"); + } + if (StringUtils.isBlank(searchCriteria.getTenantId())) { + throw new CustomException(TENANT_ID, "Tenant is mandatory"); + } + } + + + private void validateRequestInfo(RequestInfo requestInfo) { + if (requestInfo == null) { + throw new CustomException("REQUEST_INFO", "Request info is mandatory"); + } + if (requestInfo.getUserInfo() == null) { + throw new CustomException("USERINFO", "UserInfo is mandatory"); + } + if (requestInfo.getUserInfo() != null && StringUtils.isBlank(requestInfo.getUserInfo().getUuid())) { + throw new CustomException("USERINFO_UUID", "UUID is mandatory"); + } + } + + private void validateCreateMusterRollRequest(MusterRoll musterRoll) { + if (musterRoll == null) { + throw new CustomException(MUSTER_ROLL,MUSTER_ROLL_IS_MANADATORY); + } + if (musterRoll.getTenantId() == null) { + throw new CustomException(TENANT_ID,TENANT_ID_IS_MANADATORY); + } + if (musterRoll.getRegisterId() == null) { + throw new CustomException("REGISTER_ID","RegisterId is mandatory"); + } + if (musterRoll.getStartDate() == null) { + throw new CustomException("START_DATE_EMPTY","StartDate is mandatory"); + } + + //Check if the startDate is Monday - UI sends the epoch time in IST + LocalDate startDate = Instant.ofEpochMilli(musterRoll.getStartDate().longValue()).atZone(ZoneId.of(serviceConfiguration.getTimeZone())).toLocalDate(); + if (startDate.getDayOfWeek() != DayOfWeek.MONDAY) { + throw new CustomException("START_DATE_MONDAY","StartDate should be Monday"); + } + musterRoll.setStartDate(new BigDecimal(startDate.atStartOfDay(ZoneId.of(serviceConfiguration.getTimeZone())).toInstant().toEpochMilli())); + + log.info("MusterRollValidator::validateCreateMusterRoll::startDate in epoch::"+musterRoll.getStartDate()); + log.info("MusterRollValidator::validateCreateMusterRoll::startDate::"+startDate); + log.info("MusterRollValidator::validateCreateMusterRoll::endDate in epoch from request::"+musterRoll.getEndDate()); + + //Override the endDate as SUNDAY + LocalDate endDate = startDate.plusDays(6); + log.info("MusterRollValidator::validateCreateMusterRoll:: calculated endDate::"+endDate); + musterRoll.setEndDate(new BigDecimal(endDate.atStartOfDay(ZoneId.of(serviceConfiguration.getTimeZone())).toInstant().toEpochMilli())); + + } + + private void validateUpdateMusterRollRequest(MusterRoll musterRoll) { + if (musterRoll == null) { + throw new CustomException(MUSTER_ROLL,MUSTER_ROLL_IS_MANADATORY); + } + if (musterRoll.getTenantId() == null) { + throw new CustomException(TENANT_ID,TENANT_ID_IS_MANADATORY); + } + if (musterRoll.getId() == null) { + throw new CustomException("MUSTER_ROLL_ID","MusterRollId is mandatory"); + } + + } + + private void validateEstimateMusterRollRequest(MusterRoll musterRoll) { + if (musterRoll == null) { + throw new CustomException(MUSTER_ROLL,MUSTER_ROLL_IS_MANADATORY); + } + if (musterRoll.getTenantId() == null) { + throw new CustomException(TENANT_ID,TENANT_ID_IS_MANADATORY); + } + if (musterRoll.getRegisterId() == null) { + throw new CustomException("REGISTER_ID","RegisterId is mandatory"); + } + if (musterRoll.getStartDate() == null) { + throw new CustomException("START_DATE_EMPTY","StartDate is mandatory"); + } + //endDate is required for /_estimate musterroll - The estimate will be shown for last few days in the week in view/edit screen + if (musterRoll.getEndDate() == null) { + throw new CustomException("END_DATE_EMPTY","EndDate is mandatory"); + } + + log.info("MusterRollValidator::validateEstimateMusterRoll::startDate in epoch::"+musterRoll.getStartDate()); + log.info("MusterRollValidator::validateEstimateMusterRoll::endDate in epoch::"+musterRoll.getEndDate()); + + } + + private void validateWorkFlow(Workflow workflow, Map errorMap) { + if (workflow == null) { + throw new CustomException("WORK_FLOW", "Work flow is mandatory"); + } + if (StringUtils.isBlank(workflow.getAction())) { + errorMap.put("WORK_FLOW.ACTION", "Work flow's action is mandatory"); + } + } + + private void validateMDMSData(MusterRoll musterRoll, Object mdmsData, Map errorMap) { + + final String jsonPathForTenants = "$.MdmsRes." + MDMS_TENANT_MODULE_NAME + "." + MASTER_TENANTS + ".*"; + List tenantRes = null; + + try { + tenantRes = JsonPath.read(mdmsData, jsonPathForTenants); + + } catch (Exception e) { + log.error("MusterRollValidator::validateMDMSData::"+e.getMessage()); + throw new CustomException("JSONPATH_ERROR", "Failed to parse mdms response"); + } + + if (CollectionUtils.isEmpty(tenantRes)) { + log.error("The tenant: " + musterRoll.getTenantId() + " is not present in MDMS"); + errorMap.put("INVALID_TENANT", "The tenant: " + musterRoll.getTenantId() + " is not present in MDMS"); + } + + } + + /** + * Check if the user is valid. User should be enrolled as a staff in the attendance register + * @param musterRoll + * @param requestInfo + */ + public void isValidUser(MusterRoll musterRoll, RequestInfo requestInfo) { + String id = requestInfo.getUserInfo().getUuid(); + + StringBuilder uri = new StringBuilder(); + uri.append(serviceConfiguration.getAttendanceLogHost()).append(serviceConfiguration.getAttendanceRegisterEndpoint()); + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(uri.toString()) + .queryParam("tenantId",musterRoll.getTenantId()) + .queryParam("ids",musterRoll.getRegisterId()) + .queryParam("status", Status.ACTIVE); + RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); + + AttendanceRegisterResponse attendanceRegisterResponse = null; + log.info("MusterRollValidator::isValidUser::call attendance register search with tenantId::"+musterRoll.getTenantId() + +"::for user::"+id); + + try { + attendanceRegisterResponse = restTemplate.postForObject(uriBuilder.toUriString(),requestInfoWrapper,AttendanceRegisterResponse.class); + } catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) { + log.error("MusterRollValidator::isValidUser::Error thrown from attendance register service::"+httpClientOrServerExc.getStatusCode()); + throw new CustomException("ATTENDANCE_REGISTER_SERVICE_EXCEPTION","Error thrown from attendance register service::"+httpClientOrServerExc.getStatusCode()); + } + + if (attendanceRegisterResponse == null || CollectionUtils.isEmpty(attendanceRegisterResponse.getAttendanceRegister())) { + log.error("MusterRollValidator::isValidUser::User with id::" + id + " is not enrolled in the attendance register::"+musterRoll.getRegisterId()); + throw new CustomException("INVALID_USER","User is not enrolled in the attendance register"); + } + + } + + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/web/controllers/MusterRollApiController.java b/health-services/muster-roll/src/main/java/org/egov/web/controllers/MusterRollApiController.java new file mode 100644 index 0000000000..5cbed9ef0b --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/controllers/MusterRollApiController.java @@ -0,0 +1,74 @@ +package org.egov.web.controllers; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.common.contract.models.RequestInfoWrapper; +import io.swagger.annotations.ApiParam; +import org.egov.common.contract.response.ResponseInfo; +import org.egov.service.MusterRollService; +import org.egov.util.ResponseInfoCreator; +import org.egov.web.models.MusterRollRequest; +import org.egov.web.models.MusterRollResponse; +import org.egov.web.models.MusterRollSearchCriteria; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import java.util.Collections; + +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T19:58:09.415+05:30") + +@Controller +@RequestMapping("/v1") +public class MusterRollApiController { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private HttpServletRequest request; + + @Autowired + private MusterRollService musterRollService; + + @Autowired + private ResponseInfoCreator responseInfoCreator; + + @RequestMapping(value = "/_estimate", method = RequestMethod.POST) + public ResponseEntity musterRollV1EstimatePost(@ApiParam(value = "Request object to provide the estimate of the muster roll", required = true) @Valid @RequestBody MusterRollRequest body) { + MusterRollRequest musterRollRequest = musterRollService.estimateMusterRoll(body); + ResponseInfo responseInfo = responseInfoCreator.createResponseInfoFromRequestInfo(body.getRequestInfo(), true); + MusterRollResponse musterRollResponse = MusterRollResponse.builder().responseInfo(responseInfo).musterRolls(Collections.singletonList(musterRollRequest.getMusterRoll())).build(); + return new ResponseEntity(musterRollResponse, HttpStatus.OK); + } + + @RequestMapping(value = "/_create", method = RequestMethod.POST) + public ResponseEntity musterRollV1CreatePost(@ApiParam(value = "Request object to create the muster roll", required = true) @Valid @RequestBody MusterRollRequest body) { + MusterRollRequest musterRollRequest = musterRollService.createMusterRoll(body); + ResponseInfo responseInfo = responseInfoCreator.createResponseInfoFromRequestInfo(body.getRequestInfo(), true); + MusterRollResponse musterRollResponse = MusterRollResponse.builder().responseInfo(responseInfo).musterRolls(Collections.singletonList(musterRollRequest.getMusterRoll())).build(); + return new ResponseEntity(musterRollResponse, HttpStatus.OK); + } + + @RequestMapping(value = "/_search", method = RequestMethod.POST) + public ResponseEntity attendanceV1SearchPOST(@Valid @RequestBody RequestInfoWrapper requestInfoWrapper, @Valid @ModelAttribute MusterRollSearchCriteria searchCriteria) { + MusterRollResponse musterRollResponse = musterRollService.searchMusterRolls(requestInfoWrapper, searchCriteria); + return new ResponseEntity(musterRollResponse, HttpStatus.OK); + } + + @RequestMapping(value = "/_update", method = RequestMethod.POST) + public ResponseEntity musterRollV1UpdatePost(@ApiParam(value = "Request object to update the muster roll", required = true) @Valid @RequestBody MusterRollRequest body) { + MusterRollRequest musterRollRequest = musterRollService.updateMusterRoll(body); + ResponseInfo responseInfo = responseInfoCreator.createResponseInfoFromRequestInfo(body.getRequestInfo(), true); + MusterRollResponse musterRollResponse = MusterRollResponse.builder().responseInfo(responseInfo).musterRolls(Collections.singletonList(musterRollRequest.getMusterRoll())).build(); + return new ResponseEntity(musterRollResponse, HttpStatus.OK); + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceEntry.java b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceEntry.java new file mode 100644 index 0000000000..feda7475f6 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceEntry.java @@ -0,0 +1,40 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.models.AuditDetails; +import io.swagger.annotations.ApiModel; +import lombok.*; +import org.springframework.validation.annotation.Validated; + +import java.math.BigDecimal; + +/** + * This computed data will also be stored as part of the muster roll db. + */ +@ApiModel(description = "This computed data will also be stored as part of the muster roll db.") +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T19:58:09.415+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AttendanceEntry { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("time") + private BigDecimal time = null; + + @JsonProperty("attendance") + private BigDecimal attendance = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; +} + diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceLog.java b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceLog.java new file mode 100644 index 0000000000..8fd0b13f43 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceLog.java @@ -0,0 +1,67 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.models.AuditDetails; +import lombok.*; +import org.egov.common.contract.models.Document; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * AttendanceLog + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T14:44:21.051+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AttendanceLog { + @JsonProperty("id") + private String id = null; + + @JsonProperty("registerId") + private String registerId = null; + + @JsonProperty("individualId") + private String individualId = null; + + @JsonProperty("tenantId") + private String tenantId =null; + + @JsonProperty("time") + private BigDecimal time = null; + + @JsonProperty("type") + private String type = null; + + @JsonProperty("status") + private Status status = null; + + @JsonProperty("documentIds") + @Valid + private List documentIds = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + + public AttendanceLog addDocumentIdsItem(Document documentIdsItem) { + if (this.documentIds == null) { + this.documentIds = new ArrayList<>(); + } + this.documentIds.add(documentIdsItem); + return this; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceLogResponse.java b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceLogResponse.java new file mode 100644 index 0000000000..e891ad944e --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceLogResponse.java @@ -0,0 +1,41 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; +import java.util.ArrayList; +import java.util.List; + +/** + * AttendanceLogResponse + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T14:44:21.051+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AttendanceLogResponse { + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo = null; + + @JsonProperty("attendance") + @Valid + private List attendance = null; + + + public AttendanceLogResponse addAttendanceItem(AttendanceLog attendanceItem) { + if (this.attendance == null) { + this.attendance = new ArrayList<>(); + } + this.attendance.add(attendanceItem); + return this; + } + +} + diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceRegister.java b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceRegister.java new file mode 100644 index 0000000000..04c58ec780 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceRegister.java @@ -0,0 +1,79 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.egov.common.contract.models.AuditDetails; +import lombok.*; +import org.egov.works.services.common.models.attendance.StaffPermission; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * AttendanceRegister + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T14:44:21.051+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AttendanceRegister { + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("registerNumber") + private String registerNumber = null; + + @JsonProperty("name") + private String name = null; + + @JsonProperty("startDate") + private BigDecimal startDate = null; + + @JsonProperty("endDate") + private BigDecimal endDate = null; + + @JsonProperty("status") + private Status status = Status.ACTIVE; + + @JsonProperty("staff") + @Valid + private List staff = null; + + @JsonProperty("attendees") + @Valid + private List attendees = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + + public AttendanceRegister addStaffItem(StaffPermission staffItem) { + if (this.staff == null) { + this.staff = new ArrayList<>(); + } + this.staff.add(staffItem); + return this; + } + + public AttendanceRegister addAttendeesItem(IndividualEntry attendeesItem) { + if (this.attendees == null) { + this.attendees = new ArrayList<>(); + } + this.attendees.add(attendeesItem); + return this; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceRegisterResponse.java b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceRegisterResponse.java new file mode 100644 index 0000000000..444f2bcba5 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/AttendanceRegisterResponse.java @@ -0,0 +1,40 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; +import java.util.ArrayList; +import java.util.List; + +/** + * AttendanceRegisterResponse + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T14:44:21.051+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AttendanceRegisterResponse { + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo = null; + + @JsonProperty("attendanceRegister") + @Valid + private List attendanceRegister = null; + + + public AttendanceRegisterResponse addAttendanceRegisterItem(AttendanceRegister attendanceRegisterItem) { + if (this.attendanceRegister == null) { + this.attendanceRegister = new ArrayList<>(); + } + this.attendanceRegister.add(attendanceRegisterItem); + return this; + } + +} diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/IndividualEntry.java b/health-services/muster-roll/src/main/java/org/egov/web/models/IndividualEntry.java new file mode 100644 index 0000000000..ba875cf87c --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/IndividualEntry.java @@ -0,0 +1,65 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import org.egov.common.contract.models.AuditDetails; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +/** + * IndividualEntry + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T19:58:09.415+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class IndividualEntry { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("individualId") + private String individualId = null; + + @JsonProperty("actualTotalAttendance") + private BigDecimal actualTotalAttendance = null; + + @JsonProperty("modifiedTotalAttendance") + private BigDecimal modifiedTotalAttendance = null; + + @JsonProperty("attendanceEntries") + @Valid + private List attendanceEntries = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + + public IndividualEntry addAttendanceEntriesItem(AttendanceEntry attendanceEntriesItem) { + if (this.attendanceEntries == null) { + this.attendanceEntries = new ArrayList<>(); + } + this.attendanceEntries.add(attendanceEntriesItem); + return this; + } + +} + diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRoll.java b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRoll.java new file mode 100644 index 0000000000..438c1b1a88 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRoll.java @@ -0,0 +1,87 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.*; +import org.egov.common.contract.models.AuditDetails; +import org.egov.common.contract.workflow.ProcessInstance; +import org.egov.works.services.common.models.musterroll.Status; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * MusterRoll + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T19:58:09.415+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MusterRoll { + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId = null; + + @JsonProperty("musterRollNumber") + private String musterRollNumber = null; + + @JsonProperty("registerId") + @NotNull + @Size(min = 2, max = 256) + private String registerId = null; + + @JsonProperty("status") + private Status status = null; + + @JsonProperty("musterRollStatus") + private String musterRollStatus = null; + + @JsonProperty("startDate") + @NotNull + private BigDecimal startDate = null; + + @JsonProperty("endDate") + private BigDecimal endDate = null; + + @JsonProperty("individualEntries") + @Valid + private List individualEntries = null; + + @JsonProperty("referenceId") + private String referenceId = null; + + @JsonProperty("serviceCode") + private String serviceCode = null; + + @JsonProperty("additionalDetails") + private Object additionalDetails = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("processInstance") + private ProcessInstance processInstance = null; + + + public MusterRoll addIndividualEntriesItem(IndividualEntry individualEntriesItem) { + if (this.individualEntries == null) { + this.individualEntries = new ArrayList<>(); + } + this.individualEntries.add(individualEntriesItem); + return this; + } + +} + diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollRequest.java b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollRequest.java new file mode 100644 index 0000000000..2b6a85d8ff --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollRequest.java @@ -0,0 +1,43 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import org.egov.common.contract.models.Workflow; +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +import jakarta.validation.constraints.NotNull; + +/** + * MusterRollRequest + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T19:58:09.415+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MusterRollRequest { + @JsonProperty("RequestInfo") + @NotNull(message = "Request info is mandatory") + @Valid + private RequestInfo requestInfo = null; + + @JsonProperty("musterRoll") + @NotNull(message = "Muster Roll is mandatory") + @Valid + private MusterRoll musterRoll = null; + + @JsonProperty("workflow") + private Workflow workflow = null; + + +} + diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollResponse.java b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollResponse.java new file mode 100644 index 0000000000..9654600043 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollResponse.java @@ -0,0 +1,52 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.Valid; + +import org.egov.common.contract.response.ResponseInfo; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +/** + * MusterRollResponse + */ +@Validated +@jakarta.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2022-11-14T19:58:09.415+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MusterRollResponse { + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo = null; + + @JsonProperty("musterRolls") + @Valid + private List musterRolls = null; + + @JsonProperty("count") + private Integer count; + + + public MusterRollResponse addMusterRollsItem(MusterRoll musterRollsItem) { + if (this.musterRolls == null) { + this.musterRolls = new ArrayList<>(); + } + this.musterRolls.add(musterRollsItem); + return this; + } + +} + diff --git a/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollSearchCriteria.java b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollSearchCriteria.java new file mode 100644 index 0000000000..f0e66ea2c3 --- /dev/null +++ b/health-services/muster-roll/src/main/java/org/egov/web/models/MusterRollSearchCriteria.java @@ -0,0 +1,64 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.*; +import org.egov.works.services.common.models.expense.Pagination; +import org.egov.works.services.common.models.musterroll.Status; + +import java.math.BigDecimal; +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MusterRollSearchCriteria { + + @JsonProperty("ids") + private List ids; + + @JsonProperty("tenantId") + @NotNull + @Size(min = 2, max = 64) + private String tenantId; + + @JsonProperty("musterRollNumber") + private String musterRollNumber; + + @JsonProperty("registerId") + private String registerId; + + @JsonProperty("fromDate") + private BigDecimal fromDate; + + @JsonProperty("toDate") + private BigDecimal toDate; + + @JsonProperty("status") + private Status status; + + @JsonProperty("musterRollStatus") + private String musterRollStatus; + + @JsonProperty("referenceId") + private String referenceId = null; + + @JsonProperty("serviceCode") + private String serviceCode = null; + + @JsonProperty("limit") + private Integer limit; + + @JsonProperty("offset") + private Integer offset; + + @JsonProperty("sortBy") + private String sortBy; + + @JsonProperty("order") + private Pagination.OrderEnum order; + +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/resources/Muster Environment.postman_environment.json b/health-services/muster-roll/src/main/resources/Muster Environment.postman_environment.json new file mode 100644 index 0000000000..c437dd9a8c --- /dev/null +++ b/health-services/muster-roll/src/main/resources/Muster Environment.postman_environment.json @@ -0,0 +1,45 @@ +{ + "id": "cfe25c62-d30f-4e9b-b403-dbf34b56f3da", + "name": "Muster Environment", + "values": [ + { + "key": "envURL", + "value": "https://works-dev.digit.org", + "type": "default", + "enabled": true + }, + { + "key": "envAUTH", + "value": "f3e8e7b9-a6ad-4efa-a6b3-7dd4270fb462", + "type": "default", + "enabled": true + }, + { + "key": "tenantId", + "value": "pb.amritsar", + "type": "default", + "enabled": true + }, + { + "key": "musterRollId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "musterRollNumber", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "registerId", + "value": "9d8ec5f2-a1ed-4e60-afb3-96a6ab73c2b9", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2023-01-23T08:54:17.069Z", + "_postman_exported_using": "Postman/10.8.0" +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/resources/Muster Roll Service.postman_collection.json b/health-services/muster-roll/src/main/resources/Muster Roll Service.postman_collection.json new file mode 100644 index 0000000000..dbdccd1054 --- /dev/null +++ b/health-services/muster-roll/src/main/resources/Muster Roll Service.postman_collection.json @@ -0,0 +1,824 @@ +{ + "info": { + "_postman_id": "d06e2af1-4655-4db7-9a30-72d6a09babca", + "name": "Muster Roll Service", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Estimate muster roll - Success", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"TenantId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.tenantId).to.be.not.null});", + "", + "pm.test(\"TenantId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.tenantId).to.not.be.undefined});", + "", + "pm.test(\"registerId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.registerId).to.be.not.null});", + "", + "pm.test(\"registerId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.registerId).to.not.be.undefined});", + "", + "pm.test(\"startDate is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.startDate).to.be.not.null});", + "", + "pm.test(\"startDate is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.startDate).to.not.be.undefined});", + "", + "pm.test(\"endDate is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.endDate).to.be.not.null});", + "", + "pm.test(\"endDate is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.endDate).to.not.be.undefined});", + "", + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"f31d4d19-d539-4242-9b96-5316afa8bb81\",\n \"startDate\": 1670178600000,\n \"endDate\": 1670697000000\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_estimate", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_estimate" + ] + } + }, + "response": [] + }, + { + "name": "Estimate muster roll - Validation error", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(400)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"56shcwgdt227\",\n \"startDate\": 1669919400000\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_estimate", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_estimate" + ] + } + }, + "response": [] + }, + { + "name": "Create muster roll - Success - Default skill level", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"TenantId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.tenantId).to.be.not.null});", + "", + "pm.test(\"TenantId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.tenantId).to.not.be.undefined});", + "", + "pm.test(\"registerId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.registerId).to.be.not.null});", + "", + "pm.test(\"registerId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.registerId).to.not.be.undefined});", + "", + "pm.test(\"startDate is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.startDate).to.be.not.null});", + "", + "pm.test(\"startDate is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.startDate).to.not.be.undefined});", + "", + "pm.test(\"skill level is default\", function () {", + " let responseData = pm.response.json();", + " pm.expect(responseData.musterRolls[0].individualEntries[0].additionalDetails.skillCode).eq(\"UNSKILLED\")});", + "", + "pm.test(\"workflow status is submitted\", function () {", + " let responseData = pm.response.json();", + " pm.expect(responseData.musterRolls[0].musterRollStatus).eq(\"SUBMITTED\")});", + "", + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));", + "", + "let responseData = pm.response.json();", + "pm.environment.set(\"musterRollId\", responseData.musterRolls[0].id);", + "pm.environment.set(\"musterRollNumber\", responseData.musterRolls[0].musterRollNumber);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"{{registerId}}\",\n \"startDate\": 1670178600000,\n \"endDate\": 1670697000000,\n \"additionalDetails\": {\"org details\":\"org1\"}\n },\n \"workflow\": {\n \"action\": \"SUBMIT\",\n \"comments\": \"Sumbit muster roll\",\n \"assignees\": [] \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_create", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_create" + ] + } + }, + "response": [] + }, + { + "name": "Create muster roll - Success with Individual Skill all individuals", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"9d8ec5f2-a1ed-4e60-afb3-96a6ab73c2b6\",\n \"startDate\": 1670178600000,\n \"endDate\": 1670697000000,\n \"individualEntries\": [\n {\n \"individualId\": \"123e4567-e89b-12d3-a456-426614174444\",\n \"additionalDetails\": {\"code\":\"SKILLED_LEVEL_1\"}\n },\n {\n \"individualId\": \"123e4567-e89b-12d3-a456-426614174445\",\n \"additionalDetails\": {\"code\":\"SKILLED_LEVEL_2\"}\n }\n\n ],\n \"additionalDetails\": {\"org details\":\"org1\"}\n },\n \"workflow\": {\n \"action\": \"SUBMIT\",\n \"comments\": \"Sumbit muster roll\",\n \"assignees\": [] \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_create", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_create" + ] + } + }, + "response": [] + }, + { + "name": "Create muster roll - Success with Individual Skill for one individual (Not all)", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"9d8ec5f2-a1ed-4e60-afb3-96a6ab73c2b7\",\n \"startDate\": 1670178600000,\n \"endDate\": 1670697000000,\n \"individualEntries\": [\n {\n \"individualId\": \"123e4567-e89b-12d3-a456-426614174444\",\n \"additionalDetails\": {\"code\":\"SKILLED_LEVEL_4\"}\n }\n\n ],\n \"additionalDetails\": {\"org details\":\"org1\"}\n },\n \"workflow\": {\n \"action\": \"SUBMIT\",\n \"comments\": \"Sumbit muster roll\",\n \"assignees\": [] \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_create", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_create" + ] + } + }, + "response": [] + }, + { + "name": "Create muster roll - Validation error - StartDate not Monday", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(400)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"56shcwgdt227\",\n \"startDate\": 1669919400000\n },\n \"workflow\": {\n \"action\": \"SUBMIT\",\n \"comments\": \"Sumbit muster roll\"\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_create", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_create" + ] + } + }, + "response": [] + }, + { + "name": "Create muster roll - Validation error - Tenant id not in MDMS", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(400)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"bp.amritsar\",\n \"registerId\": \"56shcwgdt227\",\n \"startDate\": 1670178600000\n },\n \"workflow\": {\n \"action\": \"SUBMIT\",\n \"comments\": \"Sumbit muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_create", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_create" + ] + } + }, + "response": [] + }, + { + "name": "Create muster roll - Validation error - duplicate muster", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(400)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"registerId\": \"{{registerId}}\",\n \"startDate\": 1670178600000\n },\n \"workflow\": {\n \"action\": \"SUBMIT\",\n \"comments\": \"Sumbit muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_create", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_create" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - VERIFY action - No update of totalAttendance and skills", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"TenantId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.tenantId).to.be.not.null});", + "", + "pm.test(\"TenantId is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.tenantId).to.not.be.undefined});", + "", + "pm.test(\"muster id is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.id).to.be.not.null});", + "", + "pm.test(\"muster id is required\", function () {", + " var req = JSON.parse(pm.request.body.raw);", + " pm.expect(req.musterRoll.id).to.not.be.undefined});", + "", + "", + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"{{musterRollId}}\"\n },\n \"workflow\": {\n \"action\": \"VERIFY\",\n \"comments\": \"Verify muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - VERIFY action - Update total attendance for one individual only", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"c46f6626-a31b-4038-b981-a9655438f10a\",\n \"individualEntries\": [\n {\n \"id\": \"3d69126d-7808-4516-8915-b9338fe0f081\",\n \"modifiedTotalAttendance\": 4.5\n\n }\n\n ]\n },\n \"workflow\": {\n \"action\": \"VERIFY\",\n \"comments\": \"Verify muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - VERIFY action - Update skill for one individual only", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"9d69c270-4040-4c40-9c38-83d30bea5760\",\n \"individualEntries\": [\n {\n \"id\": \"0bbd99e9-01bb-4a04-9acf-21dc9cbb6c1a\",\n \"additionalDetails\": {\"code\":\"SKILLED_LEVEL_4\"}\n }\n\n ]\n },\n \"workflow\": {\n \"action\": \"VERIFY\",\n \"comments\": \"Verify muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - VERIFY action - Update total attendance and skill", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"43c11c3e-a157-49f9-80fe-58089d74c334\",\n \"individualEntries\": [\n {\n \"id\": \"9b4de87b-0cb6-4d43-91fc-ff7fa05b4068\",\n \"modifiedTotalAttendance\": 4.5,\n \"additionalDetails\": {\"code\":\"SKILLED_LEVEL_3\"}\n },\n {\n \"id\": \"e37149f2-f92a-48f0-ab15-76d8cdd8a1b4\",\n \"additionalDetails\": {\"code\":\"SKILLED_LEVEL_4\"}\n }\n\n ]\n },\n \"workflow\": {\n \"action\": \"VERIFY\",\n \"comments\": \"Verify muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - REJECT action", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"f9665338-8a0c-4054-93c7-ff1526616da6\",\n \"userName\": \"9070102020\",\n \"name\": \"test4\",\n \"mobileNumber\": \"9070102020\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"MUNICIPAL_ENGINEER\",\n \"name\": \"MUNICIPAL ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"{{musterRollId}}\"\n \n },\n \"workflow\": {\n \"action\": \"REJECT\",\n \"comments\": \"Reject muster roll\"\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - RESUBMIT action", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"{{musterRollId}}\",\n \"additionalDetails\": {\"computeAttendance\":\"true\"}\n },\n \"workflow\": {\n \"action\": \"RESUBMIT\",\n \"comments\": \"Resubmit muster roll\",\n \"assignees\": [] \n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Success - APPROVE action", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"f9665338-8a0c-4054-93c7-ff1526616da6\",\n \"userName\": \"9070102020\",\n \"name\": \"test4\",\n \"mobileNumber\": \"9070102020\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"MUNICIPAL_ENGINEER\",\n \"name\": \"MUNICIPAL ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"{{musterRollId}}\"\n \n },\n \"workflow\": {\n \"action\": \"APPROVE\",\n \"comments\": \"Approve muster roll\"\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Update muster roll - Validation error - VERIFY ineligible", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(400)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 169,\n \"uuid\": \"81a1988f-9072-4ff9-8aa4-eee5cf10ba57\",\n \"userName\": \"7070102021\",\n \"name\": \"test2\",\n \"mobileNumber\": \"9130011254\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"name\": \"Organization staff\",\n \"code\": \"ORG_STAFF\",\n \"tenantId\": \"{{tenantId}}\"\n }]\n }\n },\n \"musterRoll\": {\n \"tenantId\": \"{{tenantId}}\",\n \"id\":\"4a8ee311-3354-4c2b-8569-27c4798d4669\",\n \"individualEntries\": [\n {\n \"id\": \"ed30593b-afd6-4497-86a7-9c8e203a0d4f\",\n \"totalAttendance\": 3.5\n },\n {\n \"id\": \"fb8bf65e-8565-4d64-8cc7-4b04bc1edfad\",\n \"totalAttendance\": 2\n }\n\n ]\n },\n \"workflow\": {\n \"action\": \"VERIFY\",\n \"comments\": \"Verify muster roll\",\n \"assignees\": []\n \n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_update", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_update" + ] + } + }, + "response": [] + }, + { + "name": "Search Muster Roll - musterRollNumber based", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_search?tenantId=pb.amritsar&musterRollNumber={{musterRollNumber}}", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_search" + ], + "query": [ + { + "key": "tenantId", + "value": "pb.amritsar" + }, + { + "key": "musterRollNumber", + "value": "{{musterRollNumber}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Search Muster Roll - startDate and endDate based - multi muster roll response", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_search?tenantId=pb.amritsar&fromDate=1670178600000&toDate=1670697000000", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_search" + ], + "query": [ + { + "key": "tenantId", + "value": "pb.amritsar" + }, + { + "key": "fromDate", + "value": "1670178600000" + }, + { + "key": "toDate", + "value": "1670697000000" + } + ] + } + }, + "response": [] + }, + { + "name": "Search Muster Roll - state level tenant", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Response is successful\", () => ", + " pm.expect(pm.response.to.have.status(200)));" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"asset-services\",\n \"ver\": null,\n \"ts\": null,\n \"action\": null,\n \"did\": null,\n \"key\": null,\n \"msgId\": \"search with from and to values\",\n \"authToken\": \"{{envAUTH}}\",\n \"userInfo\": {\n \"id\": 172,\n \"uuid\": \"5ce80dd3-b1c0-42fd-b8f6-a2be456db31c\",\n \"userName\": \"8070102021\",\n \"name\": \"test3\",\n \"mobileNumber\": \"8070102021\",\n \"emailId\": \"xyz@egovernments.org\",\n \"locale\": null,\n \"type\": \"EMPLOYEE\",\n \"roles\": [\n {\n \"code\": \"JUNIOR_ENGINEER\",\n \"name\": \"JUNIOR ENGINEER\",\n \"tenantId\": \"{{tenantId}}\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{envURL}}/muster-roll/v1/_search?tenantId=pb", + "host": [ + "{{envURL}}" + ], + "path": [ + "muster-roll", + "v1", + "_search" + ], + "query": [ + { + "key": "tenantId", + "value": "pb" + } + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/health-services/muster-roll/src/main/resources/Muster_roll_DbSchema.png b/health-services/muster-roll/src/main/resources/Muster_roll_DbSchema.png new file mode 100644 index 0000000000..15deb03d3e Binary files /dev/null and b/health-services/muster-roll/src/main/resources/Muster_roll_DbSchema.png differ diff --git a/health-services/muster-roll/src/main/resources/application.properties b/health-services/muster-roll/src/main/resources/application.properties new file mode 100644 index 0000000000..5d8bbfe492 --- /dev/null +++ b/health-services/muster-roll/src/main/resources/application.properties @@ -0,0 +1,118 @@ +server.contextPath=/muster-roll +server.servlet.context-path=/muster-roll +server.port=8051 +app.timezone=Asia/Kolkata +org.egov.detailed.tracing.enabled=true + +#----------------DATABASE CONFIGURATION----------------# +spring.datasource.driver-class-name=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/digit-works +spring.datasource.username=postgres +spring.datasource.password=egov + +#----------------FLYWAY CONFIGURATION----------------# +spring.flyway.enabled=true +spring.flyway.table=musterroll_service_schema +spring.flyway.baseline-on-migrate=true + +#-----------------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=egov-wms-muster +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 + +#-----------------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 + +#-------------- Topic Config------------------# +musterroll.kafka.create.topic=save-musterroll +musterroll.kafka.update.topic=update-musterroll +musterroll.kafka.calculate.topic=calculate-musterroll +musterroll.default.offset=0 +musterroll.default.limit=100 +musterroll.search.max.limit=200 + +#---------------Notification---------------------# +notification.sms.enabled=true +kafka.topics.notification.sms=egov.core.notification.sms + +#----------------MDMS config---------------------# +egov.mdms.host=https://unified-dev.digit.org +egov.mdms.search.endpoint=/egov-mdms-service/v1/_search + +egov.mdms.v2.host=https://unified-dev.digit.org +egov.mdms.v2.search.endpoint=/mdms-v2/v1/_search + +#-----------------User config---------------------# +egov.user.host=https://unified-dev.digit.org +egov.user.context.path=/user/users +egov.user.create.path=/_createnovalidate +egov.user.search.path=/user/_search +egov.user.update.path=/_updatenovalidate + +#url shortner +egov.url.shortner.host=https://unified-dev.digit.org +egov.url.shortner.endpoint=/egov-url-shortening/shortener + +#------------------Idgen Config-----------------# +egov.idgen.host=https://unified-dev.digit.org/ +egov.idgen.path=egov-idgen/id/_generate +egov.idgen.musterroll.number.name=muster.number +#egov.idgen.musterroll.number.format=MR/[fy:yyyy-yy]/[cy:MM]/[cy:dd]/[SEQ_MUSTER_NUM] + +#-----------------Workflow config----------------# +egov.workflow.host=https://unified-dev.digit.org +egov.workflow.transition.path=/egov-workflow-v2/egov-wf/process/_transition +egov.workflow.businessservice.search.path=/egov-workflow-v2/egov-wf/businessservice/_search +egov.workflow.processinstance.search.path=/egov-workflow-v2/egov-wf/process/_search +musterroll.workflow.business.service=MR +musterroll.workflow.module.name=muster-roll-services + +#-----------------Localization Config--------------# +egov.localization.host=https://unified-dev.digit.org +egov.localization.search.endpoint=/localization/messages/v1/_search + +#---------------Attendance service----------------# +works.attendance.log.host=https://unified-dev.digit.org +works.attendance.log.search.endpoint=/attendance/log/v1/_search +works.attendance.register.search.endpoint=/attendance/v1/_search +works.attendance.register.search.limit=100 + +#--------------Contract Service---------------------# +works.contract.host=https://works-uat.digit.org +works.contract.endpoint=/contract/v1/_search + +#--------------Organisation Service------------------# +works.organisation.host=https://works-uat.digit.org +works.organisation.endpoint=/org-services/organisation/v1/_search + +#-----------------Expense Calculator Service-------------------# +works.expense.calculator.host=https://works-uat.digit.org +works.expense.calculator.endpoint=/expense-calculator/v1/_estimate + +#-------------muster restricted search roles---------------------------# +muster.restricted.search.roles=ORG_ADMIN,ORG_STAFF + +#---------------Individual service----------------# +works.individual.host=https://unified-dev.digit.org +works.individual.search.endpoint=/individual/v1/_search + +#---------------Banking service----------------# +works.bankaccounts.host=https://unified-dev.digit.org +works.bankaccounts.search.endpoint=/bankaccount-service/bankaccount/v1/_search + +#-----------Contract service code------------# +works.contract.service.code=WORKS-CONTRACT diff --git a/health-services/muster-roll/src/main/resources/db/Dockerfile b/health-services/muster-roll/src/main/resources/db/Dockerfile new file mode 100644 index 0000000000..e7da01d7f0 --- /dev/null +++ b/health-services/muster-roll/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"] \ No newline at end of file diff --git a/health-services/muster-roll/src/main/resources/db/migrate.sh b/health-services/muster-roll/src/main/resources/db/migrate.sh new file mode 100644 index 0000000000..c58d6f91e3 --- /dev/null +++ b/health-services/muster-roll/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 diff --git a/health-services/muster-roll/src/main/resources/db/migration/main/V20221122121630__create_mutser_roll_table.sql b/health-services/muster-roll/src/main/resources/db/migration/main/V20221122121630__create_mutser_roll_table.sql new file mode 100644 index 0000000000..9c9ebaa565 --- /dev/null +++ b/health-services/muster-roll/src/main/resources/db/migration/main/V20221122121630__create_mutser_roll_table.sql @@ -0,0 +1,48 @@ +CREATE TABLE eg_wms_muster_roll( +id character varying(256), +tenantid character varying(64) NOT NULL, +musterrollnumber character varying(128) NOT NULL, +attendanceregisterid character varying(256) NOT NULL, +startdate bigint NOT NULL, +enddate bigint NOT NULL, +musterrollstatus character varying(64) NOT NULL, +status character varying(64) NOT NULL, +additionaldetails JSONB, +createdby character varying(256) NOT NULL, +lastmodifiedby character varying(256), +createdtime bigint, +lastmodifiedtime bigint, +CONSTRAINT uk_eg_wms_muster_roll UNIQUE (musterrollnumber), +CONSTRAINT pk_eg_wms_muster_roll PRIMARY KEY (id) +); + +CREATE TABLE eg_wms_attendance_summary( +id character varying(256), +individual_id character varying(256) NOT NULL, +muster_roll_id character varying(256) NOT NULL, +musterrollnumber character varying(128) NOT NULL, +total_attendance NUMERIC, +additionaldetails JSONB, +createdby character varying(256) NOT NULL, +lastmodifiedby character varying(256), +createdtime bigint, +lastmodifiedtime bigint, +CONSTRAINT pk_eg_wms_attendance_summary PRIMARY KEY (id), +CONSTRAINT fk_eg_wms_attendance_summary FOREIGN KEY (muster_roll_id) REFERENCES eg_wms_muster_roll (id) +); + +CREATE TABLE eg_wms_attendance_entries( +id character varying(256), +attendance_summary_id character varying(256) NOT NULL, +individual_id character varying(256) NOT NULL, +musterrollnumber character varying(128) NOT NULL, +date_of_attendance bigint, +attendance NUMERIC, +additionaldetails JSONB, +createdby character varying(256) NOT NULL, +lastmodifiedby character varying(256), +createdtime bigint, +lastmodifiedtime bigint, +CONSTRAINT pk_eg_wms_attendance_entries PRIMARY KEY (id), +CONSTRAINT fk_eg_wms_attendance_entries FOREIGN KEY (attendance_summary_id) REFERENCES eg_wms_attendance_summary (id) +); \ No newline at end of file diff --git a/health-services/muster-roll/src/main/resources/db/migration/main/V20221122133030__add_index.sql b/health-services/muster-roll/src/main/resources/db/migration/main/V20221122133030__add_index.sql new file mode 100644 index 0000000000..69a77985ae --- /dev/null +++ b/health-services/muster-roll/src/main/resources/db/migration/main/V20221122133030__add_index.sql @@ -0,0 +1,24 @@ +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_tenantId ON eg_wms_muster_roll (tenantId); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_id ON eg_wms_muster_roll (id); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_musterRollNumber ON eg_wms_muster_roll (musterRollNumber); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_attendanceRegisterId ON eg_wms_muster_roll (attendanceRegisterId); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_status ON eg_wms_muster_roll (status); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_musterRollStatus ON eg_wms_muster_roll (musterRollStatus); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_startDate ON eg_wms_muster_roll (startDate); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_endDate ON eg_wms_muster_roll (endDate); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_createdtime ON eg_wms_muster_roll (createdtime); + + +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_id ON eg_wms_attendance_summary (id); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_musterRollNumber ON eg_wms_attendance_summary (musterRollNumber); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_individual_id ON eg_wms_attendance_summary (individual_id); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_total_attendance ON eg_wms_attendance_summary (total_attendance); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_createdtime ON eg_wms_attendance_summary (createdtime); + +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_id ON eg_wms_attendance_entries (id); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_musterRollNumber ON eg_wms_attendance_entries (musterRollNumber); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_individual_id ON eg_wms_attendance_entries (individual_id); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_attendance_summary_id ON eg_wms_attendance_entries (attendance_summary_id); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_attendance ON eg_wms_attendance_entries (attendance); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_date_of_attendance ON eg_wms_attendance_entries (date_of_attendance); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_createdtime ON eg_wms_attendance_entries (createdtime); diff --git a/health-services/muster-roll/src/main/resources/db/migration/main/V20230111101300__alter_mutser_roll_table.sql b/health-services/muster-roll/src/main/resources/db/migration/main/V20230111101300__alter_mutser_roll_table.sql new file mode 100644 index 0000000000..bcd993ceff --- /dev/null +++ b/health-services/muster-roll/src/main/resources/db/migration/main/V20230111101300__alter_mutser_roll_table.sql @@ -0,0 +1,40 @@ +ALTER TABLE eg_wms_muster_roll DROP CONSTRAINT uk_eg_wms_muster_roll; +ALTER TABLE eg_wms_muster_roll RENAME COLUMN tenantid TO tenant_id; +ALTER TABLE eg_wms_muster_roll RENAME COLUMN musterrollnumber TO musterroll_number; +ALTER TABLE eg_wms_muster_roll RENAME COLUMN attendanceregisterid TO attendance_register_id; +ALTER TABLE eg_wms_muster_roll RENAME COLUMN startdate TO start_date; +ALTER TABLE eg_wms_muster_roll RENAME COLUMN enddate TO end_date; +ALTER TABLE eg_wms_muster_roll RENAME COLUMN musterrollstatus TO musterroll_status; +ALTER TABLE eg_wms_muster_roll add CONSTRAINT uk_eg_wms_muster_roll UNIQUE (musterroll_number); + +ALTER TABLE eg_wms_attendance_summary RENAME COLUMN musterrollnumber TO musterroll_number; + +ALTER TABLE eg_wms_attendance_entries RENAME COLUMN musterrollnumber TO musterroll_number; +/*attendance_value - potential values are 0,0.5,1*/ +ALTER TABLE eg_wms_attendance_entries RENAME COLUMN attendance TO attendance_value; + +DROP INDEX index_eg_wms_muster_roll_tenantId; +DROP INDEX index_eg_wms_muster_roll_musterRollNumber; +DROP INDEX index_eg_wms_muster_roll_attendanceRegisterId; +DROP INDEX index_eg_wms_muster_roll_musterRollStatus; +DROP INDEX index_eg_wms_muster_roll_startDate; +DROP INDEX index_eg_wms_muster_roll_endDate; +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_tenantId ON eg_wms_muster_roll (tenant_id); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_musterRollNumber ON eg_wms_muster_roll (musterroll_number); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_attendanceRegisterId ON eg_wms_muster_roll (attendance_register_id); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_musterRollStatus ON eg_wms_muster_roll (musterroll_status); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_startDate ON eg_wms_muster_roll (start_date); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_endDate ON eg_wms_muster_roll (end_date); + +DROP INDEX index_eg_wms_attendance_summary_musterRollNumber; +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_musterRollNumber ON eg_wms_attendance_summary (musterroll_number); + +DROP INDEX index_eg_wms_attendance_entries_musterRollNumber; +DROP INDEX index_eg_wms_attendance_entries_attendance; +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_musterRollNumber ON eg_wms_attendance_entries (musterroll_number); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_entries_attendance ON eg_wms_attendance_entries (attendance_value); + + + + + diff --git a/health-services/muster-roll/src/main/resources/db/migration/main/V20230119140800__alter_attendance_summary_table.sql b/health-services/muster-roll/src/main/resources/db/migration/main/V20230119140800__alter_attendance_summary_table.sql new file mode 100644 index 0000000000..c5426e0a5f --- /dev/null +++ b/health-services/muster-roll/src/main/resources/db/migration/main/V20230119140800__alter_attendance_summary_table.sql @@ -0,0 +1,11 @@ +ALTER TABLE eg_wms_attendance_summary RENAME COLUMN total_attendance TO actual_total_attendance; +ALTER TABLE eg_wms_attendance_summary ADD modified_total_attendance NUMERIC; + +DROP INDEX index_eg_wms_attendance_summary_total_attendance; +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_actual_total_attendance ON eg_wms_attendance_summary (actual_total_attendance); +CREATE INDEX IF NOT EXISTS index_eg_wms_attendance_summary_modified_total_attendance ON eg_wms_attendance_summary (modified_total_attendance); + + + + + diff --git a/health-services/muster-roll/src/main/resources/db/migration/main/V20230405154700__add_coulmn_muster_roll_table.sql b/health-services/muster-roll/src/main/resources/db/migration/main/V20230405154700__add_coulmn_muster_roll_table.sql new file mode 100644 index 0000000000..fa4e4ad5c3 --- /dev/null +++ b/health-services/muster-roll/src/main/resources/db/migration/main/V20230405154700__add_coulmn_muster_roll_table.sql @@ -0,0 +1,9 @@ +ALTER TABLE eg_wms_muster_roll ADD COLUMN reference_id character varying(256); +ALTER TABLE eg_wms_muster_roll ADD COLUMN service_code character varying(64); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_reference_id ON eg_wms_muster_roll (reference_id); +CREATE INDEX IF NOT EXISTS index_eg_wms_muster_roll_service_code ON eg_wms_muster_roll (service_code); + + + + + diff --git a/health-services/muster-roll/src/main/resources/workflowconfig/musterRollApproval.json b/health-services/muster-roll/src/main/resources/workflowconfig/musterRollApproval.json new file mode 100644 index 0000000000..cbfec2fc58 --- /dev/null +++ b/health-services/muster-roll/src/main/resources/workflowconfig/musterRollApproval.json @@ -0,0 +1,126 @@ +"BusinessServices": [ + { + "tenantId": "pg", + "businessService": "MR", + "business": "muster-roll-services", + "businessServiceSla": 432000000, + "states": [ + { + "sla": null, + "state": null, + "applicationStatus": null, + "docUploadRequired": false, + "isStartState": true, + "isTerminateState": false, + "isStateUpdatable": true, + "actions": [ + { + "action": "SUBMIT", + "nextState": "SUBMITTED", + "roles": [ + "ORG_STAFF", + "ORG_ADMIN" + ] + } + ] + }, + { + "sla": null, + "state": "SUBMITTED", + "applicationStatus": "INWORKFLOW", + "docUploadRequired": false, + "isStartState": false, + "isTerminateState": false, + "isStateUpdatable": true, + "actions": [ + { + "action": "VERIFY AND FORWARD", + "nextState": "PENDING_FOR_APPROVAL", + "roles": [ + "MUSTER_ROLL_VERIFIER" + ] + }, + { + "action": "REJECT", + "nextState": "REJECTED", + "roles": [ + "MUSTER_ROLL_VERIFIER" + ] + } + ] + }, + { + "sla": null, + "state": "PENDING_FOR_APPROVAL", + "applicationStatus": "INWORKFLOW", + "docUploadRequired": false, + "isStartState": false, + "isTerminateState": false, + "isStateUpdatable": false, + "actions": [ + { + "action": "APPROVE", + "nextState": "APPROVED", + "roles": [ + "MUSTER_ROLL_APPROVER" + ] + }, + { + "action": "REJECT", + "nextState": "REJECTED", + "roles": [ + "MUSTER_ROLL_APPROVER" + ] + } + ] + }, + { + "sla": null, + "state": "APPROVED", + "applicationStatus": "ACTIVE", + "docUploadRequired": false, + "isStartState": false, + "isTerminateState": true, + "isStateUpdatable": false, + "actions": [ + { + "action": "BILLCREATE", + "nextState": "BILLCREATED", + "roles": [ + "BILL_CREATOR", + "BILL_VIEWER" + ] + } + ] + }, + { + "sla": null, + "state": "BILLCREATED", + "applicationStatus": "BILLCREATED", + "docUploadRequired": false, + "isStartState": false, + "isTerminateState": true, + "isStateUpdatable": false + }, + { + "sla": null, + "state": "REJECTED", + "applicationStatus": "REJECTED", + "docUploadRequired": false, + "isStartState": false, + "isTerminateState": false, + "isStateUpdatable": false, + "actions": [ + { + "action": "RESUBMIT", + "nextState": "SUBMITTED", + "roles": [ + "ORG_ADMIN", + "ORG_STAFF" + ] + } + ] + } + ] + } + ] \ No newline at end of file diff --git a/health-services/muster-roll/src/test/java/org/egov/TestConfiguration.java b/health-services/muster-roll/src/test/java/org/egov/TestConfiguration.java new file mode 100644 index 0000000000..0246aadc48 --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/TestConfiguration.java @@ -0,0 +1,16 @@ +package org.egov; + +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/muster-roll/src/test/java/org/egov/helper/MusterRollRequestBuilderTest.java b/health-services/muster-roll/src/test/java/org/egov/helper/MusterRollRequestBuilderTest.java new file mode 100644 index 0000000000..64405c2e61 --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/helper/MusterRollRequestBuilderTest.java @@ -0,0 +1,160 @@ +package org.egov.helper; + +import com.fasterxml.jackson.databind.ObjectMapper; +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.Role; +import org.egov.common.contract.request.User; +import org.egov.common.contract.response.ResponseInfo; +import org.egov.common.models.individual.Individual; +import org.egov.common.models.individual.IndividualBulkResponse; +import org.egov.web.models.*; +import org.egov.works.services.common.models.bankaccounts.BankAccount; +import org.egov.works.services.common.models.bankaccounts.BankAccountResponse; + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Slf4j +public class MusterRollRequestBuilderTest { + + private MusterRollRequest builder; + + public static MusterRollRequestBuilderTest builder(){ + return new MusterRollRequestBuilderTest(); + } + + public MusterRollRequest withMusterForCreateSuccess(){ + MusterRoll musterRoll = MusterRoll.builder().tenantId("pb.amritsar").registerId("95ba2d55-b7a4-41e2-8598-19a83d63c9a4").startDate(new BigDecimal("1670178600000")).endDate(new BigDecimal("1670697000000")).build(); + Workflow workflow = Workflow.builder().action("SUBMIT").build(); + this.builder = MusterRollRequest.builder().musterRoll(musterRoll).requestInfo(getRequestInfo()).workflow(workflow).build(); + return this.builder; + } + + public MusterRollRequest withMusterForCreateValidationSuccess(){ + MusterRoll musterRoll = MusterRoll.builder().tenantId("pb.amritsar").registerId("95ba2d55-b7a4-41e2-8598-19a83d63c9a4").startDate(new BigDecimal("1670178600000")).endDate(new BigDecimal("1670697000000")).build(); + Workflow workflow = Workflow.builder().action("SUBMIT").build(); + this.builder = MusterRollRequest.builder().musterRoll(musterRoll).requestInfo(getRequestInfo()).workflow(workflow).build(); + return this.builder; + } + + public MusterRollRequest withMusterForCreateException(){ + MusterRoll musterRoll = MusterRoll.builder().tenantId("pb.amritsar").startDate(new BigDecimal("1669919400000")).endDate(new BigDecimal("1670697000000")).registerId(UUID.randomUUID().toString()).build(); + this.builder = MusterRollRequest.builder().musterRoll(musterRoll).requestInfo(getRequestInfo()).build(); + return this.builder; + } + + public RequestInfo getRequestInfo(){ + Role role = new Role(1L,"Organization staff","ORG_STAFF","pb.amritsar"); + List roles = new ArrayList<>(); + roles.add(role); + User userInfo = User.builder().id(172L).uuid("5ce80dd3-b1c0-42fd-b8f6-a2be456db31c").userName("8070102021").name("test3").mobileNumber("8070102021") + .emailId("xyz@egovernments.org").type("EMPLOYEE").roles(roles).build(); + return RequestInfo.builder().apiId("muster-service").msgId("search with from and to values").userInfo(userInfo).build(); + } + + public ResponseInfo getResponseInfo_Success() { + return ResponseInfo.builder().apiId("muster-roll-service").ver(null).ts(null).resMsgId(null).msgId("search with from and to values") + .status("successful").build(); + } + + public static Object getMdmsResponse() { + + Object mdmsResponse = null; + + try { + ObjectMapper objectMapper = new ObjectMapper(); + Path path = Paths.get("src/test/resources/MusterRollMDMSData.json"); + String exampleRequest = Files.readString(path, StandardCharsets.UTF_8); + mdmsResponse = objectMapper.readValue(exampleRequest, Object.class); + } catch (Exception exception) { + log.error("CalculationServiceTest::getMdmsResponse::Exception while parsing mdms json"); + } + return mdmsResponse; + } + + public static Object getTenantMdmsResponse() { + + Object mdmsResponse = null; + + try { + ObjectMapper objectMapper = new ObjectMapper(); + Path path = Paths.get("src/test/resources/TenantMDMSData.json"); + String exampleRequest = Files.readString(path, StandardCharsets.UTF_8); + mdmsResponse = objectMapper.readValue(exampleRequest, Object.class); + } catch (Exception exception) { + log.error("CalculationServiceTest::getMdmsResponse::Exception while parsing mdms json"); + } + return mdmsResponse; + } + + public static AttendanceLogResponse getAttendanceLogResponse() { + List logs = new ArrayList<>(); + AttendanceLog attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174444") + .time(new BigDecimal("1670211000000")).type("ENTRY") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174444") + .time(new BigDecimal("1670225400000")).type("EXIT") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174444") + .time(new BigDecimal("1670297400000")).type("ENTRY") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174444") + .time(new BigDecimal("1670304600000")).type("EXIT") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174445") + .time(new BigDecimal("1670297400000")).type("ENTRY") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174445") + .time(new BigDecimal("1670329800000")).type("EXIT") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174444") + .time(new BigDecimal("1670383800000")).type("ENTRY") + .build(); + logs.add(attendanceLog); + attendanceLog = AttendanceLog.builder().individualId("123e4567-e89b-12d3-a456-426614174444") + .time(new BigDecimal("1670405400000")).type("EXIT") + .build(); + logs.add(attendanceLog); + + return AttendanceLogResponse.builder().attendance(logs).build(); + } + + public static IndividualBulkResponse getIndividualResponse() { + List individuals = new ArrayList<>(); + Individual individual = Individual.builder().id("123e4567-e89b-12d3-a456-426614174444") + .build(); + individuals.add(individual); + + return IndividualBulkResponse.builder().individual(individuals).build(); + } + + public static BankAccountResponse getBankDetailsResponse() { + List accounts = new ArrayList<>(); + BankAccount bankAccount = BankAccount.builder().referenceId("123e4567-e89b-12d3-a456-426614174444") + .build(); + accounts.add(bankAccount); + + return BankAccountResponse.builder().bankAccounts(accounts).build(); + } + + public static AttendanceRegisterResponse getAttendanceRegisterResponse() { + List attendanceRegisterList = new ArrayList<>(); + AttendanceRegister attendanceRegister = AttendanceRegister.builder().id("196dc78f-54eb-4462-a924-f9e753834228").build(); + attendanceRegisterList.add(attendanceRegister); + return AttendanceRegisterResponse.builder().attendanceRegister(attendanceRegisterList).build(); + } +} diff --git a/health-services/muster-roll/src/test/java/org/egov/service/CalculationServiceTest.java b/health-services/muster-roll/src/test/java/org/egov/service/CalculationServiceTest.java new file mode 100644 index 0000000000..ee7a641cad --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/service/CalculationServiceTest.java @@ -0,0 +1,158 @@ +package org.egov.service; + +import lombok.extern.slf4j.Slf4j; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.helper.MusterRollRequestBuilderTest; +import org.egov.tracer.model.CustomException; +import org.egov.util.MdmsUtil; +import org.egov.util.MusterRollServiceUtil; +import org.egov.web.models.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; + +/**@ExtendWith(MockitoExtension.class) +@Slf4j +public class CalculationServiceTest { + + @InjectMocks + private CalculationService calculationService; + @Mock + private MdmsUtil mdmsUtils; + @Mock + private MusterRollServiceConfiguration config; + @Mock + private RestTemplate restTemplate; + @Mock + private MusterRollServiceUtil musterRollServiceUtil; + + + @BeforeEach + void setUp() throws Exception { + //MOCK MDMS Response + Object mdmsResponse = MusterRollRequestBuilderTest.getMdmsResponse(); + lenient().when(mdmsUtils.mDMSCallMuster(any(MusterRollRequest.class), + any(String.class))).thenReturn(mdmsResponse); + lenient().when(config.getTimeZone()).thenReturn("Asia/Kolkata"); + } + + @Test + void shouldCalculateAttendance_IfAttendanceLogsPresent(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + getMockAttendanceLogsSuccess(); + getMockIndividualSuccess(); + getMockBankDetailsSuccess(); + getMockAttendanceRegister(); + //calculationService.createAttendance(musterRollRequest,true); + assertEquals(2, musterRollRequest.getMusterRoll().getIndividualEntries().size()); + } + + @Test + void shouldCalculateHalfDayAttendance_IfWorkHours_2(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + getMockAttendanceLogsSuccess(); + getMockIndividualSuccess(); + getMockBankDetailsSuccess(); + //calculationService.createAttendance(musterRollRequest,true); + AttendanceEntry attendanceEntry = musterRollRequest.getMusterRoll().getIndividualEntries().get(0).getAttendanceEntries().get(0); + assertEquals(new BigDecimal("0.5"),attendanceEntry.getAttendance()); + } + + @Test + void shouldCalculateFullDayAttendance_IfWorkHours_6(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + getMockAttendanceLogsSuccess(); + getMockIndividualSuccess(); + getMockBankDetailsSuccess(); + //calculationService.createAttendance(musterRollRequest,true); + AttendanceEntry attendanceEntry = musterRollRequest.getMusterRoll().getIndividualEntries().get(0).getAttendanceEntries().get(2); + assertEquals(new BigDecimal("1.0"),attendanceEntry.getAttendance()); + } + + @Test + void shouldCalculateZeroAttendance_IfNoAttendanceLogged(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + getMockAttendanceLogsSuccess(); + getMockIndividualSuccess(); + getMockBankDetailsSuccess(); + //calculationService.createAttendance(musterRollRequest,true); + AttendanceEntry attendanceEntry = musterRollRequest.getMusterRoll().getIndividualEntries().get(0).getAttendanceEntries().get(3); + assertEquals(new BigDecimal("0.0"),attendanceEntry.getAttendance()); + } + + @Test + void shouldCalculateTotalAttendanceAs2_IfSuccess(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + getMockAttendanceLogsSuccess(); + getMockIndividualSuccess(); + getMockBankDetailsSuccess(); + //calculationService.createAttendance(musterRollRequest,true); + BigDecimal totalAttendance = musterRollRequest.getMusterRoll().getIndividualEntries().get(0).getActualTotalAttendance(); + assertEquals(new BigDecimal("2.0"),totalAttendance); + } + + @Test + void shouldThrowException_IfNoAttendanceLogsFound(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateException(); + getMockAttendanceLogsFailure(); + CustomException exception = assertThrows(CustomException.class, ()-> calculationService.createAttendance(musterRollRequest,true)); + assertTrue(exception.getCode().contentEquals("ATTENDANCE_LOG_EMPTY")); + } + + void getMockAttendanceLogsSuccess() { + //MOCK Attendance log search service response + lenient().when(config.getAttendanceLogHost()).thenReturn("http://localhost:8023"); + lenient().when(config.getAttendanceLogEndpoint()).thenReturn("/attendance/log/v1/_search"); + AttendanceLogResponse attendanceLogResponse = MusterRollRequestBuilderTest.getAttendanceLogResponse(); + lenient().when(restTemplate.postForObject(any(String.class),any(Object.class),eq(AttendanceLogResponse.class))). + thenReturn(attendanceLogResponse); + } + + void getMockAttendanceLogsFailure() { + //MOCK Attendance log search service response + lenient().when(config.getAttendanceLogHost()).thenReturn("http://localhost:8023"); + lenient().when(config.getAttendanceLogEndpoint()).thenReturn("/attendance/log/v1/_search"); + AttendanceLogResponse attendanceLogResponse = null; + lenient().when(restTemplate.postForObject(any(String.class),any(Object.class),eq(AttendanceLogResponse.class))). + thenReturn(attendanceLogResponse); + } + + void getMockAttendanceRegister() { + //MOCK Attendance log search service response + lenient().when(config.getAttendanceLogHost()).thenReturn("http://localhost:8023"); + lenient().when(config.getAttendanceRegisterEndpoint()).thenReturn("/attendance/v1/_search"); + AttendanceRegisterResponse attendanceResponse = MusterRollRequestBuilderTest.getAttendanceRegisterResponse(); + lenient().when(restTemplate.postForObject(any(String.class),any(Object.class),eq(AttendanceRegisterResponse.class))). + thenReturn(attendanceResponse); + } + + void getMockIndividualSuccess() { + //MOCK Attendance log search service response + lenient().when(config.getIndividualHost()).thenReturn("http://localhost:8023"); + lenient().when(config.getIndividualSearchEndpoint()).thenReturn("/attendance/log/v1/_search"); + IndividualBulkResponse response = MusterRollRequestBuilderTest.getIndividualResponse(); + lenient().when(restTemplate.postForObject(any(String.class),any(Object.class),eq(IndividualBulkResponse.class))). + thenReturn(response); + } + + void getMockBankDetailsSuccess() { + //MOCK Attendance log search service response + lenient().when(config.getBankaccountsHost()).thenReturn("http://localhost:8023"); + lenient().when(config.getBankaccountsSearchEndpoint()).thenReturn("/attendance/log/v1/_search"); + BankAccountResponse response = MusterRollRequestBuilderTest.getBankDetailsResponse(); + lenient().when(restTemplate.postForObject(any(String.class),any(Object.class),eq(BankAccountResponse.class))). + thenReturn(response); + } + +}**/ diff --git a/health-services/muster-roll/src/test/java/org/egov/service/EnrichmentServiceTest.java b/health-services/muster-roll/src/test/java/org/egov/service/EnrichmentServiceTest.java new file mode 100644 index 0000000000..a6ebef73a8 --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/service/EnrichmentServiceTest.java @@ -0,0 +1,103 @@ +package org.egov.service; + +import lombok.extern.slf4j.Slf4j; +import org.egov.common.contract.idgen.IdGenerationResponse; +import org.egov.common.contract.idgen.IdResponse; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.helper.MusterRollRequestBuilderTest; +import org.egov.repository.IdGenRepository; +import org.egov.repository.MusterRollRepository; +import org.egov.tracer.model.CustomException; +import org.egov.util.MdmsUtil; +import org.egov.util.MusterRollServiceUtil; +import org.egov.web.models.MusterRollRequest; +import org.egov.works.services.common.models.musterroll.Status; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@Slf4j +class EnrichmentServiceTest { + + @InjectMocks + private EnrichmentService enrichmentService; + @Mock + private MdmsUtil mdmsUtils; + @Mock + private MusterRollServiceConfiguration config; + @Mock + private MusterRollServiceUtil musterRollServiceUtil; + @Mock + private IdGenRepository idGenRepository; + @Mock + private MusterRollRepository musterRollRepository; + + + @Test + void shouldGenerateMusterRollId_IfSuccess(){ + idGenResponseSuccess(); + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + enrichmentService.enrichMusterRollOnCreate(musterRollRequest); + assertNotNull(musterRollRequest.getMusterRoll().getId()); + } + + @Test + void shouldGenerateMusterRollNumber_IfSuccess(){ + idGenResponseSuccess(); + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + enrichmentService.enrichMusterRollOnCreate(musterRollRequest); + assertEquals("MR/2022-23/01/05/000131", musterRollRequest.getMusterRoll().getMusterRollNumber()); + } + + @Test + void checkStatusAsActiveForEstimate(){ + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + enrichmentService.enrichMusterRollOnEstimate(musterRollRequest); + assertEquals(Status.ACTIVE.toString(),musterRollRequest.getMusterRoll().getStatus().toString()); + } + + @Test + void shouldThrowException_IfIdgenFailed(){ + idGenResponseFailure(); + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + CustomException exception = assertThrows(CustomException.class, ()-> enrichmentService.enrichMusterRollOnCreate(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("IDGEN ERROR")); + } + + + void idGenResponseSuccess() { + //MOCK Idgen Response + IdResponse idResponse = IdResponse.builder().id("MR/2022-23/01/05/000131").build(); + List idResponses = new ArrayList<>(); + idResponses.add(idResponse); + IdGenerationResponse idGenerationResponse = IdGenerationResponse.builder().idResponses(idResponses).build(); + when(idGenRepository.getId( + MusterRollRequestBuilderTest.builder().getRequestInfo(), + "pb.amritsar", + null, + "", + 1 + )).thenReturn(idGenerationResponse); + } + + + void idGenResponseFailure() { + //MOCK Idgen Response + List idResponses = new ArrayList<>(); + IdGenerationResponse idGenerationResponse = IdGenerationResponse.builder().idResponses(idResponses).build(); + lenient().when(idGenRepository.getId(eq(MusterRollRequestBuilderTest.builder().getRequestInfo()), eq("pb.amritsar"), eq(null), eq(""), eq(1))) + .thenReturn(idGenerationResponse); + } + +} diff --git a/health-services/muster-roll/src/test/java/org/egov/service/MusterRollServiceTest.java b/health-services/muster-roll/src/test/java/org/egov/service/MusterRollServiceTest.java new file mode 100644 index 0000000000..00df05c030 --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/service/MusterRollServiceTest.java @@ -0,0 +1,90 @@ +package org.egov.service; + +import lombok.extern.slf4j.Slf4j; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.helper.MusterRollRequestBuilderTest; +import org.egov.kafka.MusterRollProducer; +import org.egov.repository.MusterRollRepository; +import org.egov.tracer.model.CustomException; +import org.egov.validator.MusterRollValidator; +import org.egov.web.models.MusterRoll; +import org.egov.web.models.MusterRollRequest; +import org.egov.web.models.MusterRollSearchCriteria; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +@Slf4j +class MusterRollServiceTest { + + @InjectMocks + private MusterRollService musterRollService; + @Mock + private MusterRollValidator musterRollValidator; + @Mock + private EnrichmentService enrichmentService; + @Mock + private CalculationService calculationService; + @Mock + private WorkflowService workflowService; + @Mock + private MusterRollProducer musterRollProducer; + @Mock + private MusterRollServiceConfiguration serviceConfiguration; + @Mock + private MusterRollRepository musterRollRepository; + + + @Test + void checkMusterRollRequestCreate_IfValid() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + List musterRolls = null; + lenient().when(musterRollRepository.getMusterRoll(any(MusterRollSearchCriteria.class),any(ArrayList.class))).thenReturn(musterRolls); + musterRollService.createMusterRoll(musterRollRequest); + assertNotNull(musterRollRequest.getMusterRoll()); + } + + @Test + void checkMusterRollRequestEstimate_IfValid() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + List musterRolls = null; + lenient().when(musterRollRepository.getMusterRoll(any(MusterRollSearchCriteria.class),any(ArrayList.class))).thenReturn(musterRolls); + musterRollService.estimateMusterRoll(musterRollRequest); + assertNotNull(musterRollRequest.getMusterRoll()); + } + + @Test + void shouldThrowException_IfDuplicateMusterRoll() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + List musterRolls = new ArrayList<>(); + musterRolls.add(musterRollRequest.getMusterRoll()); + List registerIds = null; + lenient().when(musterRollRepository.getMusterRoll(any(MusterRollSearchCriteria.class),eq(registerIds))).thenReturn(musterRolls); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollService.createMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("DUPLICATE_MUSTER_ROLL")); + } + + @Test + void shouldThrowException_IfWorkflowServiceFails() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + List musterRolls = null; + lenient().when(musterRollRepository.getMusterRoll(any(MusterRollSearchCriteria.class),any(ArrayList.class))).thenReturn(musterRolls); + lenient().when(workflowService.updateWorkflowStatus(any(MusterRollRequest.class))).thenThrow(new CustomException("BUSINESSSERVICE_DOESN'T_EXIST","")); + + assertThrows(CustomException.class, () -> { + musterRollService.createMusterRoll(musterRollRequest); + }); + } + +} diff --git a/health-services/muster-roll/src/test/java/org/egov/validator/MusterRollValidatorTest.java b/health-services/muster-roll/src/test/java/org/egov/validator/MusterRollValidatorTest.java new file mode 100644 index 0000000000..89ded1ba23 --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/validator/MusterRollValidatorTest.java @@ -0,0 +1,156 @@ +package org.egov.validator; + +import lombok.extern.slf4j.Slf4j; +import org.egov.config.MusterRollServiceConfiguration; +import org.egov.helper.MusterRollRequestBuilderTest; +import org.egov.repository.MusterRollRepository; +import org.egov.service.CalculationService; +import org.egov.tracer.model.CustomException; +import org.egov.util.MdmsUtil; +import org.egov.util.MusterRollServiceUtil; +import org.egov.web.models.AttendanceLogResponse; +import org.egov.web.models.AttendanceRegisterResponse; +import org.egov.web.models.MusterRollRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; + +@ExtendWith(MockitoExtension.class) +@Slf4j +class MusterRollValidatorTest { + + @InjectMocks + private MusterRollValidator musterRollValidator; + @Mock + private MusterRollServiceConfiguration serviceConfiguration; + @Mock + private MdmsUtil mdmsUtils; + @Mock + private RestTemplate restTemplate; + + + @BeforeEach + void setUp() throws Exception { + //MOCK MDMS Response + Object mdmsResponse = MusterRollRequestBuilderTest.getTenantMdmsResponse(); + lenient().when(mdmsUtils.mDMSCall(any(MusterRollRequest.class), + any(String.class))).thenReturn(mdmsResponse); + lenient().when(serviceConfiguration.getTimeZone()).thenReturn("Asia/Kolkata"); + + } + + @Test + void shouldNotThrowException_IfCreateValidationSuccess() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + getMockAttendanceRegisterSuccess(); + assertDoesNotThrow(() -> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + } + + @Test + void shouldNotThrowException_IfEstimateValidationSuccess() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + assertDoesNotThrow(() -> musterRollValidator.validateEstimateMusterRoll(musterRollRequest)); + } + + @Test + void shouldThrowException_IfRequestInfoIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.setRequestInfo(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("REQUEST_INFO")); + } + + @Test + void shouldThrowException_IfUserInfoIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.getRequestInfo().setUserInfo(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("USERINFO")); + } + + @Test + void shouldThrowException_IfUUIDIsBlank() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.getRequestInfo().getUserInfo().setUuid(""); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("USERINFO_UUID")); + } + + @Test + void shouldThrowException_IfMusterRollIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.setMusterRoll(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("MUSTER_ROLL")); + } + + @Test + void shouldThrowException_IfTenantIdIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.getMusterRoll().setTenantId(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("TENANT_ID")); + } + + @Test + void shouldThrowException_IfRegisterIdIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.getMusterRoll().setRegisterId(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("REGISTER_ID")); + } + + @Test + void shouldThrowException_IfStartDateIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.getMusterRoll().setStartDate(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("START_DATE_EMPTY")); + } + + @Test + void shouldThrowException_IfStartDateIsNotMonday() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.getMusterRoll().setStartDate(new BigDecimal("1669919400000")); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("START_DATE_MONDAY")); + } + + @Test + void shouldThrowException_IfWorkflowIsNull() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + musterRollRequest.setWorkflow(null); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertTrue(exception.getCode().contentEquals("WORK_FLOW")); + } + + @Test + void shouldThrowException_IfWorkflowActionIsBlank() { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateValidationSuccess(); + getMockAttendanceRegisterSuccess(); + musterRollRequest.getWorkflow().setAction(""); + CustomException exception = assertThrows(CustomException.class, ()-> musterRollValidator.validateCreateMusterRoll(musterRollRequest)); + assertNotNull(exception.getErrors().get("WORK_FLOW.ACTION")); + } + + void getMockAttendanceRegisterSuccess() { + //MOCK Attendance log search service response + lenient().when(serviceConfiguration.getAttendanceLogHost()).thenReturn("http://localhost:8023"); + lenient().when(serviceConfiguration.getAttendanceRegisterEndpoint()).thenReturn("/attendance/v1/_search"); + AttendanceRegisterResponse response = MusterRollRequestBuilderTest.getAttendanceRegisterResponse(); + lenient().when(restTemplate.postForObject(any(String.class),any(Object.class),any())). + thenReturn(response); + } + +} diff --git a/health-services/muster-roll/src/test/java/org/egov/web/controllers/MusterRollApiControllerTest.java b/health-services/muster-roll/src/test/java/org/egov/web/controllers/MusterRollApiControllerTest.java new file mode 100644 index 0000000000..c9500e9d94 --- /dev/null +++ b/health-services/muster-roll/src/test/java/org/egov/web/controllers/MusterRollApiControllerTest.java @@ -0,0 +1,142 @@ +package org.egov.web.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.MusterRollMain; +import org.egov.TestConfiguration; +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.response.ResponseInfo; +import org.egov.helper.MusterRollRequestBuilderTest; +import org.egov.repository.MusterRollRepository; +import org.egov.service.MusterRollService; +import org.egov.tracer.model.CustomException; +import org.egov.tracer.model.ErrorRes; +import org.egov.util.ResponseInfoCreator; +import org.egov.web.models.MusterRollRequest; +import org.egov.web.models.MusterRollResponse; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** +* API tests for MusterRollApiController +*/ + + +@ContextConfiguration(classes= MusterRollMain.class) +@WebMvcTest(MusterRollApiController.class) +@Import({TestConfiguration.class}) +@AutoConfigureMockMvc +class MusterRollApiControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private MusterRollService musterRollService; + + @MockBean + private ResponseInfoCreator responseInfoCreator; + + @MockBean + private MusterRollRepository musterRollRepository; + + + @Test + void musterRollV1EstimatePostSuccess() throws Exception { + + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + ResponseInfo responseInfo = MusterRollRequestBuilderTest.builder().getResponseInfo_Success(); + when(musterRollService.estimateMusterRoll(any(MusterRollRequest.class))).thenReturn(musterRollRequest); + when(responseInfoCreator.createResponseInfoFromRequestInfo(any(RequestInfo.class), eq(true))) + .thenReturn(responseInfo); + ObjectMapper objectMapper = new ObjectMapper(); + String content = objectMapper.writeValueAsString(musterRollRequest); + MvcResult result = mockMvc.perform(post("/v1/_estimate").contentType(MediaType + .APPLICATION_JSON_UTF8).content(content)) + .andExpect(status().isOk()).andReturn(); + String responseStr = result.getResponse().getContentAsString(); + MusterRollResponse response = objectMapper.readValue(responseStr, + MusterRollResponse.class); + assertEquals(1, response.getMusterRolls().size()); + assertEquals("successful", response.getResponseInfo().getStatus()); + } + + @Test + void musterRollV1EstimatePostFailure() throws Exception { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateException(); + ResponseInfo responseInfo = MusterRollRequestBuilderTest.builder().getResponseInfo_Success(); + when(musterRollService.estimateMusterRoll(any(MusterRollRequest.class))).thenThrow(new CustomException("END_DATE_EMPTY","EndDate is mandatory")); + when(responseInfoCreator.createResponseInfoFromRequestInfo(any(RequestInfo.class), eq(true))) + .thenReturn(responseInfo); + ObjectMapper objectMapper = new ObjectMapper(); + String content = objectMapper.writeValueAsString(musterRollRequest); + MvcResult result = mockMvc.perform(post("/v1/_estimate").contentType(MediaType + .APPLICATION_JSON_UTF8).content(content)) + .andExpect(status().isBadRequest()).andReturn(); + String responseStr = result.getResponse().getContentAsString(); + ErrorRes response = objectMapper.readValue(responseStr, + ErrorRes.class); + + assertEquals(1, response.getErrors().size()); + assertEquals("END_DATE_EMPTY",response.getErrors().get(0).getCode()); + } + + @Test + void musterRollV1CreatePostSuccess() throws Exception { + + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateSuccess(); + ResponseInfo responseInfo = MusterRollRequestBuilderTest.builder().getResponseInfo_Success(); + when(musterRollService.createMusterRoll(any(MusterRollRequest.class))).thenReturn(musterRollRequest); + when(responseInfoCreator.createResponseInfoFromRequestInfo(any(RequestInfo.class), eq(true))) + .thenReturn(responseInfo); + ObjectMapper objectMapper = new ObjectMapper(); + String content = objectMapper.writeValueAsString(musterRollRequest); + MvcResult result = mockMvc.perform(post("/v1/_create").contentType(MediaType + .APPLICATION_JSON_UTF8).content(content)) + .andExpect(status().isOk()).andReturn(); + String responseStr = result.getResponse().getContentAsString(); + MusterRollResponse response = objectMapper.readValue(responseStr, + MusterRollResponse.class); + + assertEquals(1, response.getMusterRolls().size()); + assertEquals("successful", response.getResponseInfo().getStatus()); + } + + @Test + void musterRollV1CreatePostFailure() throws Exception { + MusterRollRequest musterRollRequest = MusterRollRequestBuilderTest.builder().withMusterForCreateException(); + ResponseInfo responseInfo = MusterRollRequestBuilderTest.builder().getResponseInfo_Success(); + when(musterRollService.createMusterRoll(any(MusterRollRequest.class))).thenThrow(new CustomException("DUPLICATE_MUSTER_ROLL","Muster roll already exists for this register and date")); + when(responseInfoCreator.createResponseInfoFromRequestInfo(any(RequestInfo.class), eq(true))) + .thenReturn(responseInfo); + ObjectMapper objectMapper = new ObjectMapper(); + String content = objectMapper.writeValueAsString(musterRollRequest); + MvcResult result = mockMvc.perform(post("/v1/_create").contentType(MediaType + .APPLICATION_JSON_UTF8).content(content)) + .andExpect(status().isBadRequest()).andReturn(); + String responseStr = result.getResponse().getContentAsString(); + ErrorRes response = objectMapper.readValue(responseStr, + ErrorRes.class); + + assertEquals(1, response.getErrors().size()); + assertEquals("DUPLICATE_MUSTER_ROLL",response.getErrors().get(0).getCode()); + } + +} diff --git a/health-services/muster-roll/src/test/resources/MusterRollMDMSData.json b/health-services/muster-roll/src/test/resources/MusterRollMDMSData.json new file mode 100644 index 0000000000..365ae4761a --- /dev/null +++ b/health-services/muster-roll/src/test/resources/MusterRollMDMSData.json @@ -0,0 +1,65 @@ +{ + "ResponseInfo": null, + "MdmsRes": { + "common-masters": { + "WageSeekerSkills": [ + { + "name": "Unskilled", + "code": "UNSKILLED", + "active": true, + "amount": 2000 + }, + { + "name": "Skilled Level 1", + "code": "SKILLED_LEVEL_1", + "active": true, + "amount": 2000 + }, + { + "name": "Skilled Level 2", + "code": "SKILLED_LEVEL_2", + "active": true, + "amount": 2000 + }, + { + "name": "Skilled Level 3", + "code": "SKILLED_LEVEL_3", + "active": true, + "amount": 1000 + }, + { + "name": "Skilled Level 4", + "code": "SKILLED_LEVEL_4", + "active": true, + "amount": 1000 + } + ], + "MusterRoll": [ + { + "code": "ENTRY_HOUR", + "value": "9" + }, + { + "code": "EXIT_HOUR_HALF_DAY", + "value": "14" + }, + { + "code": "EXIT_HOUR_FULL_DAY", + "value": "18" + }, + { + "code": "HALF_DAY_NUM_HOURS", + "value": "4" + }, + { + "code": "FULL_DAY_NUM_HOURS", + "value": "8" + }, + { + "code": "ROUND_OFF_HOURS", + "value": "true" + } + ] + } + } +} \ No newline at end of file diff --git a/health-services/muster-roll/src/test/resources/TenantMDMSData.json b/health-services/muster-roll/src/test/resources/TenantMDMSData.json new file mode 100644 index 0000000000..a28cc965f7 --- /dev/null +++ b/health-services/muster-roll/src/test/resources/TenantMDMSData.json @@ -0,0 +1,402 @@ +{ + "ResponseInfo": null, + "MdmsRes": { + "tenant": { + "tenants": [ + { + "code": "pb", + "name": "Punjab", + "description": "Punjab", + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.jalandhar/logo.png", + "imageId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.jalandhar/logo.png", + "domainUrl": "www.mcjalandhar.in", + "type": "CITY", + "twitterUrl": "https://twitter.com/search?q=%23jalandhar", + "facebookUrl": "https://www.facebook.com/city/jalandhar-Punjab", + "emailId": "complaints.mcj@gmail.com", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM" + }, + "city": { + "name": "Punjab", + "localName": "Punjab", + "districtCode": "0", + "districtName": "Punjab", + "districtTenantCode": "pb.punjab", + "regionName": "Punjab", + "ulbGrade": "ST", + "longitude": 75.3412, + "latitude": 31.1471, + "shapeFileLocation": null, + "captcha": null, + "code": "0", + "ddrName": null + }, + "address": "5 Punjab Municipal Bhawan, 3, Dakshin Marg, 35A, Sector 35A, Chandigarh, 160022", + "pincode": [], + "contactNumber": "0181-2227015", + "pdfHeader": "PB_PDF_HEADER", + "pdfContactDetails": "PB_CONTACT_DETAILS" + }, + { + "code": "pb.jalandhar", + "name": "Jalandhar", + "description": "Jalandhar", + "logoId": "", + "imageId": "", + "domainUrl": "www.mcjalandhar.in", + "type": "CITY", + "twitterUrl": "https://twitter.com/search?q=%23jalandhar", + "facebookUrl": "https://www.facebook.com/city/jalandhar-Punjab", + "emailId": "complaints.mcj@gmail.com", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM" + }, + "city": { + "name": "Jalandhar", + "localName": "Janlandhar", + "districtCode": "10", + "districtName": "Jalandhar", + "districtTenantCode": "pb.jalandhar", + "regionName": "Jalandhar Region", + "ulbGrade": "Municipal Corporation", + "ulbType": "Municipal Corporation", + "longitude": 75.5761829, + "latitude": 31.3260152, + "shapeFileLocation": null, + "captcha": null, + "code": "1013", + "regionCode": "4", + "municipalityName": "Janlandhar", + "ddrName": "Jalandhar-MC" + }, + "address": "Municipal Corporation Office, Dr. B.R.Ambedkar Admin Complex, Nehru Garden, Jalandhar City-144001", + "pincode": [ + 144001, + 144002, + 144003, + 144004, + 144005, + 144006, + 144007, + 144008, + 144009, + 144010 + ], + "contactNumber": "0181-2227015", + "pdfHeader": "PB_JALANDHAR_PDF_HEADER", + "pdfContactDetails": "PB_JALANDHAR_CONTACT_DETAILS" + }, + { + "code": "pb.phagwara", + "name": "Phagwara", + "description": "Phagwara", + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.phagwara/logo.png", + "imageId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.phagwara/logo.png", + "domainUrl": "http://www.mcphagwara.com/index.php", + "type": "CITY", + "twitterUrl": null, + "facebookUrl": null, + "emailId": "comm.mcpgr@punjab.gov.in", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM" + }, + "city": { + "name": "Phagwara", + "localName": "Phagwara", + "districtCode": "10", + "districtName": "Jalandhar", + "districtTenantCode": "pb.jalandhar", + "regionName": "Jalandhar Region", + "ulbGrade": "Municipal Corporation", + "ulbType": "Municipal Corporation", + "longitude": 75.7708, + "latitude": 31.224, + "shapeFileLocation": null, + "captcha": null, + "code": "1014", + "regionCode": "4", + "municipalityName": "Phagwara", + "ddrName": "Phagwara-MC" + }, + "address": "Town Hall, Phagwara, Punjab - 144401", + "pincode": [ + 144401, + 144402, + 144403 + ], + "contactNumber": "01824-260426", + "pdfHeader": "PB_PHAGWARA_PDF_HEADER", + "pdfContactDetails": "PB_PHAGWARA_CONTACT_DETAILS" + }, + { + "code": "pb.amritsar", + "name": "Amritsar", + "description": null, + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.amritsar/logo.png", + "imageId": "/pb-egov-assets/pb.amritsar/logo.png", + "domainUrl": "www.amritsarcorp.com", + "type": "CITY", + "twitterUrl": "https://twitter.com/search?q=%23AMRITSAR", + "facebookUrl": "https://www.facebook.com/city/Amritsar-Punjab", + "emailId": "cmcasr@gmail.com", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM", + "Sat": "9.00 AM - 12.00 PM" + }, + "city": { + "name": "Amritsar", + "localName": "Amritsar", + "districtCode": "1", + "districtName": "Amritsar", + "districtTenantCode": "pb.amritsar", + "regionName": "Amritsar Region", + "ulbGrade": "Municipal Corporation", + "ulbType": "Municipal Corporation", + "longitude": 74.8723, + "latitude": 31.634, + "shapeFileLocation": null, + "captcha": null, + "code": "107", + "regionCode": "1", + "municipalityName": "Amritsar", + "ddrName": "Amritsar-MC" + }, + "address": "Municipal Corporation Amritsar, C Block, Ranjit Avenue, Amritsar, Punjab", + "pincode": [ + 143001, + 143002, + 143003, + 143004, + 143005, + 143006, + 143007, + 143008, + 143009, + 143010 + ], + "contactNumber": "0183-2545155", + "helpLineNumber": "0183-2210300", + "pdfHeader": "PB_AMRITSAR_PDF_HEADER", + "pdfContactDetails": "PB_AMRITSAR_CONTACT_DETAILS" + }, + { + "code": "pb.nawanshahr", + "name": "Nawanshahr", + "description": null, + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.nawanshahr/logo.png", + "imageId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.nawanshahr/logo.png", + "domainUrl": "www.nawanshahr.com", + "type": "CITY", + "twitterUrl": null, + "facebookUrl": null, + "emailId": "", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM" + }, + "city": { + "name": "Nawanshahr", + "localName": "Nawanshahr", + "districtCode": "17", + "districtName": "Nawanshar", + "districtTenantCode": "pb.nawanshar", + "regionName": "Jalandhar Region", + "ulbGrade": "MC Class I", + "ulbType": "Municipal Council", + "longitude": 76.0392, + "latitude": 31.0913, + "shapeFileLocation": null, + "captcha": null, + "code": "1703", + "regionCode": "4", + "municipalityName": "SBS Nagar", + "ddrName": "Jalandhar-DDR" + }, + "address": "Committee Bazar", + "pincode": [ + 144513, + 144514, + 144516, + 144517 + ], + "contactNumber": "1823220085", + "helpLineNumber": "", + "pdfHeader": "PB_NAWANSHAHR_PDF_HEADER", + "pdfContactDetails": "PB_NAWANSHAHR_CONTACT_DETAILS" + }, + { + "code": "pb.mohali", + "name": "Mohali", + "description": "Mohali", + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.mohali/logo.png", + "imageId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.mohali/logo.png", + "domainUrl": "www.mcjalandhar.in", + "type": "CITY", + "twitterUrl": "https://twitter.com/search?q=%23mohali", + "facebookUrl": "https://www.facebook.com/city/mohali-Punjab", + "emailId": "complaints.mcj@gmail.com", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM" + }, + "city": { + "name": "Mohali", + "localName": "Mohali", + "districtCode": "15", + "districtName": "Mohali", + "districtTenantCode": "pb.mohali", + "regionName": "Patiala Region", + "ulbGrade": "Municipal Corporation", + "ulbType": "Municipal Corporation", + "longitude": 76.7179, + "latitude": 30.7046, + "shapeFileLocation": null, + "captcha": null, + "code": "1508", + "regionCode": "6", + "municipalityName": "SAS Nagar", + "ddrName": "Mohali-MC" + }, + "address": "Sector 68, Sahibzada Ajit Singh Nagar, Punjab 160062", + "pincode": [ + 140301, + 140302, + 140303, + 140304, + 140305, + 140306, + 140307 + ], + "contactNumber": "0172-5044907", + "pdfHeader": "PB_MOHALI_PDF_HEADER", + "pdfContactDetails": "PB_MOHALI_CONTACT_DETAILS" + }, + { + "code": "pb.nayagaon", + "name": "Nayagaon", + "description": "Nayagaon", + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.nayagaon/logo.png", + "imageId": null, + "domainUrl": "http://lgpunjab.gov.in/eSewa/nayagaon", + "type": "CITY", + "twitterUrl": null, + "facebookUrl": null, + "emailId": "eonpnayagaon@ymail.com", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 5.00 PM" + }, + "city": { + "name": "Nayagaon", + "localName": "NayaGaon", + "districtCode": "15", + "districtName": "Mohali", + "districtTenantCode": "pb.mohali", + "regionName": "Patiala Region", + "ulbGrade": "MC Class II", + "ulbType": "Municipal Council", + "longitude": 76.7943, + "latitude": 30.7761, + "shapeFileLocation": null, + "captcha": null, + "code": "1506", + "regionCode": "6", + "municipalityName": "NayaGaon", + "ddrName": "Patiala-DDR" + }, + "address": "Not available", + "pincode": [ + 147001, + 147002, + 147003, + 147004, + 147005 + ], + "contactNumber": "Not available", + "pdfHeader": "PB_NAYAGAON_PDF_HEADER", + "pdfContactDetails": "PB_NAYAGAON_CONTACT_DETAILS" + }, + { + "code": "pb.derabassi", + "name": "Derabassi", + "description": "Derabassi", + "logoId": "https://s3.ap-south-1.amazonaws.com/pb-egov-assets/pb.derabassi/logo.png", + "imageId": null, + "domainUrl": "http://lgpunjab.gov.in/eSewa/derabassi", + "type": "CITY", + "twitterUrl": null, + "facebookUrl": null, + "emailId": "eomcdera@gmail.com", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 5.00 PM" + }, + "city": { + "name": "Derabassi", + "localName": "DeraBassi", + "districtCode": "15", + "districtName": "Mohali", + "districtTenantCode": "pb.mohali", + "regionName": "Patiala Region", + "ulbGrade": "MC Class I", + "ulbType": "Municipal Council", + "longitude": 76.8433, + "latitude": 30.5964, + "shapeFileLocation": null, + "captcha": null, + "code": "1502", + "regionCode": "6", + "municipalityName": "DerraBassi", + "ddrName": "Patiala-DDR" + }, + "address": "Not available", + "pincode": [ + 140506, + 140507 + ], + "contactNumber": "1762281025", + "pdfHeader": "PB_DERABASSI_PDF_HEADER", + "pdfContactDetails": "PB_DERABASSI_CONTACT_DETAILS" + }, + { + "code": "pb.patiala", + "name": "Patiala", + "description": "Patiala", + "logoId": "http://mseva.lgpunjab.gov.in/pb-egov-assets/pb.patiala/logo.png", + "imageId": null, + "domainUrl": "https://patiala.nic.in/", + "type": "CITY", + "twitterUrl": null, + "facebookUrl": null, + "emailId": "dc.ptl@punjab.gov.in", + "OfficeTimings": { + "Mon - Fri": "9.00 AM - 6.00 PM" + }, + "city": { + "name": "Patiala", + "localName": "Patiala", + "districtCode": "19", + "districtName": "Patiala", + "districtTenantCode": "pb.patiala", + "regionName": "Patiala Region", + "ulbGrade": "Municipal Corporation", + "ulbType": "Municipal Corporation", + "longitude": 76.3869, + "latitude": 30.3398, + "shapeFileLocation": null, + "captcha": null, + "code": "1910", + "regionCode": "6", + "municipalityName": "Patiala", + "ddrName": "Patiala-MC" + }, + "address": "Office of the Deputy Commissioner District Administrative Complex, Patiala-147001", + "contactNumber": "0175-2311300", + "pincode": [ + 140506, + 140507 + ], + "pdfHeader": "PB_PATIALA_PDF_HEADER", + "pdfContactDetails": "PB_PATIALA_CONTACT_DETAILS" + } + ] + } + } +} \ No newline at end of file