diff --git a/build/build-config.yml b/build/build-config.yml index bd81babfa..98545f953 100644 --- a/build/build-config.yml +++ b/build/build-config.yml @@ -118,3 +118,10 @@ config: dockerfile: "build/maven/Dockerfile" - work-dir: "municipal-services/property-services/src/main/resources/db" image-name: "property-services-db" + - name: "builds/mGramSeva/business-services/egov-apportion-service" + build: + - work-dir: "business-services/egov-apportion-service" + image-name: "egov-apportion-service" + dockerfile: "build/maven/Dockerfile" + - work-dir: "business-services/egov-apportion-service/src/main/resources/db" + image-name: "egov-apportion-service-db" diff --git a/business-services/egov-apportion-service/Apportion.postman_collection.json b/business-services/egov-apportion-service/Apportion.postman_collection.json new file mode 100644 index 000000000..8ae77190d --- /dev/null +++ b/business-services/egov-apportion-service/Apportion.postman_collection.json @@ -0,0 +1,40 @@ +{ + "info": { + "_postman_id": "199db000-e1ea-41e0-a559-2fca1e4d5d9d", + "name": "Apportion", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "apportion", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"RequestInfo\": {\n \"apiId\": \"Rainmaker\",\n \"action\": \"\",\n \"did\": 1,\n \"key\": \"\",\n \"msgId\": \"20170310130900|en_IN\",\n \"requesterId\": \"\",\n \"ts\": \"\",\n \"ver\": \".01\",\n \"userInfo\": {\n \n \"id\": 104,\n \"uuid\": \"1dc1db2c-6f26-4803-a3a7-028eb4abff6e\",\n \"userName\": \"9999999999\",\n \"name\": \"dfghj\",\n \"gender\": null,\n \"mobileNumber\": \"9999999999\",\n \"type\": \"CITIZEN\",\n \"roles\": [\n {\n \"id\": null,\n \"name\": \"Citizen\",\n \"code\": \"CITIZEN\"\n }\n ],\n \"tenantId\": \"pb\"\n },\n \"authToken\": \"7f7d617b-f7b4-4e2c-907d-dfd0256f2331\"\n },\n \"tenantId\":\"pb.amritsar\",\n \"Bills\":[\n {\n \"id\": \"10090\",\n \"payeeName\": \"NAMEchanged\",\n \"payeeAddress\": \"ADDRESS\",\n \"mobileNumber\": \"9999999999\",\n \"payeeEmail\": null,\n \"isActive\": true,\n \"isCancelled\": false,\n \"collectionMap\": {\n \"PT\": \"213409\"\n },\n \"billDetails\": [\n {\n \"id\": \"10166\",\n \"bill\": \"10090\",\n \"billDate\": 1551249289654,\n \"billDescription\": \"PT Consumer Code: PT-107-016526:AS-2019-02-27-016985\",\n \"billNumber\": \"10166\",\n \"consumerCode\": \"PT-107-016526:AS-2019-02-27-016985\",\n \"consumerType\": \"BUILTUP\",\n \"minimumAmount\": 100,\n \"totalAmount\": 213409,\n \"collectedAmount\": 0,\n \"collectionModesNotAllowed\": [\n \"\"\n ],\n \"tenantId\": \"pb.amritsar\",\n \"businessService\": \"PT\",\n \"displayMessage\": \"PT Consumer Code: PT-107-016526:AS-2019-02-27-016985\",\n \"callBackForApportioning\": false,\n \"receiptNumber\": null,\n \"receiptDate\": null,\n \"receiptType\": null,\n \"channel\": null,\n \"voucherHeader\": null,\n \"boundary\": null,\n \"reasonForCancellation\": null,\n \"amountPaid\": null,\n \"cancellationRemarks\": null,\n \"status\": null,\n \"billAccountDetails\": [\n {\n \"id\": \"37770\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1100101\",\n \"order\": 2,\n \"accountDescription\": \"PT_CANCER_CESS-1522540800000-1554076799000\",\n \"amount\": 8750,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"CURRENT_AMOUNT\"\n },\n {\n \"id\": \"37771\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405019\",\n \"order\": 99,\n \"accountDescription\": \"PT_DECIMAL_CEILING_CREDIT-1522540800000-1554076799000\",\n \"amount\": 0.1,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"CURRENT_AMOUNT\"\n },\n {\n \"id\": \"37772\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405013\",\n \"order\": 2,\n \"accountDescription\": \"PT_FIRE_CESS-1522540800000-1554076799000\",\n \"amount\": 8750,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"CURRENT_AMOUNT\"\n },\n {\n \"id\": \"37773\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405015\",\n \"order\": 1,\n \"accountDescription\": \"PT_OWNER_EXEMPTION-1522540800000-1554076799000\",\n \"amount\": -5000,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"EXEMPTION\"\n },\n {\n \"id\": \"37774\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405013\",\n \"order\": 3,\n \"accountDescription\": \"PT_TAX-1522540800000-1554076799000\",\n \"amount\": 180000,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"CURRENT_AMOUNT\"\n },\n {\n \"id\": \"37775\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405019\",\n \"order\": 10,\n \"accountDescription\": \"PT_TIME_INTEREST-1522540800000-1554076799000\",\n \"amount\": 3408.9,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"OTHERS\"\n },\n {\n \"id\": \"37776\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405013\",\n \"order\": 1,\n \"accountDescription\": \"PT_TIME_PENALTY-1522540800000-1554076799000\",\n \"amount\": 17500,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"OTHERS\"\n },\n {\n \"id\": \"37777\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405016\",\n \"order\": 1,\n \"accountDescription\": \"PT_TIME_REBATE-1522540800000-1554076799000\",\n \"amount\": 0,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"REBATE\"\n },\n {\n \"id\": \"37778\",\n \"tenantId\": \"pb.amritsar\",\n \"billDetail\": \"10166\",\n \"glcode\": \"1405019\",\n \"order\": 100,\n \"accountDescription\": \"PT_UNIT_USAGE_EXEMPTION-1522540800000-1554076799000\",\n \"amount\": 0,\n \"adjustedAmount\": 0,\n \"isActualDemand\": true,\n \"purpose\": \"EXEMPTION\"\n }\n ],\n \"manualReceiptNumber\": null,\n \"stateId\": null\n } \n ],\n \"tenantId\": \"pb.amritsar\",\n \"auditDetails\": null\n }\n \t]\n\n}" + }, + "url": { + "raw": "https://egov-micro-dev.egovernments.org/apportion-service/v1/_apportion", + "protocol": "https", + "host": [ + "egov-micro-dev", + "egovernments", + "org" + ], + "path": [ + "apportion-service", + "v1", + "_apportion" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/CHANGELOG.md b/business-services/egov-apportion-service/CHANGELOG.md new file mode 100644 index 000000000..46f281803 --- /dev/null +++ b/business-services/egov-apportion-service/CHANGELOG.md @@ -0,0 +1,37 @@ + + +# Changelog +All notable changes to this module will be documented in this file. +## 1.1.5 - 2023-02-02 + +- Transition from 1.1.5-beta version to 1.1.5 version + +## 1.1.5-beta - 2022-01-13 +- Updated to log4j2 version 2.17.1 + +## 1.1.4 2021-02-26 + +- Updated domain name in application.properties + +## 1.1.3 2021-01-12 + +- Apportion and backupdate to demands enabled for zero payments, which was blocked till this point of time + +## 1.1.3 + +## 1.1.2 2020-08-24 +- Fixed Bug RAIN-1432: Added audit for demand apportion + +## 1.1.1 - 2020-06-17 + +- Fixed Bug WOR-415: Regarding adjustedAmount during multiple advance adjustment + +## 1.1.0 - 2020-06-25 +- Added typescript definition generation plugin +- Upgraded to `tracer:2.0.0-SNAPSHOT` +- Upgraded to spring boot `2.2.6-RELEASE` +- Deleted `Dockerfile` and `start.sh` as it is no longer in use + +## 1.0.0 + +- Base version diff --git a/business-services/egov-apportion-service/LOCALSETUP.md b/business-services/egov-apportion-service/LOCALSETUP.md new file mode 100644 index 000000000..e696a7854 --- /dev/null +++ b/business-services/egov-apportion-service/LOCALSETUP.md @@ -0,0 +1,37 @@ +# Local Setup + +To setup the egov-apportion-service service in your local system, clone the [Business Service repository](https://github.com/egovernments/business-services). + +## Dependencies + +### Infra Dependency + +- [x] Postgres DB +- [ ] Redis +- [ ] Elasticsearch +- [x] Kafka + - [ ] Consumer + - [x] Producer + +## Running Locally + +To run the egov-apportion-service in local system, you need to port forward below services. + +```bash + kubectl port-forward -n egov {egov-mdms} 8088:8080 +``` + +Update below listed properties in `application.properties` before running the project: + +```ini + +-spring.datasource.url=jdbc:postgresql://localhost:5432/{local postgres db name} + +-spring.flyway.url=jdbc:postgresql://localhost:5432/{local postgres db name} + +-egov.mdms.host={mdms hostname} + +``` + +Optionally egov=persister should be ran locally if auditing is required and following config path should be added in it: + (https://raw.githubusercontent.com/egovernments/configs/master/egov-persister/apportion-persister.yml) \ No newline at end of file diff --git a/business-services/egov-apportion-service/README.md b/business-services/egov-apportion-service/README.md new file mode 100644 index 000000000..f8d6cda41 --- /dev/null +++ b/business-services/egov-apportion-service/README.md @@ -0,0 +1,73 @@ + + +# eGov ApportionService + + +Module is used to distribute the paid amount among the taxHeads. + +### Apportion +The Apportioning service is used to distribute the paid amount among the respective taxHead. +There are two API's first is /apportion-service/bill/_apportion endpoint which is used to apportion the bill. In this case each bill is processed seperately. +The billDetails in each bill are grouped by businessService. For each group the apportionPaidAmount() function is called to apportion the paid amount. +The second endpoint is /apportion-service/demand/_apportion which is used to apportion advance amount in demands. Demands for the same consumer code should be sent for the apportion. +The interface Apportion is to be implemented for custom logic. Default implementation is provided in class called OrderByPriorityApportion, +Default Implementation: + 1. Apportions the paid amount based on the order in each taxhead (ascending wise priority) + 2. If multiple billDetails are present they are sorted by fromPeriod and the oldest one is apportioned first + 3. Validation is placed to enforce that all taxHeads containing negative amounts should have order less than the one with positive amounts + 4. Any advance amount is added to the latest billDetail as new billAccountDetail +For custom implementation the methods getBusinessService() and apportionPaidAmount() has to be implemented. The first method returns key of the implementation while the second method contains the apportion logic for the particular key +The apportion request and respose are stored for audit using persister + + + +### Service Dependencies +- egov-mdms +- egov-persister + + +### Project Structure +*Packages* + - config - Contains all the configuration properties related to module + - service - Consists of all services containing the business logic. + - util - Contains utility functions and constants. + - repository - Fetch data from dependent micro services + - web/controllers - Controllers for the app. + - web/models - POJO for the module. + - producer - Contains kafka producer + + +### Resources +- Granular details about the API's can be found in the [swagger api definition](https://raw.githubusercontent.com/egovernments/docs/collections/contracts/apportion/egov-apportion-service.yml) +- Postman collection for all the API's can be found in the [postman collection](https://raw.githubusercontent.com/egovernments/egov-services/core/egov-apportion-service/Apportion.postman_collection.json) + + +## Build & Run + + + mvn clean install + java -jar target/egov-apportion-service-1.1.1-SNAPSHOT.jar + + + +### API Details + +`BasePath` /apportion-service/v2/[API endpoint] + +##### Method +**a) Apportion Bill `POST /bill/_apportion` :** API (Bulk API) Apportions the paid amount in the field collectedAmount of billDetails in the bill +**b) Apportion demands `POST /demand/_apportion` :** API Apportions the advance amount from previous billing cycles in the latest demands + + +### Kafka Consumers + +- NA + + +### Kafka Producers + +- Following are the Producer topic. + - **save-apportion-bill-request** :- This topic is used to save the bill apportion request for audit. + - **save-apportion-bill-response** :- This topic is used to save the bill apportion response for audit. + - **save-apportion-demand-request** :- This topic is used to save the demand apportion request for audit. + - **save-apportion-demand-response** :- This topic is used to save the demand apportion response for audit. diff --git a/business-services/egov-apportion-service/pom.xml b/business-services/egov-apportion-service/pom.xml new file mode 100644 index 000000000..0a33e1589 --- /dev/null +++ b/business-services/egov-apportion-service/pom.xml @@ -0,0 +1,176 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + org.egov + egov-apportion-service + 1.1.5-SNAPSHOT + egov-apportion-service + + 2.17.1 + 1.8 + ${java.version} + 1.18.8 + ${java.version} + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework + spring-beans + 5.2.20.RELEASE + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-test + test + + + io.swagger + swagger-core + 1.5.18 + + + org.flywaydb + flyway-core + + + org.projectlombok + lombok + true + + + org.egov.services + services-common + 1.0.0-RELEASE + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + org.postgresql + postgresql + 42.2.2.jre7 + + + javax.validation + validation-api + + + org.egov.services + tracer + 2.0.0-SNAPSHOT + compile + + + org.egov + mdms-client + 0.0.2-SNAPSHOT + compile + + + + + 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/ + + + + src/main/java + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.projectlombok + lombok + + + org.springframework.boot + spring-boot-devtools + + + + + + cz.habarta.typescript-generator + typescript-generator-maven-plugin + 2.22.595 + + + generate + + generate + + process-classes + + + + jackson2 + + org.egov.web.models.ApportionDemandResponse + org.egov.web.models.ApportionRequest + org.egov.web.models.ApportionResponse + org.egov.web.models.ApportionRequestV2 + org.egov.web.models.Bill + org.egov.web.models.BillAccountDetail + org.egov.web.models.BillDetail + org.egov.web.models.Demand + org.egov.web.models.DemandDetail + org.egov.web.models.Role + org.egov.web.models.TaxHeadMaster + org.egov.web.models.AuditDetails + org.egov.web.models.enums.Category + org.egov.web.models.enums.DemandApportionRequest + org.egov.web.models.enums.Purpose + org.egov.web.models.enums.ReceiptType + + + org.egov.web.models.Demand$StatusEnum:DemandStatusEnum + + + org.egov.common.contract.request.User:User + org.egov.common.contract.request.RequestInfo:RequestInfo + org.egov.common.contract.response.ResponseInfo:ResponseInfo + + Digit + true + module + + + + + diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/ApportionApp.java b/business-services/egov-apportion-service/src/main/java/org/egov/ApportionApp.java new file mode 100644 index 000000000..cd931b839 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/ApportionApp.java @@ -0,0 +1,37 @@ +package org.egov; + + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.tracer.config.TracerConfiguration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Import; + +import java.util.TimeZone; + +@SpringBootApplication +@ComponentScan(basePackages = { "org.egov", "org.egov.web.controllers" , "org.egov.config"}) +@Import({ TracerConfiguration.class }) +public class ApportionApp { + + @Value("${app.timezone}") + private String timeZone; + + @Bean + public ObjectMapper objectMapper(){ + return new ObjectMapper() + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .setTimeZone(TimeZone.getTimeZone(timeZone)); + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(ApportionApp.class, args); + } + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/config/ApportionConfig.java b/business-services/egov-apportion-service/src/main/java/org/egov/config/ApportionConfig.java new file mode 100644 index 000000000..0a0484063 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/config/ApportionConfig.java @@ -0,0 +1,69 @@ +package org.egov.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.TimeZone; + +@Data +@Component +public class ApportionConfig { + + @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; + } + + + + //Persister Config + @Value("${persister.save.bill.apportion.request.topic}") + private String billRequestTopic; + + @Value("${persister.save.bill.apportion.response.topic}") + private String billResponseTopic; + + @Value("${persister.save.demand.apportion.request.topic}") + private String demandRequestTopic; + + @Value("${persister.save.demand.apportion.response.topic}") + private String demandResponseTopic; + + //MDMS + @Value("${egov.mdms.host}") + private String mdmsHost; + + @Value("${egov.mdms.search.endpoint}") + private String mdmsEndPoint; + + //Default implementation switch + @Value("${egov.apportion.default.value.order}") + private Boolean apportionByValueAndOrder; + + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/producer/Producer.java b/business-services/egov-apportion-service/src/main/java/org/egov/producer/Producer.java new file mode 100644 index 000000000..118934431 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/producer/Producer.java @@ -0,0 +1,18 @@ +package org.egov.producer; + +import lombok.extern.slf4j.Slf4j; +import org.egov.tracer.kafka.CustomKafkaTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class Producer { + + @Autowired + private CustomKafkaTemplate kafkaTemplate; + + public void push(String topic, Object value) { + kafkaTemplate.send(topic, value); + } +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/repository/ServiceRequestRepository.java b/business-services/egov-apportion-service/src/main/java/org/egov/repository/ServiceRequestRepository.java new file mode 100644 index 000000000..6bf3c5172 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/repository/ServiceRequestRepository.java @@ -0,0 +1,46 @@ +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; + log.info("URI: "+uri.toString()); + try { + log.info("Request: "+mapper.writeValueAsString(request)); + 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; + } +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/Apportion.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/Apportion.java new file mode 100644 index 000000000..bcb2d4a8b --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/Apportion.java @@ -0,0 +1,29 @@ +package org.egov.service; + +import org.egov.web.models.Bill; +import org.egov.web.models.BillDetail; + +import java.math.BigDecimal; +import java.util.*; + +public interface Apportion { + + + + /** + * Should return the code of the BusinessService for which the interface is implemented + * @return Code of the BusinessService + */ + String getBusinessService(); + + + /** + * Distibutes the paid amount among the Bill account details + * @param bill The bill to be apportioned + * @return Apportioned BillDetails + */ + List apportionPaidAmount(Bill bill, Object masterData); + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionService.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionService.java new file mode 100644 index 000000000..c550a7a7f --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionService.java @@ -0,0 +1,125 @@ +package org.egov.service; + +import static org.egov.util.ApportionConstants.DEFAULT; + +import java.math.BigDecimal; +import java.util.*; + +import org.egov.config.ApportionConfig; +import org.egov.producer.Producer; +import org.egov.web.models.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + + +@Service +public class ApportionService { + + private final List apportions; + private Map APPORTION_MAP = new HashMap<>(); + + private Producer producer; + private ApportionConfig config; + private MDMSService mdmsService; + + + @Autowired + public ApportionService(List apportions, Producer producer, + ApportionConfig config, MDMSService mdmsService) { + this.apportions = Collections.unmodifiableList(apportions); + this.producer = producer; + this.config = config; + this.mdmsService = mdmsService; + initialize(); + } + + private void initialize() { + if (Objects.isNull(apportions)) + throw new IllegalStateException("No Apportion found, spring initialization failed."); + + if (APPORTION_MAP.isEmpty() && !apportions.isEmpty()) { + apportions.forEach(apportion -> { + APPORTION_MAP.put(apportion.getBusinessService(), apportion); + }); + } + APPORTION_MAP = Collections.unmodifiableMap(APPORTION_MAP); + } + + + /** + * Apportions the paid amount for the given list of bills + * + * @param request The apportion request + * @return Apportioned Bills + */ + public List apportionBills(ApportionRequest request) { + List bills = request.getBills(); + Apportion apportion; + + //Save the request through persister + producer.push(config.getBillRequestTopic(), request); + + //Fetch the required MDMS data + Object masterData = mdmsService.mDMSCall(request.getRequestInfo(), request.getTenantId()); + + for (Bill bill : bills) { + + // Create a map of businessService to list of billDetails belonging to that businessService + // Map> businessServiceToBillDetails = util.groupByBusinessService(billInfo.getBillDetails()); + + bill.getBillDetails().sort(Comparator.comparing(BillDetail::getFromPeriod)); + + + String businessKey = bill.getBusinessService(); + BigDecimal amountPaid = bill.getAmountPaid(); + + List billDetails = bill.getBillDetails(); + + if (CollectionUtils.isEmpty(billDetails)) + continue; + + // Get the appropriate implementation of Apportion + if (isApportionPresent(businessKey)) + apportion = getApportion(businessKey); + else + apportion = getApportion(DEFAULT); + + /* + * Apportion the paid amount among the given list of billDetail + */ + apportion.apportionPaidAmount(bill, masterData); + } + + + + + //Save the response through persister + producer.push(config.getBillResponseTopic(), request); + return bills; + } + + + /** + * Retrives the apportion for the given businessService + * + * @param businessService The businessService of the billDetails + * @return Apportion object for the given businessService + */ + private Apportion getApportion(String businessService) { + return APPORTION_MAP.get(businessService); + } + + + /** + * Checks if the apportion is present for the given businessService + * + * @param businessService The businessService of the billDetails + * @return True if the apportion is present else false + */ + private Boolean isApportionPresent(String businessService) { + return APPORTION_MAP.containsKey(businessService); + } + + +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionServiceV2.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionServiceV2.java new file mode 100644 index 000000000..9a133c11b --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionServiceV2.java @@ -0,0 +1,274 @@ +package org.egov.service; + +import static org.egov.util.ApportionConstants.DEFAULT; + +import java.math.BigDecimal; +import java.util.*; + +import org.egov.config.ApportionConfig; +import org.egov.producer.Producer; +import org.egov.web.models.*; +import org.egov.web.models.enums.DemandApportionRequest; +import org.egov.web.models.enums.Purpose; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + + +@Service +public class ApportionServiceV2 { + + private final List apportions; + private Map APPORTION_MAP = new HashMap<>(); + + private Producer producer; + private ApportionConfig config; + private MDMSService mdmsService; + private TranslationService translationService; + + + @Autowired + public ApportionServiceV2(List apportions, Producer producer, + ApportionConfig config, MDMSService mdmsService, + TranslationService translationService) { + this.apportions = Collections.unmodifiableList(apportions); + this.producer = producer; + this.config = config; + this.mdmsService = mdmsService; + this.translationService = translationService; + initialize(); + } + + private void initialize() { + if (Objects.isNull(apportions)) + throw new IllegalStateException("No Apportion found, spring initialization failed."); + + if (APPORTION_MAP.isEmpty() && !apportions.isEmpty()) { + apportions.forEach(apportion -> { + APPORTION_MAP.put(apportion.getBusinessService(), apportion); + }); + } + APPORTION_MAP = Collections.unmodifiableMap(APPORTION_MAP); + } + + + /** + * Apportions the paid amount for the given list of bills + * + * @param request The apportion request + * @return Apportioned Bills + */ + public List apportionBills(ApportionRequest request) { + List bills = request.getBills(); + ApportionV2 apportion; + + //Save the request through persister + producer.push(config.getBillRequestTopic(), request); + + //Fetch the required MDMS data + Object masterData = mdmsService.mDMSCall(request.getRequestInfo(), request.getTenantId()); + + for (Bill bill : bills) { + + // Create a map of businessService to list of billDetails belonging to that businessService + // Map> businessServiceToBillDetails = util.groupByBusinessService(billInfo.getBillDetails()); + + bill.getBillDetails().sort(Comparator.comparing(BillDetail::getFromPeriod)); + + + String businessKey = bill.getBusinessService(); + BigDecimal amountPaid = bill.getAmountPaid(); + + List billDetails = bill.getBillDetails(); + + if (CollectionUtils.isEmpty(billDetails)) + continue; + + // Get the appropriate implementation of Apportion + if (isApportionPresent(businessKey)) + apportion = getApportion(businessKey); + else + apportion = getApportion(DEFAULT); + + /* + * Apportion the paid amount among the given list of billDetail + */ + + ApportionRequestV2 apportionRequestV2 = translationService.translate(bill); + List taxDetails = apportion.apportionPaidAmount(apportionRequestV2, masterData, true); + updateAdjustedAmountInBills(bill,taxDetails); + addAdvanceIfExistForBill(billDetails,taxDetails); + } + + //Save the response through persister + producer.push(config.getBillResponseTopic(), request); + return bills; + } + + + /** + * Retrives the apportion for the given businessService + * + * @param businessService The businessService of the billDetails + * @return Apportion object for the given businessService + */ + private ApportionV2 getApportion(String businessService) { + return APPORTION_MAP.get(businessService); + } + + + /** + * Checks if the apportion is present for the given businessService + * + * @param businessService The businessService of the billDetails + * @return True if the apportion is present else false + */ + private Boolean isApportionPresent(String businessService) { + return APPORTION_MAP.containsKey(businessService); + } + + + + /** + * Apportions the paid amount for the given list of demands + * + * @param request The apportion request + * @return Apportioned Bills + */ + public List apportionDemands(DemandApportionRequest request) { + List demands = request.getDemands(); + ApportionV2 apportion; + + //Save the request through persister + producer.push(config.getDemandRequestTopic(), request); + + //Fetch the required MDMS data + Object masterData = mdmsService.mDMSCall(request.getRequestInfo(), request.getTenantId()); + + demands.sort(Comparator.comparing(Demand::getTaxPeriodFrom)); + + ApportionRequestV2 apportionRequestV2 = translationService.translate(demands,masterData); + + + /* + * Need to validate that all demands that come for apportioning + * has same businessService and consumerCode + * */ + String businessKey = demands.get(0).getBusinessService(); + + if (isApportionPresent(businessKey)) + apportion = getApportion(businessKey); + else + apportion = getApportion(DEFAULT); + + List taxDetails = apportion.apportionPaidAmount(apportionRequestV2, masterData, false); + updateAdjustedAmountInDemands(demands,taxDetails); + addAdvanceIfExistForDemand(demands,taxDetails); + + + + //Save the response through persister + producer.push(config.getDemandResponseTopic(), request); + return demands; + } + + + /** + * Updates adjusted amount in demand from mao returned after apportion + * @param demands + * @param taxDetails + */ + private void updateAdjustedAmountInDemands(List demands,List taxDetails){ + + Map idToAdjustedAmount = new HashMap<>(); + taxDetails.forEach(taxDetail -> { + taxDetail.getBuckets().forEach(bucket -> { + idToAdjustedAmount.put(bucket.getEntityId(),bucket.getAdjustedAmount()); + }); + }); + + demands.forEach(demand -> { + demand.getDemandDetails().forEach(demandDetail -> { + demandDetail.setCollectionAmount(idToAdjustedAmount.get(demandDetail.getId())); + }); + }); + + } + + /** + * Updates adjusted amount in bill from mao returned after apportion + * @param bill + * @param taxDetails + */ + private void updateAdjustedAmountInBills(Bill bill,List taxDetails){ + + Map idToBucket = new HashMap<>(); + Map idToAmountPaid = new HashMap<>(); + + taxDetails.forEach(taxDetail -> { + idToAmountPaid.put(taxDetail.getEntityId(),taxDetail.getAmountPaid()); + taxDetail.getBuckets().forEach(bucket -> { + idToBucket.put(bucket.getEntityId(),bucket); + }); + }); + + bill.getBillDetails().forEach(billDetail -> { + billDetail.setAmountPaid(idToAmountPaid.get(billDetail.getId())); + billDetail.getBillAccountDetails().forEach(billAccountDetail -> { + billAccountDetail.setAdjustedAmount(idToBucket.get(billAccountDetail.getId()).getAdjustedAmount()); + if(billAccountDetail.getTaxHeadCode().contains("ADVANCE")){ + billAccountDetail.setAmount(idToBucket.get(billAccountDetail.getId()).getAmount()); + } + + }); + }); + } + + + private void addAdvanceIfExistForDemand(List demands,List taxDetails){ + + Bucket advanceBucket = null; + TaxDetail taxDetail = taxDetails.get(taxDetails.size()-1); + + for(Bucket bucket : taxDetail.getBuckets()){ + if(bucket.getEntityId()==null && bucket.getPurpose().equals(Purpose.ADVANCE_AMOUNT)){ + advanceBucket = bucket; + break; + } + } + + if(advanceBucket != null){ + DemandDetail demandDetailForAdvance = new DemandDetail(); + demandDetailForAdvance.setTaxAmount(advanceBucket.getAmount()); + demandDetailForAdvance.setTaxHeadMasterCode(advanceBucket.getTaxHeadCode()); + demands.get(demands.size()-1).getDemandDetails().add(demandDetailForAdvance); + } + + } + + + private void addAdvanceIfExistForBill(List billDetails,List taxDetails){ + + Bucket advanceBucket = null; + TaxDetail taxDetail = taxDetails.get(taxDetails.size()-1); + + for(Bucket bucket : taxDetail.getBuckets()){ + if(bucket.getEntityId()==null && bucket.getPurpose().equals(Purpose.ADVANCE_AMOUNT)){ + advanceBucket = bucket; + break; + } + } + + if(advanceBucket != null){ + BillAccountDetail billAccountDetailForAdvance = new BillAccountDetail(); + billAccountDetailForAdvance.setAmount(advanceBucket.getAmount()); + billAccountDetailForAdvance.setPurpose(Purpose.ADVANCE_AMOUNT); + billAccountDetailForAdvance.setTaxHeadCode(advanceBucket.getTaxHeadCode()); + billDetails.get(billDetails.size()-1).getBillAccountDetails().add(billAccountDetailForAdvance); + } + + } + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionV2.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionV2.java new file mode 100644 index 000000000..6d5e37b0a --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/ApportionV2.java @@ -0,0 +1,22 @@ +package org.egov.service; + +import org.egov.web.models.ApportionRequestV2; +import org.egov.web.models.Bill; +import org.egov.web.models.BillDetail; +import org.egov.web.models.TaxDetail; + +import java.util.List; + +public interface ApportionV2 { + + /** + * Should return the code of the BusinessService for which the interface is implemented + * @return Code of the BusinessService + */ + String getBusinessService(); + + + + List apportionPaidAmount(ApportionRequestV2 apportionRequestV2, Object masterData, Boolean isBillApportion); + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/MDMSService.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/MDMSService.java new file mode 100644 index 000000000..f23081164 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/MDMSService.java @@ -0,0 +1,139 @@ +package org.egov.service; + +import org.egov.common.contract.request.RequestInfo; +import org.egov.config.ApportionConfig; +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.ApportionRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import static org.egov.util.ApportionConstants.*; + +@Service +public class MDMSService { + + + private ServiceRequestRepository serviceRequestRepository; + + private ApportionConfig config; + + @Autowired + public MDMSService(ServiceRequestRepository serviceRequestRepository,ApportionConfig config) { + this.serviceRequestRepository = serviceRequestRepository; + this.config = config; + } + + /** + * Fetches MDMS data based on the apportion request + * @param requestInfo The apportion request for which master data is required + * @return MasterData from MDMS + */ + public Object mDMSCall(RequestInfo requestInfo,String tenantId){ + MdmsCriteriaReq mdmsCriteriaReq = getMDMSRequest(requestInfo,tenantId); + Object result = serviceRequestRepository.fetchResult(getMdmsSearchUrl(), mdmsCriteriaReq); + return result; + } + + /** + * Creates MDMS search criteria object MdmsCriteriaReq + * @param requestInfo The requestInfo of the apportion request + * @param tenantId TenantId of the request + * @return MDMS search criteria + */ + private MdmsCriteriaReq getMDMSRequest(RequestInfo requestInfo, String tenantId){ + ModuleDetail moduleDetail = getBillingModuleDetail(); + + List moduleDetails = new LinkedList<>(); + moduleDetails.add(moduleDetail); + + MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId) + .build(); + + MdmsCriteriaReq mdmsCriteriaReq = MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria) + .requestInfo(requestInfo).build(); + return mdmsCriteriaReq; + } + + + + /** + * 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()); + } + + + /** + * Creates request to search taxhead in mdms + * @return MDMS request for taxhead + */ + private ModuleDetail getTaxHeadMasterRequest() { + // filter to only get code field from master data + + // final String filterCodeForUom = "$.[?(@.active==true)]"; + + ModuleDetail tlModuleDtls = ModuleDetail.builder() + .masterDetails(Collections.singletonList(MasterDetail.builder().name(MDMS_TAXHEAD).build())) + .moduleName(MDMS_BILLING_SERVICE).build(); + + return tlModuleDtls; + } + + + /** + * Creates request to search businessService in mdms + * @return MDMS request for businessService + */ + private ModuleDetail getBusinessServiceRequest() { + // filter to only get code field from master data + + // final String filterCodeForUom = "$.[?(@.active==true)]"; + + ModuleDetail tlModuleDtls = ModuleDetail.builder() + .masterDetails(Collections.singletonList(MasterDetail.builder().name(MDMS_BUSINESSSERVICE).build())) + .moduleName(MDMS_BILLING_SERVICE).build(); + + return tlModuleDtls; + } + + + /** + * Creates request to search businessService in mdms + * @return MDMS request for businessService + */ + private ModuleDetail getBillingModuleDetail() { + + List masterDetails = new ArrayList<>(); + + MasterDetail businessServiceMasterDetail = MasterDetail.builder().name(MDMS_BUSINESSSERVICE).build(); + MasterDetail taxHeadMasterDetail = MasterDetail.builder().name(MDMS_TAXHEAD).build(); + + masterDetails.add(businessServiceMasterDetail); + masterDetails.add(taxHeadMasterDetail); + + ModuleDetail tlModuleDtls = ModuleDetail.builder() + .masterDetails(masterDetails) + .moduleName(MDMS_BILLING_SERVICE).build(); + + return tlModuleDtls; + } + + + + + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/TaxHeadMasterService.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/TaxHeadMasterService.java new file mode 100644 index 000000000..0af052bd3 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/TaxHeadMasterService.java @@ -0,0 +1,87 @@ +package org.egov.service; + +import com.jayway.jsonpath.JsonPath; +import org.egov.tracer.model.CustomException; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.util.ApportionConstants.*; + +@Service +public class TaxHeadMasterService { + + + /** + * Fetches the advance amount taxHead for the given businessService from MDMSData + * @param businessService The businessService for which taxhead is to be fetched + * @param mdmsData The master data received from MDMS Service + * @return The code of the TaxHead + */ + public String getAdvanceTaxHead(String businessService,Object mdmsData){ + + String jsonpath = ADVANCE_TAXHEAD_JSONPATH_CODE; + jsonpath = jsonpath.replace("{}",businessService); + + + List taxHeads = JsonPath.read(mdmsData,jsonpath); + + if(CollectionUtils.isEmpty(taxHeads)) + throw new CustomException("NO TAXHEAD FOUND","No Advance taxHead found for businessService: "+businessService); + + return taxHeads.get(0); + } + + + /** + * Creates a map of taxHeadCode to priority for taxHeads of given businessService + * @param businessService + * @param mdmsData + * @return + */ + public Map getCodeToOrderMap(String businessService,Object mdmsData){ + + String jsonpath = TAXHEAD_JSONPATH_CODE; + jsonpath = jsonpath.replace("{}",businessService); + + List> taxHeads = JsonPath.read(mdmsData,jsonpath); + + if(CollectionUtils.isEmpty(taxHeads)) + throw new CustomException("NO TAXHEAD FOUND","No taxHeads found for businessService: "+businessService); + + Map codeToOrderMap = new HashMap<>(); + + taxHeads.forEach(taxHead -> { + String taxHeadCode = taxHead.get(MDMS_TAXHEADCODE_KEY); + Integer order = Integer.valueOf(taxHead.get(MDMS_ORDER_KEY)); + codeToOrderMap.put(taxHeadCode,order); + }); + + return codeToOrderMap; + } + + + /** + * Fetches the isAdvanceAllowed flag from the MDMS data for the given businessService + * @param businessService BusinessService for which advance flag has to be returned + * @param mdmsData MDMS data for Billing + * @return boolean flag indicating whether advance payment is allowed for the given businessService + */ + public Boolean isAdvanceAllowed(String businessService, Object mdmsData){ + + String jsonpath = ADVANCE_BUSINESSSERVICE_JSONPATH_CODE; + jsonpath = jsonpath.replace("{}",businessService); + + List isAdvanceAllowedList = JsonPath.read(mdmsData,jsonpath); + + if(CollectionUtils.isEmpty(isAdvanceAllowedList)) + throw new CustomException("NO BUSINESSSERVICE FOUND","No businessService or isAdvanceAllowed flag found for code: "+businessService); + + return isAdvanceAllowedList.get(0); + } + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/TranslationService.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/TranslationService.java new file mode 100644 index 000000000..7f683da72 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/TranslationService.java @@ -0,0 +1,126 @@ +package org.egov.service; + +import org.egov.tracer.model.CustomException; +import org.egov.web.models.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class TranslationService { + + + private TaxHeadMasterService taxHeadMasterService; + + + @Autowired + public TranslationService(TaxHeadMasterService taxHeadMasterService) { + this.taxHeadMasterService = taxHeadMasterService; + } + + + public ApportionRequestV2 translate(Bill bill){ + + String businessService = bill.getBusinessService(); + BigDecimal amountPaid = bill.getAmountPaid(); + Boolean isAdvanceAllowed = bill.getIsAdvanceAllowed(); + + ApportionRequestV2 apportionRequestV2 = ApportionRequestV2.builder().amountPaid(amountPaid).businessService(businessService) + .isAdvanceAllowed(isAdvanceAllowed).build(); + + List billDetails = bill.getBillDetails(); + + for(BillDetail billDetail : billDetails){ + + TaxDetail taxDetail = TaxDetail.builder().fromPeriod(billDetail.getFromPeriod()).amountToBePaid(billDetail.getAmount()) + .amountPaid((billDetail.getAmountPaid() == null) ? BigDecimal.ZERO : billDetail.getAmountPaid()) + .entityId(billDetail.getId()) + .build(); + + billDetail.getBillAccountDetails().forEach(billAccountDetail -> { + Bucket bucket = Bucket.builder().amount(billAccountDetail.getAmount()) + .adjustedAmount((billAccountDetail.getAdjustedAmount()==null) ? BigDecimal.ZERO : billAccountDetail.getAdjustedAmount()) + .taxHeadCode(billAccountDetail.getTaxHeadCode()) + .priority(billAccountDetail.getOrder()) + .entityId(billAccountDetail.getId()) + .build(); + taxDetail.addBucket(bucket); + }); + + apportionRequestV2.addTaxDetail(taxDetail); + } + + return apportionRequestV2; + + } + + + + public ApportionRequestV2 translate(List demands,Object mdmsData) { + + // Group by businessService before calling this function + String businessService = demands.get(0).getBusinessService(); + + + Map codeToOrderMap = taxHeadMasterService.getCodeToOrderMap(businessService,mdmsData); + + // FIX ME + BigDecimal amountPaid = BigDecimal.ZERO; + Boolean isAdvanceAllowed = taxHeadMasterService.isAdvanceAllowed(businessService,mdmsData); + + + ApportionRequestV2 apportionRequestV2 = ApportionRequestV2.builder().amountPaid(amountPaid).businessService(businessService) + .isAdvanceAllowed(isAdvanceAllowed).build(); + + Map errorMap = new HashMap<>(); + + for(Demand demand : demands){ + + TaxDetail taxDetail = TaxDetail.builder().fromPeriod(demand.getTaxPeriodFrom()).entityId(demand.getId()).build(); + + BigDecimal amountToBePaid = BigDecimal.ZERO; + BigDecimal collectedAmount = BigDecimal.ZERO; + + for(DemandDetail demandDetail : demand.getDemandDetails()){ + + Integer priority = codeToOrderMap.get(demandDetail.getTaxHeadMasterCode()); + + if(priority == null) + errorMap.put("INVALID_TAXHEAD_CODE","Order is null or taxHead is not found for code: "+demandDetail.getTaxHeadMasterCode()); + + Bucket bucket = Bucket.builder().amount(demandDetail.getTaxAmount()) + .adjustedAmount((demandDetail.getCollectionAmount()==null) ? BigDecimal.ZERO : demandDetail.getCollectionAmount()) + .taxHeadCode(demandDetail.getTaxHeadMasterCode()) + .priority(priority) + .entityId(demandDetail.getId()) + .build(); + taxDetail.addBucket(bucket); + + + amountToBePaid = amountToBePaid.add(demandDetail.getTaxAmount()); + collectedAmount = collectedAmount.add(demandDetail.getCollectionAmount()); + } + + taxDetail.setAmountPaid(collectedAmount); + taxDetail.setAmountToBePaid(amountToBePaid); + + apportionRequestV2.addTaxDetail(taxDetail); + + } + + if(!CollectionUtils.isEmpty(errorMap)) + throw new CustomException(errorMap); + + return apportionRequestV2; + } + + + + + + } diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/apportions/OrderByPriority.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/apportions/OrderByPriority.java new file mode 100644 index 000000000..a3b24f74b --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/apportions/OrderByPriority.java @@ -0,0 +1,356 @@ +package org.egov.service.apportions; + +import org.apache.kafka.common.metrics.stats.Percentiles.BucketSizing; +import org.egov.config.ApportionConfig; +import org.egov.service.ApportionV2; +import org.egov.service.TaxHeadMasterService; +import org.egov.tracer.model.CustomException; +import org.egov.web.models.ApportionRequestV2; +import org.egov.web.models.Bucket; +import org.egov.web.models.TaxDetail; +import org.egov.web.models.enums.Purpose; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collector; + +import static org.egov.util.ApportionConstants.DEFAULT; + +@Service +public class OrderByPriority implements ApportionV2 { + + private TaxHeadMasterService taxHeadMasterService; + + private ApportionConfig config; + + @Autowired + public OrderByPriority(TaxHeadMasterService taxHeadMasterService, ApportionConfig config) { + this.taxHeadMasterService = taxHeadMasterService; + this.config = config; + } + + @Override + public String getBusinessService() { + return DEFAULT; + } + + @Override + public List apportionPaidAmount(ApportionRequestV2 apportionRequestV2, Object masterData, + Boolean isBillApportion) { + List taxDetails = apportionRequestV2.getTaxDetails(); + taxDetails.sort(Comparator.comparing(TaxDetail::getFromPeriod)); + BigDecimal remainingAmount = apportionRequestV2.getAmountPaid(); + BigDecimal amount; + Boolean isAmountPositive; + + /* + * If zero amount payment is done and the total amount of bill or demands is + * zero. We will set the collection amount to the taxamount for all taxHeads + */ + if (apportionRequestV2.getAmountPaid().compareTo(BigDecimal.ZERO) == 0 + && getTotalAmount(taxDetails, isBillApportion).compareTo(BigDecimal.ZERO) == 0) { + apportionZeroPaymentAndZeroAmountToBePaid(taxDetails); + return taxDetails; + } + + if (apportionRequestV2.getIsAdvanceAllowed()) { + BigDecimal requiredAdvanceAmount = apportionAndGetRequiredAdvance(apportionRequestV2); + remainingAmount = remainingAmount.add(requiredAdvanceAmount); + } + + if (!config.getApportionByValueAndOrder()) + validateOrder(taxDetails); + + BigDecimal amountBeforeApportion = remainingAmount; + + for (TaxDetail taxDetail : taxDetails) { + + if (remainingAmount.compareTo(BigDecimal.ZERO) == 0) { + taxDetail.setAmountPaid(BigDecimal.ZERO); + continue; + } + + if (!config.getApportionByValueAndOrder()) + taxDetail.getBuckets().sort(Comparator.comparing(Bucket::getAmount)); + else + taxDetail.getBuckets().sort(Comparator.comparing(Bucket::getAmount).thenComparing(Bucket::getPriority)); + + for (Bucket bucket : taxDetail.getBuckets()) { + + amount = bucket.getAmount().subtract(bucket.getAdjustedAmount()); + isAmountPositive = amount.compareTo(BigDecimal.ZERO) >= 0; + + if (isAmountPositive) { + + if (remainingAmount.equals(BigDecimal.ZERO)) { + bucket.setAdjustedAmount(bucket.getAdjustedAmount().add(BigDecimal.ZERO)); + continue; + } + + if (remainingAmount.compareTo(amount) <= 0) { + bucket.setAdjustedAmount(bucket.getAdjustedAmount().add(remainingAmount)); + remainingAmount = BigDecimal.ZERO; + } + + if (remainingAmount.compareTo(amount) > 0) { + bucket.setAdjustedAmount(bucket.getAdjustedAmount().add(amount)); + remainingAmount = remainingAmount.subtract(amount); + } + } else { + // FIX ME + // advance should be checked from purpose + if (!bucket.getTaxHeadCode().contains("ADVANCE")) { + bucket.setAdjustedAmount(amount); + remainingAmount = remainingAmount.subtract(amount); + } + } + } + + if (taxDetail.getAmountPaid() == null) + taxDetail.setAmountPaid(BigDecimal.ZERO); + + taxDetail.setAmountPaid(taxDetail.getAmountPaid().add(amountBeforeApportion.subtract(remainingAmount))); + amountBeforeApportion = remainingAmount; + } + + // If advance amount is available + if (remainingAmount.compareTo(BigDecimal.ZERO) > 0) { + addAdvanceBillAccountDetail(remainingAmount, apportionRequestV2, masterData); + } + + return taxDetails; + } + + /** + * Creates a advance BillAccountDetail and adds it to the latest billDetail + * + * @param advanceAmount The advance amount paid + * @param apportionRequestV2 The bill for which apportioning is done + * @param masterData The required masterData for the TaxHeads + */ + private void addAdvanceBillAccountDetail(BigDecimal advanceAmount, ApportionRequestV2 apportionRequestV2, + Object masterData) { + List taxDetails = apportionRequestV2.getTaxDetails(); + String taxHead = taxHeadMasterService.getAdvanceTaxHead(apportionRequestV2.getBusinessService(), masterData); + + TaxDetail latestTaxDetail = taxDetails.get(taxDetails.size() - 1); + Bucket bucketForAdvance = null; + + // Search if advance bucket already exist + for (Bucket bucket : latestTaxDetail.getBuckets()) { + if (bucket.getTaxHeadCode().contains("ADVANCE")) { + bucketForAdvance = bucket; + break; + } + } + + // If advance bucket is not present add new one else update existing one + if (bucketForAdvance == null) { + // Creating the advance bucket + bucketForAdvance = new Bucket(); + bucketForAdvance.setAmount(advanceAmount.negate()); + bucketForAdvance.setPurpose(Purpose.ADVANCE_AMOUNT); + bucketForAdvance.setTaxHeadCode(taxHead); + + // Setting the advance bucket in the latest taxDetail + taxDetails.get(taxDetails.size() - 1).getBuckets().add(bucketForAdvance); + } + + else { + bucketForAdvance.setAmount(bucketForAdvance.getAmount().add(advanceAmount.negate())); + } + + // Updating the amountPaid in the taxDetail + BigDecimal amountPaid = taxDetails.get(taxDetails.size() - 1).getAmountPaid(); + taxDetails.get(taxDetails.size() - 1).setAmountPaid(amountPaid.add(advanceAmount)); + } + + private void validateOrder(List taxDetails) { + Map errorMap = new HashMap<>(); + taxDetails.forEach(taxDetail -> { + + if (taxDetail.getFromPeriod() == null) + errorMap.put("INVALID PERIOD", "The fromPeriod cannot be null"); + + List buckets = taxDetail.getBuckets(); + + int maxOrderOfNegativeTaxHead = Integer.MIN_VALUE; + int minOrderOfPositiveTaxHead = Integer.MAX_VALUE; + + for (int i = 0; i < buckets.size(); i++) { + if (buckets.get(i).getPriority() == null) { + errorMap.put("INVALID ORDER", "Order is null for: " + buckets.get(i)); + continue; + } + + if (buckets.get(i).getAmount().compareTo(BigDecimal.ZERO) > 0 + && minOrderOfPositiveTaxHead > buckets.get(i).getPriority()) { + minOrderOfPositiveTaxHead = buckets.get(i).getPriority(); + } else if (buckets.get(i).getAmount().compareTo(BigDecimal.ZERO) < 0 + && maxOrderOfNegativeTaxHead < buckets.get(i).getPriority()) { + maxOrderOfNegativeTaxHead = buckets.get(i).getPriority(); + } + } + if (minOrderOfPositiveTaxHead < maxOrderOfNegativeTaxHead) + throw new CustomException("INVALID ORDER", "Positive TaxHeads should be after Negative TaxHeads"); + }); + if (!errorMap.isEmpty()) + throw new CustomException(errorMap); + } + + /** + * Apportions the advance taxhead and returns the advance amount. + * + * @param apportionRequestV2 + * @return + */ + private BigDecimal apportionAndGetRequiredAdvance(ApportionRequestV2 apportionRequestV2) { + + List taxDetails = apportionRequestV2.getTaxDetails(); + + BigDecimal totalPositiveAmount = BigDecimal.ZERO; + + for (TaxDetail taxDetail : taxDetails) { + + if (taxDetail.getAmountToBePaid().compareTo(BigDecimal.ZERO) > 0) + totalPositiveAmount = totalPositiveAmount.add(taxDetail.getAmountToBePaid()); + + } + + /** + * If net amount to be paid is zero for all billDetails no advance payment from + * previous billing cycles is required for apportion + * + */ + + if (totalPositiveAmount.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + + /* + * net = Bill Account Detail amount - Bill Account Detail adj amount In case + * when advance + net > total Positive: 200 + (100 - 20) > 230 Bill Account + * Detail amount 100 Bill Account Detail adj amount 20 current advance 200 Total + * positive 230 final adjusted amount = 20 + (230 - 200) = 50 + */ + + BigDecimal advance = BigDecimal.ZERO; + BigDecimal remainingAmount = totalPositiveAmount; + for (TaxDetail taxDetail : taxDetails) { + + if (taxDetail.getAmountPaid() == null) + taxDetail.setAmountPaid(BigDecimal.ZERO); + + BigDecimal totalAdvance = new BigDecimal(taxDetail.getBuckets().stream() + .filter(i -> i.getTaxHeadCode().equalsIgnoreCase("WS_ADVANCE_CARRYFORWARD")) + .mapToInt(i -> i.getAmount().intValue()).sum()) + .subtract(new BigDecimal(taxDetail.getBuckets().stream() + .filter(i -> i.getTaxHeadCode().equalsIgnoreCase("WS_ADVANCE_CARRYFORWARD")) + .mapToInt(i -> i.getAdjustedAmount().intValue()).sum())); + Long count = taxDetail.getBuckets().stream() + .filter(i -> i.getTaxHeadCode().equalsIgnoreCase("WS_ADVANCE_CARRYFORWARD")).count(); + + if (count > 0) { + if (totalAdvance.abs().compareTo(totalPositiveAmount) > 0) { + if(count==1) { + advance = totalPositiveAmount; + } + else { + advance = totalPositiveAmount.negate(); + } + } else { + advance = totalAdvance; + } + } + for (Bucket bucket : taxDetail.getBuckets()) { + + // FIX ME + // advance should be checked from purpose + if (bucket.getTaxHeadCode().contains("ADVANCE")) { + + BigDecimal net = bucket.getAmount().subtract(bucket.getAdjustedAmount()); + + if (totalAdvance.abs().compareTo(totalPositiveAmount) > 0) { + Boolean allNegative = taxDetail.getBuckets().stream() + .allMatch(i -> i.getAmount().compareTo(BigDecimal.ZERO) < 0); + // Advance heads whose amount is partially getting used + if (allNegative) { + if (net.negate().compareTo(totalPositiveAmount) > 0 && remainingAmount.compareTo(BigDecimal.ZERO)>0) { + bucket.setAdjustedAmount(bucket.getAdjustedAmount().add(totalPositiveAmount.negate())); + taxDetail.setAmountPaid(taxDetail.getAmountPaid().add(totalPositiveAmount.negate())); + remainingAmount = BigDecimal.ZERO; + break; + } else { + bucket.setAdjustedAmount(bucket.getAdjustedAmount().add(remainingAmount.compareTo(net.abs())<0?remainingAmount.negate():net)); + taxDetail.setAmountPaid(taxDetail.getAmountPaid().add(remainingAmount.compareTo(net.abs())<0?remainingAmount.negate():net)); + remainingAmount = remainingAmount.add(net); + } + + } else { + if (net.negate().compareTo(totalPositiveAmount) < 0) { + bucket.setAdjustedAmount(bucket.getAdjustedAmount().add(BigDecimal.ZERO)); + taxDetail.setAmountPaid(taxDetail.getAmountPaid().add(BigDecimal.ZERO)); + } else { + BigDecimal diff = totalAdvance.add(totalPositiveAmount); + BigDecimal adjustedAmount = bucket.getAdjustedAmount(); + bucket.setAdjustedAmount(adjustedAmount.add(bucket.getAmount().subtract(diff))); + taxDetail.setAmountPaid( + taxDetail.getAmountPaid().add(bucket.getAmount().subtract(diff))); + advance = bucket.getAmount().subtract(diff); + } + } + + } else { + // Advance heads whose amount is completely getting used + bucket.setAdjustedAmount(bucket.getAmount()); + taxDetail.setAmountPaid(taxDetail.getAmountPaid().add(net)); + + } + + } + + } + } + return advance.negate(); + } + + private BigDecimal getTotalAmount(List taxDetails, Boolean isBillApportion) { + + BigDecimal totalAmount = BigDecimal.ZERO; + + for (TaxDetail taxDetail : taxDetails) { + if (isBillApportion) { + totalAmount = totalAmount.add(taxDetail.getAmountToBePaid()); + } else { + totalAmount = totalAmount.add(taxDetail.getAmountToBePaid()).subtract(taxDetail.getAmountPaid()); + } + + } + return totalAmount; + } + + /** + * In case of zero payment and total amount to be paid equal to zero we set + * adjusted amount equal to tax amount for all buckets + * + * @param taxDetails + */ + private void apportionZeroPaymentAndZeroAmountToBePaid(List taxDetails) { + + for (TaxDetail taxDetail : taxDetails) { + + List buckets = taxDetail.getBuckets(); + + buckets.forEach(bucket -> { + bucket.setAdjustedAmount(bucket.getAmount()); + }); + + } + + } + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/service/apportions/OrderByPriorityApportion.java b/business-services/egov-apportion-service/src/main/java/org/egov/service/apportions/OrderByPriorityApportion.java new file mode 100644 index 000000000..e4d50a878 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/service/apportions/OrderByPriorityApportion.java @@ -0,0 +1,268 @@ +package org.egov.service.apportions; + + +import org.egov.config.ApportionConfig; +import org.egov.service.Apportion; +import org.egov.service.TaxHeadMasterService; +import org.egov.tracer.model.CustomException; +import org.egov.web.models.Bill; +import org.egov.web.models.enums.Purpose; +import org.egov.web.models.BillAccountDetail; +import org.egov.web.models.BillDetail; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.egov.util.ApportionConstants.*; + +@Service +public class OrderByPriorityApportion implements Apportion { + + + private TaxHeadMasterService taxHeadMasterService; + + private ApportionConfig config; + + + @Autowired + public OrderByPriorityApportion(TaxHeadMasterService taxHeadMasterService, ApportionConfig config) { + this.taxHeadMasterService = taxHeadMasterService; + this.config = config; + } + + + + @Override + public String getBusinessService() { + return DEFAULT; + } + + + /** + * 1. Sort the billDetails based on fromPeriod + * 2. For each billDetail sort the BillAccountDetails by order + * 3. Start apportioning by assigning positive adjustedAmount for negative amounts + * and negative adjustmentAmount for postive amounts + * 4. If any advance amount is remaining new BillAccountDetail is created for it + * and assigned to last BillDetail + * @return + */ + @Override + public List apportionPaidAmount(Bill bill, Object masterData) { + bill.getBillDetails().sort(Comparator.comparing(BillDetail::getFromPeriod)); + List billDetails = bill.getBillDetails(); + BigDecimal remainingAmount = bill.getAmountPaid(); + BigDecimal amount; + Boolean isAmountPositive; + + if(bill.getIsAdvanceAllowed()){ + BigDecimal requiredAdvanceAmount = apportionAndGetRequiredAdvance(bill); + remainingAmount = remainingAmount.add(requiredAdvanceAmount); + } + + if(!config.getApportionByValueAndOrder()) + validateOrder(billDetails); + + BigDecimal amountBeforeApportion = remainingAmount; + + for (BillDetail billDetail : billDetails){ + + if(remainingAmount.compareTo(BigDecimal.ZERO)==0){ + billDetail.setAmountPaid(BigDecimal.ZERO); + continue; + } + + if(!config.getApportionByValueAndOrder()) + billDetail.getBillAccountDetails().sort(Comparator.comparing(BillAccountDetail::getAmount)); + else + billDetail.getBillAccountDetails().sort(Comparator.comparing(BillAccountDetail::getAmount).thenComparing(BillAccountDetail::getOrder)); + + + for(BillAccountDetail billAccountDetail : billDetail.getBillAccountDetails()) { + + amount = billAccountDetail.getAmount(); + isAmountPositive = amount.compareTo(BigDecimal.ZERO) >= 0; + + if (isAmountPositive) { + + if (remainingAmount.equals(BigDecimal.ZERO)) { + billAccountDetail.setAdjustedAmount(BigDecimal.ZERO); + continue; + } + + if (remainingAmount.compareTo(amount) <= 0) { + billAccountDetail.setAdjustedAmount(remainingAmount); + remainingAmount = BigDecimal.ZERO; + } + + if (remainingAmount.compareTo(amount) > 0) { + billAccountDetail.setAdjustedAmount(amount); + remainingAmount = remainingAmount.subtract(amount); + } + } + else { + // FIX ME + // advance should be checked from purpose + if(!billAccountDetail.getTaxHeadCode().contains("ADVANCE")) { + billAccountDetail.setAdjustedAmount(amount); + remainingAmount = remainingAmount.subtract(amount); + } + } + } + + if(billDetail.getAmountPaid()==null) + billDetail.setAmountPaid(BigDecimal.ZERO); + + billDetail.setAmountPaid(billDetail.getAmountPaid().add(amountBeforeApportion.subtract(remainingAmount))); + amountBeforeApportion = remainingAmount; + } + + + //If advance amount is available + if(remainingAmount.compareTo(BigDecimal.ZERO)>0){ + addAdvanceBillAccountDetail(remainingAmount,bill,masterData); + } + + + return billDetails; + } + + + /** + * Validates if order is not null in each BillAccountDetail in the given list of billDetails + * and all positive taxHeadOrder come after negative taxHeadOrder + * @param billDetails List of billDetails to be validated + */ + private void validateOrder(List billDetails){ + Map errorMap = new HashMap<>(); + billDetails.forEach(billDetail -> { + + if(billDetail.getFromPeriod()==null || billDetail.getToPeriod()==null) + errorMap.put("INVALID PERIOD","The fromPeriod and toPeriod in BillDetail cannot be null"); + + List billAccountDetails = billDetail.getBillAccountDetails(); + + int maxOrderOfNegativeTaxHead = Integer.MIN_VALUE; + int minOrderOfPositiveTaxHead = Integer.MAX_VALUE; + + for(int i=0;i0 && + minOrderOfPositiveTaxHead > billAccountDetails.get(i).getOrder()){ + minOrderOfPositiveTaxHead = billAccountDetails.get(i).getOrder(); + } + else if(billAccountDetails.get(i).getAmount().compareTo(BigDecimal.ZERO)<0 && + maxOrderOfNegativeTaxHead < billAccountDetails.get(i).getOrder()){ + maxOrderOfNegativeTaxHead = billAccountDetails.get(i).getOrder(); + } + } + if(minOrderOfPositiveTaxHead < maxOrderOfNegativeTaxHead) + throw new CustomException("INVALID ORDER","Positive TaxHeads should be after Negative TaxHeads"); + }); + if(!errorMap.isEmpty()) + throw new CustomException(errorMap); + } + + + /** + * Creates a advance BillAccountDetail and adds it to the latest billDetail + * @param advanceAmount The advance amount paid + * @param bill The bill for which apportioning is done + * @param masterData The required masterData for the TaxHeads + */ + private void addAdvanceBillAccountDetail(BigDecimal advanceAmount,Bill bill,Object masterData){ + List billDetails = bill.getBillDetails(); + String taxHead = taxHeadMasterService.getAdvanceTaxHead(bill.getBusinessService(),masterData); + BillAccountDetail billAccountDetailForAdvance = new BillAccountDetail(); + billAccountDetailForAdvance.setAmount(advanceAmount.negate()); + billAccountDetailForAdvance.setPurpose(Purpose.ADVANCE_AMOUNT); + billAccountDetailForAdvance.setTaxHeadCode(taxHead); + billDetails.get(billDetails.size()-1).getBillAccountDetails().add(billAccountDetailForAdvance); + } + + + /** + * Apportions the advance taxhead and returns the advance amount. + * @param bill + * @return + */ + private BigDecimal apportionAndGetRequiredAdvance(Bill bill){ + + List billDetails = bill.getBillDetails(); + + BigDecimal totalPositiveAmount = BigDecimal.ZERO; + + for (BillDetail billDetail : billDetails) { + + if(billDetail.getAmount().compareTo(BigDecimal.ZERO) > 0 ) + totalPositiveAmount = totalPositiveAmount.add(billDetail.getAmount()); + + } + + /** + * If net amount to be paid is zero for all billDetails no advance payment from + * previous billing cycles is required for apportion + * + */ + + if(totalPositiveAmount.compareTo(BigDecimal.ZERO) == 0) + return BigDecimal.ZERO; + + + /* net = Bill Account Detail amount - Bill Account Detail adj amount + * In case when advance + net > total Positive: 200 + (100 - 20) > 230 + * Bill Account Detail amount 100 + Bill Account Detail adj amount 20 + current advance 200 + Total positive 230 + final adjusted amount = 20 + (230 - 200) = 50 + * */ + + BigDecimal advance = BigDecimal.ZERO; + for (BillDetail billDetail : billDetails) { + + if(billDetail.getAmountPaid()==null) + billDetail.setAmountPaid(BigDecimal.ZERO); + + for(BillAccountDetail billAccountDetail : billDetail.getBillAccountDetails()) { + + // FIX ME + // advance should be checked from purpose + if(billAccountDetail.getTaxHeadCode().contains("ADVANCE")){ + + BigDecimal net = billAccountDetail.getAmount().subtract(billAccountDetail.getAdjustedAmount()); + if(advance.add(net).abs().compareTo(totalPositiveAmount) > 0){ + BigDecimal diff = totalPositiveAmount.subtract(advance); + BigDecimal adjustedAmount = billAccountDetail.getAdjustedAmount(); + billAccountDetail.setAdjustedAmount(adjustedAmount.add(diff).negate()); + advance = totalPositiveAmount.negate(); + billDetail.setAmountPaid(billDetail.getAmountPaid().add(diff.negate())); + break; + } + else { + advance = advance.add(net); + billAccountDetail.setAdjustedAmount(billAccountDetail.getAmount()); + billDetail.setAmountPaid(billDetail.getAmountPaid().add(net)); + } + } + + } + + } + return advance.negate(); + } + + + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/util/ApportionConstants.java b/business-services/egov-apportion-service/src/main/java/org/egov/util/ApportionConstants.java new file mode 100644 index 000000000..376d33ec0 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/util/ApportionConstants.java @@ -0,0 +1,29 @@ +package org.egov.util; + +import org.springframework.stereotype.Component; + +@Component +public class ApportionConstants { + + public static final String DEFAULT = "DEFAULT"; + + public static final String MDMS_BILLING_SERVICE = "BillingService"; + + public static final String MDMS_TAXHEAD = "TaxHeadMaster"; + + public static final String MDMS_BUSINESSSERVICE = "BusinessService"; + + public static final String ADVANCE_TAXHEAD_JSONPATH_CODE = "$.MdmsRes.BillingService.TaxHeadMaster[?(@.category=='ADVANCE_COLLECTION' && @.service==\"{}\")].code"; + + public static final String ADVANCE_BUSINESSSERVICE_JSONPATH_CODE = "$.MdmsRes.BillingService.BusinessService[?(@.code==\"{}\")].isAdvanceAllowed"; + + public static final String TAXHEAD_JSONPATH_CODE = "$.MdmsRes.BillingService.TaxHeadMaster[?(@.service==\"{}\")]"; + + public static final String MDMS_ORDER_KEY = "order"; + + public static final String MDMS_TAXHEADCODE_KEY = "code"; + + + public ApportionConstants() { + } +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/util/ResponseInfoFactory.java b/business-services/egov-apportion-service/src/main/java/org/egov/util/ResponseInfoFactory.java new file mode 100644 index 000000000..43735b827 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/util/ResponseInfoFactory.java @@ -0,0 +1,25 @@ +package org.egov.util; + +import org.egov.common.contract.request.RequestInfo; +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.stereotype.Component; + +@Component +public class ResponseInfoFactory { + + public ResponseInfo createResponseInfoFromRequestInfo(final RequestInfo requestInfo, final Boolean success) { + + final String apiId = requestInfo != null ? requestInfo.getApiId() : ""; + final String ver = requestInfo != null ? requestInfo.getVer() : ""; + Long ts = null; + if(requestInfo!=null) + ts = requestInfo.getTs(); + final String resMsgId = "uief87324"; // FIXME : Hard-coded + final String msgId = requestInfo != null ? requestInfo.getMsgId() : ""; + final String responseStatus = success ? "successful" : "failed"; + + return ResponseInfo.builder().apiId(apiId).ver(ver).ts(ts).resMsgId(resMsgId).msgId(msgId).resMsgId(resMsgId) + .status(responseStatus).build(); + } + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/controllers/ApportionController.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/controllers/ApportionController.java new file mode 100644 index 000000000..4126b1aee --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/controllers/ApportionController.java @@ -0,0 +1,67 @@ +package org.egov.web.controllers; + + +import org.egov.service.ApportionService; +import org.egov.service.ApportionServiceV2; +import org.egov.util.ResponseInfoFactory; +import org.egov.web.models.ApportionRequest; +import org.egov.web.models.ApportionResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.annotations.*; +import org.egov.web.models.AuditDetails; +import org.egov.web.models.Bill; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.*; + +import javax.validation.Valid; + +@javax.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2019-02-25T15:07:36.183+05:30") + +@Controller +@RequestMapping("/v1") +public class ApportionController { + + private final ObjectMapper objectMapper; + + @Autowired + private ApportionService apportionService; + + @Autowired + private ResponseInfoFactory responseInfoFactory; + + @Autowired + public ApportionController(ObjectMapper objectMapper, ApportionService apportionService) { + this.objectMapper = objectMapper; + this.apportionService = apportionService; + } + + + /** + * Executes the apportioning process on the given bills + * @param apportionRequest The ApportionRequest containing the bill to be apportioned + * @return Apportioned Bills + */ + @RequestMapping(value="/_apportion", method = RequestMethod.POST) + public ResponseEntity apportionPost(@Valid @RequestBody ApportionRequest apportionRequest){ + List billInfos = apportionService.apportionBills(apportionRequest); + ApportionResponse response = ApportionResponse.builder() + .tenantId(apportionRequest.getTenantId()) + .bills(billInfos) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(apportionRequest.getRequestInfo(), + true)).build(); + return new ResponseEntity<>(response,HttpStatus.OK); + } + + + + +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/controllers/ApportionControllerV2.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/controllers/ApportionControllerV2.java new file mode 100644 index 000000000..028c9ee3d --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/controllers/ApportionControllerV2.java @@ -0,0 +1,75 @@ +package org.egov.web.controllers; + + +import org.egov.service.ApportionServiceV2; +import org.egov.util.ResponseInfoFactory; +import org.egov.web.models.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.egov.web.models.enums.DemandApportionRequest; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.*; + +import javax.validation.Valid; + +@javax.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2019-02-25T15:07:36.183+05:30") + +@Controller +@RequestMapping("/v2") +public class ApportionControllerV2 { + + private final ObjectMapper objectMapper; + + private ApportionServiceV2 apportionService; + + private ResponseInfoFactory responseInfoFactory; + + + @Autowired + public ApportionControllerV2(ObjectMapper objectMapper, ApportionServiceV2 apportionService, ResponseInfoFactory responseInfoFactory) { + this.objectMapper = objectMapper; + this.apportionService = apportionService; + this.responseInfoFactory = responseInfoFactory; + } + + + + + /** + * Executes the apportioning process on the given bills + * @param apportionRequest The ApportionRequest containing the bill to be apportioned + * @return Apportioned Bills + */ + @RequestMapping(value="/bill/_apportion", method = RequestMethod.POST) + public ResponseEntity apportionPost(@Valid @RequestBody ApportionRequest apportionRequest){ + List billInfos = apportionService.apportionBills(apportionRequest); + ApportionResponse response = ApportionResponse.builder() + .tenantId(apportionRequest.getTenantId()) + .bills(billInfos) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(apportionRequest.getRequestInfo(), + true)).build(); + return new ResponseEntity<>(response,HttpStatus.OK); + } + + @RequestMapping(value="/demand/_apportion", method = RequestMethod.POST) + public ResponseEntity apportionPost(@Valid @RequestBody DemandApportionRequest apportionRequest){ + List demands = apportionService.apportionDemands(apportionRequest); + ApportionDemandResponse response = ApportionDemandResponse.builder() + .tenantId(apportionRequest.getTenantId()) + .demands(demands) + .responseInfo(responseInfoFactory.createResponseInfoFromRequestInfo(apportionRequest.getRequestInfo(), + true)).build(); + return new ResponseEntity<>(response,HttpStatus.OK); + } + + + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/enums/CollectionType.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/enums/CollectionType.java new file mode 100644 index 000000000..c185ff1ab --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/enums/CollectionType.java @@ -0,0 +1,44 @@ +package org.egov.web.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public enum CollectionType { + + COUNTER("COUNTER"), + FIELD("FIELD"), + ONLINE("ONLINE"); + + + private String value; + + CollectionType(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + public String getValue() { + return value; + } + + @JsonCreator + public static CollectionType fromValue(String text) { + for (CollectionType b : CollectionType.values()) { + if (0 ==b.value.toString().compareTo(text)) { + return b; + } + } + return null; + } + +} + diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionDemandResponse.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionDemandResponse.java new file mode 100644 index 000000000..76df9d22c --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionDemandResponse.java @@ -0,0 +1,36 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; +import org.egov.common.contract.response.ResponseInfo; + +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; + +@Data +@Builder +public class ApportionDemandResponse { + + + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("demands") + @Valid + private List demands = null; + + + public ApportionDemandResponse addDemandsItem(Demand demand) { + if (this.demands == null) { + this.demands = new ArrayList<>(); + } + this.demands.add(demand); + return this; + } + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionRequest.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionRequest.java new file mode 100644 index 000000000..d53f36810 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionRequest.java @@ -0,0 +1,52 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; + +import java.util.ArrayList; +import java.util.List; + +import org.egov.common.contract.request.RequestInfo; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +/** + * Receipt Request with Request Info. + */ +@ApiModel(description = "Receipt Request with Request Info.") +@Validated +@javax.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2019-02-25T15:07:36.183+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ApportionRequest { + @JsonProperty("RequestInfo") + private RequestInfo requestInfo = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("Bills") + @Valid + private List bills = null; + + + public ApportionRequest addBillsItem(Bill billsItem) { + if (this.bills == null) { + this.bills = new ArrayList<>(); + } + this.bills.add(billsItem); + return this; + } + +} + diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionRequestV2.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionRequestV2.java new file mode 100644 index 000000000..503210d6b --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionRequestV2.java @@ -0,0 +1,39 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Data +@Builder +public class ApportionRequestV2 { + + @JsonProperty("businessService") + private String businessService = null; + + @JsonProperty("amountPaid") + private BigDecimal amountPaid = null; + + @JsonProperty("isAdvanceAllowed") + private Boolean isAdvanceAllowed = null; + + @JsonProperty("buckets") + private List taxDetails = null; + + + public void addTaxDetail(TaxDetail taxDetail){ + if(this.taxDetails==null){ + this.taxDetails = new ArrayList<>(); + this.taxDetails.add(taxDetail); + } + else this.getTaxDetails().add(taxDetail); + + } + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionResponse.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionResponse.java new file mode 100644 index 000000000..b2fb8ccf2 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/ApportionResponse.java @@ -0,0 +1,53 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; + +import java.util.ArrayList; +import java.util.List; + +import org.egov.common.contract.response.ResponseInfo; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +/** + * Receipt Request with Request Info. + */ +@ApiModel(description = "Receipt Request with Request Info.") +@Validated +@javax.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2019-02-25T15:07:36.183+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ApportionResponse { + + @JsonProperty("ResponseInfo") + private ResponseInfo responseInfo = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("Bills") + @Valid + private List bills = null; + + + public ApportionResponse addBillsItem(Bill billsItem) { + if (this.bills == null) { + this.bills = new ArrayList<>(); + } + this.bills.add(billsItem); + return this; + } + +} + diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/AuditDetails.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/AuditDetails.java new file mode 100644 index 000000000..46e7dcd00 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/AuditDetails.java @@ -0,0 +1,44 @@ +package org.egov.web.models; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; +import javax.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +/** + * Collection of audit related fields used by most models + */ +@ApiModel(description = "Collection of audit related fields used by most models") +@Validated +@javax.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2019-02-25T15:07:36.183+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AuditDetails { + @JsonProperty("createdBy") + private String createdBy = null; + + @JsonProperty("lastModifiedBy") + private String lastModifiedBy = null; + + @JsonProperty("createdTime") + private Long createdTime = null; + + @JsonProperty("lastModifiedTime") + private Long lastModifiedTime = null; + + +} + diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Bill.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Bill.java new file mode 100644 index 000000000..b3784163b --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Bill.java @@ -0,0 +1,172 @@ +package org.egov.web.models; + + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.springframework.util.CollectionUtils; + +@Getter +@Setter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class Bill { + // TODO some of the fields are mandatory in yml, lets discuss billdetail and billaccountdetail also for more clarity + + @JsonProperty("id") + private String id = null; + + @JsonProperty("mobileNumber") + private String mobileNumber = null; + + @JsonProperty("paidBy") + private String paidBy = null; + + @JsonProperty("payerName") + private String payerName = null; + + @JsonProperty("payerAddress") + private String payerAddress = null; + + @JsonProperty("payerEmail") + private String payerEmail = null; + + @JsonProperty("payerId") + private String payerId = null; + + @JsonProperty("status") + private StatusEnum status = null; + + @JsonProperty("reasonForCancellation") + private String reasonForCancellation = null; + + @JsonProperty("isCancelled") + private Boolean isCancelled = null; + + @JsonProperty("additionalDetails") + private JsonNode additionalDetails = null; + + @JsonProperty("billDetails") + @Valid + private List billDetails = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + @JsonProperty("collectionModesNotAllowed") + private List collectionModesNotAllowed = null; + + @JsonProperty("partPaymentAllowed") + private Boolean partPaymentAllowed = null; + + @NotNull + @JsonProperty("isAdvanceAllowed") + private Boolean isAdvanceAllowed; + + @JsonProperty("minimumAmountToBePaid") + private BigDecimal minimumAmountToBePaid = null; + + @JsonProperty("businessService") + private String businessService = null; + + @JsonProperty("totalAmount") + private BigDecimal totalAmount = null; + + @JsonProperty("consumerCode") + private String consumerCode = null; + + @JsonProperty("billNumber") + private String billNumber = null; + + @JsonProperty("billDate") + private Long billDate = null; + + @NotNull + @JsonProperty("amountPaid") + private BigDecimal amountPaid = null; + + + + public enum StatusEnum { + ACTIVE("ACTIVE"), + + CANCELLED("CANCELLED"), + + PAID("PAID"), + + EXPIRED("EXPIRED"); + + private String value; + + StatusEnum(String value) { + this.value = value; + } + + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + public static boolean contains(String test) { + for (StatusEnum val : StatusEnum.values()) { + if (val.name().equalsIgnoreCase(test)) { + return true; + } + } + return false; + } + + @JsonCreator + public static StatusEnum fromValue(String text) { + for (StatusEnum b : StatusEnum.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } + + } + + public Boolean addBillDetail(BillDetail billDetail) { + + if (CollectionUtils.isEmpty(billDetails)) { + + billDetails = new ArrayList<>(); + return billDetails.add(billDetail); + } else { + + if (!billDetails.contains(billDetail)) + return billDetails.add(billDetail); + else + return false; + } + } + + +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/BillAccountDetail.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/BillAccountDetail.java new file mode 100644 index 000000000..7c63e65ef --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/BillAccountDetail.java @@ -0,0 +1,69 @@ +package org.egov.web.models; + + +import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.egov.web.models.AuditDetails; +import org.egov.web.models.enums.Purpose; + +import javax.validation.constraints.Size; + +@Setter +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class BillAccountDetail { + + @Size(max=64) + @JsonProperty("id") + private String id = null; + + @Size(max=64) + @JsonProperty("tenantId") + private String tenantId = null; + + @Size(max=64) + @JsonProperty("billDetailId") + private String billDetailId = null; + + @Size(max=64) + @JsonProperty("demandDetailId") + private String demandDetailId = null; + + @JsonProperty("order") + private Integer order = null; + + @JsonProperty("amount") + private BigDecimal amount = null; + + @JsonProperty("adjustedAmount") + private BigDecimal adjustedAmount = null; + + @JsonProperty("isActualDemand") + private Boolean isActualDemand = null; + + @Size(max=64) + @JsonProperty("taxHeadCode") + private String taxHeadCode = null; + + @JsonProperty("additionalDetails") + private JsonNode additionalDetails = null; + + @JsonProperty("purpose") + private Purpose purpose = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails; +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/BillDetail.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/BillDetail.java new file mode 100644 index 000000000..9740c9d16 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/BillDetail.java @@ -0,0 +1,127 @@ +package org.egov.web.models; + + + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import javax.validation.constraints.NotNull; +import org.egov.web.enums.CollectionType; +import org.egov.web.models.AuditDetails; +import org.egov.web.models.BillAccountDetail; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Setter +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +public class BillDetail { + + @JsonProperty("id") + private String id = null; + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("demandId") + private String demandId = null; + + @JsonProperty("billId") + private String billId = null; + + @JsonProperty("amount") + @NotNull + private BigDecimal amount = null; + + @JsonProperty("amountPaid") + private BigDecimal amountPaid = null; + + @NotNull + @JsonProperty("fromPeriod") + private Long fromPeriod = null; + + @NotNull + @JsonProperty("toPeriod") + private Long toPeriod = null; + + @JsonProperty("collectedAmount") + private BigDecimal collectedAmount = null; + + @JsonProperty("additionalDetails") + private JsonNode additionalDetails = null; + + @JsonProperty("receiptDate") + private Long receiptDate = null; + + @JsonProperty("receiptType") + private String receiptType = null; + + @JsonProperty("channel") + private String channel = null; + + @JsonProperty("voucherHeader") + private String voucherHeader = null; + + @JsonProperty("boundary") + private String boundary = null; + + @JsonProperty("manualReceiptNumber") + private String manualReceiptNumber = null; + + @JsonProperty("manualReceiptDate") + private Long manualReceiptDate = null; + + + @JsonProperty("billAccountDetails") + private List billAccountDetails = null; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails = null; + + + private String billDescription; + + @NotNull + @JsonProperty("expiryDate") + private Long expiryDate; + + private String displayMessage; + + private Boolean callBackForApportioning; + + private String cancellationRemarks; + + public Boolean addBillAccountDetail(BillAccountDetail billAccountDetail) { + + if (CollectionUtils.isEmpty(billAccountDetails)) { + + billAccountDetails = new ArrayList<>(); + return billAccountDetails.add(billAccountDetail); + } else { + + if (!billAccountDetails.contains(billAccountDetail)) + return billAccountDetails.add(billAccountDetail); + else + return false; + } + } + +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Bucket.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Bucket.java new file mode 100644 index 000000000..11d40016f --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Bucket.java @@ -0,0 +1,41 @@ +package org.egov.web.models; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.web.models.enums.Category; +import org.egov.web.models.enums.Purpose; + +import java.math.BigDecimal; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Bucket { + + @JsonProperty("taxHeadCode") + private String taxHeadCode = null; + + @JsonProperty("category") + private Category category = null; + + @JsonProperty("amount") + private BigDecimal amount = null; + + @JsonProperty("adjustedAmount") + private BigDecimal adjustedAmount = null; + + @JsonProperty("priority") + private Integer priority = null; + + @JsonProperty("purpose") + private Purpose purpose = null; + + @JsonProperty("entityId") + private String entityId = null; + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Demand.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Demand.java new file mode 100644 index 000000000..7635cf9c9 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Demand.java @@ -0,0 +1,124 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.User; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * A Object which holds the basic info about the revenue assessment for which the demand is generated like module name, consumercode, owner, etc. + */ + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Demand { + + @JsonProperty("id") + private String id; + + @NotNull + @JsonProperty("tenantId") + private String tenantId; + + @NotNull + @JsonProperty("consumerCode") + private String consumerCode; + + @NotNull + @JsonProperty("consumerType") + private String consumerType; + + @NotNull + @JsonProperty("businessService") + private String businessService; + + @Valid + @JsonProperty("payer") + private User payer; + + @NotNull + @JsonProperty("taxPeriodFrom") + private Long taxPeriodFrom; + + @NotNull + @JsonProperty("taxPeriodTo") + private Long taxPeriodTo; + + @Default + @JsonProperty("demandDetails") + @Valid + @NotNull + @Size(min=1) + private List demandDetails = new ArrayList<>(); + + @JsonProperty("auditDetails") + private AuditDetails auditDetails; + + @JsonProperty("billExpiryTime") + private Long billExpiryTime; + + @JsonProperty("additionalDetails") + private Object additionalDetails; + + @Default + @JsonProperty("minimumAmountPayable") + private BigDecimal minimumAmountPayable = BigDecimal.ZERO; + + /** + * Gets or Sets status + */ + public enum StatusEnum { + + ACTIVE("ACTIVE"), + + CANCELLED("CANCELLED"), + + ADJUSTED("ADJUSTED"); + + private String value; + + StatusEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static StatusEnum fromValue(String text) { + for (StatusEnum b : StatusEnum.values()) { + if (String.valueOf(b.value).equalsIgnoreCase(text)) { + return b; + } + } + return null; + } + } + + @JsonProperty("status") + private StatusEnum status; + + + public Demand addDemandDetailsItem(DemandDetail demandDetailsItem) { + this.demandDetails.add(demandDetailsItem); + return this; + } + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/DemandDetail.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/DemandDetail.java new file mode 100644 index 000000000..95469ac2b --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/DemandDetail.java @@ -0,0 +1,47 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +/** + * A object holds a demand and collection values for a tax head and period. + */ + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DemandDetail { + + @NotNull + @JsonProperty("id") + private String id; + + @JsonProperty("demandId") + private String demandId; + + @NotNull @JsonProperty("taxHeadMasterCode") + private String taxHeadMasterCode; + + @NotNull @JsonProperty("taxAmount") + private BigDecimal taxAmount; + + @NotNull @JsonProperty("collectionAmount") @Default + private BigDecimal collectionAmount = BigDecimal.ZERO; + + @JsonProperty("additionalDetails") + private Object additionalDetails; + + @JsonProperty("auditDetails") + private AuditDetails auditDetails; + + @JsonProperty("tenantId") + private String tenantId; +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Role.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Role.java new file mode 100644 index 000000000..879d3c6a5 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/Role.java @@ -0,0 +1,38 @@ +package org.egov.web.models; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; +import javax.validation.constraints.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Builder; + +/** + * minimal representation of the Roles in the system to be carried along in UserInfo with RequestInfo meta data. Actual authorization service to extend this to have more role related attributes + */ +@ApiModel(description = "minimal representation of the Roles in the system to be carried along in UserInfo with RequestInfo meta data. Actual authorization service to extend this to have more role related attributes ") +@Validated +@javax.annotation.Generated(value = "org.egov.codegen.SpringBootCodegen", date = "2019-02-25T15:07:36.183+05:30") + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class Role { + @JsonProperty("name") + private String name = null; + + @JsonProperty("description") + private String description = null; + + +} + diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxAndPayment.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxAndPayment.java new file mode 100644 index 000000000..59298da3c --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxAndPayment.java @@ -0,0 +1,21 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TaxAndPayment { + + @JsonProperty("businessService") + private String businessService = null; + + @JsonProperty("taxAmount") + private BigDecimal taxAmount = null; + + @JsonProperty("amountPaid") + private BigDecimal amountPaid = null; + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxDetail.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxDetail.java new file mode 100644 index 000000000..50afc3bef --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxDetail.java @@ -0,0 +1,42 @@ +package org.egov.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Data +@Builder +public class TaxDetail { + + + @NotNull + @JsonProperty("entityId") + private String entityId = null; + + @JsonProperty("amountPaid") + private BigDecimal amountPaid = null; + + @JsonProperty("amountToBePaid") + private BigDecimal amountToBePaid = null; + + @JsonProperty("fromPeriod") + private Long fromPeriod = null; + + @JsonProperty("buckets") + private List buckets = null; + + public void addBucket(Bucket bucket){ + if(this.buckets==null){ + this.buckets = new ArrayList<>(); + this.buckets.add(bucket); + } + else this.getBuckets().add(bucket); + + } + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxHeadMaster.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxHeadMaster.java new file mode 100644 index 000000000..26e6dc817 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/TaxHeadMaster.java @@ -0,0 +1,44 @@ +package org.egov.web.models; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.web.models.enums.Category; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TaxHeadMaster { + + private String id; + + @NotNull + private String tenantId; + @Valid + @NotNull + private Category category; + @NotNull + private String service; + @NotNull + private String name; + + private String code; + + private Boolean isDebit = false; + + private Boolean isActualDemand; + @NotNull + private Long validFrom; + @NotNull + private Long validTill; + + private Integer order; + + private AuditDetails auditDetail; + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/Category.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/Category.java new file mode 100644 index 000000000..5fcc891f4 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/Category.java @@ -0,0 +1,48 @@ +package org.egov.web.models.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Category of demand like tax, fee, rebate, penalty etc. + */ +public enum Category { + + TAX("TAX"), + + FEE("FEE"), + + REBATE("REBATE"), + + EXEMPTION("EXEMPTION"), + + ADVANCE_COLLECTION("ADVANCE_COLLECTION"), + + PENALTY("PENALTY"), + + FINES("FINES"), + + CHARGES("CHARGES"); + + private String value; + + Category(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static Category fromValue(String text) { + for (Category b : Category.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/DemandApportionRequest.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/DemandApportionRequest.java new file mode 100644 index 000000000..a4844bda5 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/DemandApportionRequest.java @@ -0,0 +1,37 @@ +package org.egov.web.models.enums; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.egov.common.contract.request.RequestInfo; +import org.egov.web.models.Demand; + +import javax.validation.Valid; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DemandApportionRequest { + + + @JsonProperty("RequestInfo") + private RequestInfo requestInfo = null; + + + @JsonProperty("tenantId") + private String tenantId = null; + + @JsonProperty("Demands") + @Valid + private List demands = null; + + + + + +} diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/Purpose.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/Purpose.java new file mode 100644 index 000000000..2fe7c8f55 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/Purpose.java @@ -0,0 +1,47 @@ +package org.egov.web.models.enums; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public enum Purpose { + + ARREAR_AMOUNT("ARREAR"), + + CURRENT_AMOUNT("CURRENT"), + + ADVANCE_AMOUNT("ADVANCE"), + + EXEMPTION("EXEMPTION"), + + ARREAR_LATEPAYMENT_CHARGES("ARREAR_LATEPAYMENT_CHARGES"), + + CURRENT_LATEPAYMENT_CHARGES("CURRENT_LATEPAYMENT_CHARGES"), + + CHEQUE_BOUNCE_PENALTY("CHEQUE_BOUNCE_PENALTY"), + + REBATE("REBATE"), + + OTHERS("OTHERS"); + + private String value; + + Purpose(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static Purpose fromValue(String text) { + for (Purpose b : Purpose.values()) { + if (String.valueOf(b.value).equals(text)) { + return b; + } + } + return null; + } +} \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/ReceiptType.java b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/ReceiptType.java new file mode 100644 index 000000000..a21963563 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/java/org/egov/web/models/enums/ReceiptType.java @@ -0,0 +1,30 @@ +package org.egov.web.models.enums; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.annotation.JsonValue; + + public enum ReceiptType { + ADHOC("Adhoc"), + BILLBASED("BillBased"), + CHALLAN("Challan"); + + + private String name; + + ReceiptType(final String name) { + this.name = name; + } + + @Override + @JsonValue + public String toString() { + return StringUtils.capitalize(name()); + } + + public String getName() { + return name; + } + + } + diff --git a/business-services/egov-apportion-service/src/main/resources/application.properties b/business-services/egov-apportion-service/src/main/resources/application.properties new file mode 100644 index 000000000..aa9f6186a --- /dev/null +++ b/business-services/egov-apportion-service/src/main/resources/application.properties @@ -0,0 +1,57 @@ +server.contextPath=/apportion-service +server.servlet.context-path=/apportion-service +server.port=8280 +app.timezone=UTC + +spring.datasource.driver-class-name=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/devbackup +spring.datasource.username=postgres +spring.datasource.password=postgres + +##----------------------------- FLYWAY CONFIGURATIONS ------------------------------# + +spring.flyway.url=jdbc:postgresql://localhost:5432/test +spring.flyway.user=postgres +spring.flyway.password=postgres +#spring.flyway.table=public +spring.flyway.baseline-on-migrate=true +spring.flyway.outOfOrder=true +spring.flyway.locations=classpath:/db/migration/main +spring.flyway.enabled=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-tl-services +spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer +spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer +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 +#org.egov.detailed.tracing.enabled = false + +#Persister Config +persister.save.bill.apportion.request.topic=save-apportion-bill-request +persister.save.bill.apportion.response.topic=save-apportion-bill-response +persister.save.demand.apportion.request.topic=save-apportion-demand-request +persister.save.demand.apportion.response.topic=save-apportion-demand-response + +#MDMS Config +egov.mdms.host=https://dev.digit.org +egov.mdms.search.endpoint=/egov-mdms-service/v1/_search + +#Default apportion switch +egov.apportion.default.value.order=true + +management.endpoints.web.base-path=/ +spring.main.allow-bean-definition-overriding=false diff --git a/business-services/egov-apportion-service/src/main/resources/apportion-persister.yml b/business-services/egov-apportion-service/src/main/resources/apportion-persister.yml new file mode 100644 index 000000000..9416d1567 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/resources/apportion-persister.yml @@ -0,0 +1,220 @@ +serviceMaps: + serviceName: apportion-services + mappings: + - version: 1.0 + description: Persists apportioning request + fromTopic: save-apportion-request + isTransaction: true + queryMaps: + + - query: INSERT INTO eg_appr_bills_request(billId,tenantid,mobileNumber,payerName,payerAddress,payerEmail,paidBy,taxAndPayments,isActive,isCancelled,billDetails,createdBy,createdTime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + basePath: Bills.* + jsonMaps: + - jsonPath: $.Bills.*.id + + - jsonPath: $.Bills.*.tenantId + + - jsonPath: $.Bills.*.mobileNumber + + - jsonPath: $.Bills.*.payerName + + - jsonPath: $.Bills.*.payerAddress + + - jsonPath: $.Bills.*.payerEmail + + - jsonPath: $.Bills.*.paidBy + + - jsonPath: $.Bills.*.taxAndPayments + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.isActive + + - jsonPath: $.Bills.*.isCancelled + + - jsonPath: $.Bills.*.billDetails + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.auditDetails.createdBy + + - jsonPath: $.Bills.*.auditDetails.createdTime + + + - version: 1.0 + description: Persists apportioning response + fromTopic: save-apportion-response + isTransaction: true + queryMaps: + + - query: INSERT INTO eg_appr_bills_response(billId,tenantid,mobileNumber,payerName,payerAddress,payerEmail,paidBy,taxAndPayments,isActive,isCancelled,billDetails,createdBy,createdTime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + basePath: Bills.* + jsonMaps: + - jsonPath: $.Bills.*.id + + - jsonPath: $.Bills.*.tenantId + + - jsonPath: $.Bills.*.mobileNumber + + - jsonPath: $.Bills.*.payerName + + - jsonPath: $.Bills.*.payerAddress + + - jsonPath: $.Bills.*.payerEmail + + - jsonPath: $.Bills.*.paidBy + + - jsonPath: $.Bills.*.taxAndPayments + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.isActive + + - jsonPath: $.Bills.*.isCancelled + + - jsonPath: $.Bills.*.billDetails + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.auditDetails.createdBy + + - jsonPath: $.Bills.*.auditDetails.createdTime + + - version: 1.0 + description: Persists apportioning request + fromTopic: save-apportion-bill-request + isTransaction: true + queryMaps: + + - query: INSERT INTO eg_appr_bills_request(billId,tenantid,mobileNumber,payerName,payerAddress,payerEmail,paidBy,taxAndPayments,isActive,isCancelled,billDetails,createdBy,createdTime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + basePath: Bills.* + jsonMaps: + - jsonPath: $.Bills.*.id + + - jsonPath: $.Bills.*.tenantId + + - jsonPath: $.Bills.*.mobileNumber + + - jsonPath: $.Bills.*.payerName + + - jsonPath: $.Bills.*.payerAddress + + - jsonPath: $.Bills.*.payerEmail + + - jsonPath: $.Bills.*.paidBy + + - jsonPath: $.Bills.*.taxAndPayments + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.isActive + + - jsonPath: $.Bills.*.isCancelled + + - jsonPath: $.Bills.*.billDetails + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.auditDetails.createdBy + + - jsonPath: $.Bills.*.auditDetails.createdTime + + + - version: 1.0 + description: Persists apportioning response + fromTopic: save-apportion-bill-response + isTransaction: true + queryMaps: + + - query: INSERT INTO eg_appr_bills_response(billId,tenantid,mobileNumber,payerName,payerAddress,payerEmail,paidBy,taxAndPayments,isActive,isCancelled,billDetails,createdBy,createdTime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + basePath: Bills.* + jsonMaps: + - jsonPath: $.Bills.*.id + + - jsonPath: $.Bills.*.tenantId + + - jsonPath: $.Bills.*.mobileNumber + + - jsonPath: $.Bills.*.payerName + + - jsonPath: $.Bills.*.payerAddress + + - jsonPath: $.Bills.*.payerEmail + + - jsonPath: $.Bills.*.paidBy + + - jsonPath: $.Bills.*.taxAndPayments + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.isActive + + - jsonPath: $.Bills.*.isCancelled + + - jsonPath: $.Bills.*.billDetails + type: JSON + dbType: JSONB + + - jsonPath: $.Bills.*.auditDetails.createdBy + + - jsonPath: $.Bills.*.auditDetails.createdTime + + + - version: 1.0 + description: Persists apportioning request + fromTopic: save-apportion-demand-request + isTransaction: true + queryMaps: + + - query: INSERT INTO eg_appr_demand_request(demandId,tenantid,consumerCode,demandDetails,taxPeriodFrom,taxPeriodTo,status,createdBy,createdTime) VALUES (?,?,?,?,?,?,?,?,?) + basePath: Demands.* + jsonMaps: + - jsonPath: $.Demands.*.id + + - jsonPath: $.Demands.*.tenantId + + - jsonPath: $.Demands.*.consumerCode + + - jsonPath: $.Demands.*.demandDetails + type: JSON + dbType: JSONB + + - jsonPath: $.Demands.*.taxPeriodFrom + + - jsonPath: $.Demands.*.taxPeriodTo + + - jsonPath: $.Demands.*.status + + - jsonPath: $.Demands.*.auditDetails.createdBy + + - jsonPath: $.Demands.*.auditDetails.createdTime + + + - version: 1.0 + description: Persists apportioning response + fromTopic: save-apportion-demand-response + isTransaction: true + queryMaps: + + - query: INSERT INTO eg_appr_demand_response(demandId,tenantid,consumerCode,demandDetails,taxPeriodFrom,taxPeriodTo,status,createdBy,createdTime) VALUES (?,?,?,?,?,?,?,?,?) + basePath: Demands.* + jsonMaps: + - jsonPath: $.Demands.*.id + + - jsonPath: $.Demands.*.tenantId + + - jsonPath: $.Demands.*.consumerCode + + - jsonPath: $.Demands.*.demandDetails + type: JSON + dbType: JSONB + + - jsonPath: $.Demands.*.taxPeriodFrom + + - jsonPath: $.Demands.*.taxPeriodTo + + - jsonPath: $.Demands.*.status + + - jsonPath: $.Demands.*.auditDetails.createdBy + + - jsonPath: $.Demands.*.auditDetails.createdTime diff --git a/business-services/egov-apportion-service/src/main/resources/db/Dockerfile b/business-services/egov-apportion-service/src/main/resources/db/Dockerfile new file mode 100644 index 000000000..a5699ff7d --- /dev/null +++ b/business-services/egov-apportion-service/src/main/resources/db/Dockerfile @@ -0,0 +1,9 @@ +FROM egovio/flyway:4.1.2 + +COPY ./migration/main /flyway/sql + +COPY migrate.sh /usr/bin/migrate.sh + +RUN chmod +x /usr/bin/migrate.sh + +CMD ["/usr/bin/migrate.sh"] diff --git a/business-services/egov-apportion-service/src/main/resources/db/migrate.sh b/business-services/egov-apportion-service/src/main/resources/db/migrate.sh new file mode 100644 index 000000000..43960b25c --- /dev/null +++ b/business-services/egov-apportion-service/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 -ignoreMissingMigrations=true migrate \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20190226180533__eg_appr_create_ddl.sql b/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20190226180533__eg_appr_create_ddl.sql new file mode 100644 index 000000000..22d0fd259 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20190226180533__eg_appr_create_ddl.sql @@ -0,0 +1,31 @@ +CREATE TABLE eg_appr_bills_request( + billDetails JSONB, + tenantId character varying(64), + payerName character varying(256), + payerAddress character varying(1024), + payerEmail character varying(254), + paidBy character varying(1024), + collectionMap JSONB, + billId character varying(64), + isActive boolean, + isCancelled boolean, + mobileNumber character varying(64), + createdBy character varying(64), + createdTime bigint + ); + + CREATE TABLE eg_appr_bills_response( + billDetails JSONB, + tenantId character varying(64), + payerName character varying(256), + payerAddress character varying(1024), + payerEmail character varying(254), + paidBy character varying(1024), + collectionMap JSONB, + billId character varying(64), + isActive boolean, + isCancelled boolean, + mobileNumber character varying(64), + createdBy character varying(64), + createdTime bigint + ); \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20190311135135__eg_alter_column_ddl.sql b/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20190311135135__eg_alter_column_ddl.sql new file mode 100644 index 000000000..e238e5a17 --- /dev/null +++ b/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20190311135135__eg_alter_column_ddl.sql @@ -0,0 +1,5 @@ +ALTER TABLE eg_appr_bills_request +RENAME COLUMN collectionMap TO taxAndPayments; + +ALTER TABLE eg_appr_bills_response +RENAME COLUMN collectionMap TO taxAndPayments; \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20200803122838__eg_appr_demand_ddl.sql b/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20200803122838__eg_appr_demand_ddl.sql new file mode 100644 index 000000000..4b8edd62d --- /dev/null +++ b/business-services/egov-apportion-service/src/main/resources/db/migration/main/V20200803122838__eg_appr_demand_ddl.sql @@ -0,0 +1,23 @@ +CREATE TABLE eg_appr_demand_request( +demandId character varying(128), +tenantId character varying(128), +consumerCode character varying(128), +taxperiodfrom bigint, +taxperiodto bigint, +"status" character varying(128), +demandDetails JSONB, +createdBy character varying(64), +createdTime bigint +); + +CREATE TABLE eg_appr_demand_response( +demandId character varying(128), +tenantId character varying(128), +consumerCode character varying(128), +taxperiodfrom bigint, +taxperiodto bigint, +"status" character varying(128), +demandDetails JSONB, +createdBy character varying(64), +createdTime bigint +); \ No newline at end of file diff --git a/business-services/egov-apportion-service/src/test/java/org/egov/TestConfiguration.java b/business-services/egov-apportion-service/src/test/java/org/egov/TestConfiguration.java new file mode 100644 index 000000000..d13056a19 --- /dev/null +++ b/business-services/egov-apportion-service/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/business-services/egov-apportion-service/src/test/java/org/egov/web/controllers/ApportionControllerTest.java b/business-services/egov-apportion-service/src/test/java/org/egov/web/controllers/ApportionControllerTest.java new file mode 100644 index 000000000..727a04eed --- /dev/null +++ b/business-services/egov-apportion-service/src/test/java/org/egov/web/controllers/ApportionControllerTest.java @@ -0,0 +1,44 @@ +package org.egov.web.controllers; + +import org.junit.Test; +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.egov.TestConfiguration; + +import static org.mockito.Matchers.any; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** +* API tests for ApportionController +*/ +@Ignore +@RunWith(SpringRunner.class) +@WebMvcTest(ApportionController.class) +@Import(TestConfiguration.class) +public class ApportionControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void apportionPostSuccess() throws Exception { + mockMvc.perform(post("/apportion/_apportion").contentType(MediaType + .APPLICATION_JSON_UTF8)) + .andExpect(status().isOk()); + } + + @Test + public void apportionPostFailure() throws Exception { + mockMvc.perform(post("/apportion/_apportion").contentType(MediaType + .APPLICATION_JSON_UTF8)) + .andExpect(status().isBadRequest()); + } + +} diff --git a/frontend/micro-ui/web/install-deps.sh b/frontend/micro-ui/web/install-deps.sh old mode 100644 new mode 100755 diff --git a/frontend/micro-ui/web/public/index.html b/frontend/micro-ui/web/public/index.html index 51e8b7805..fddbc103c 100644 --- a/frontend/micro-ui/web/public/index.html +++ b/frontend/micro-ui/web/public/index.html @@ -10,6 +10,44 @@ mSeva +