From 3292733c83b61eb3a28536812b61793e7e53fce1 Mon Sep 17 00:00:00 2001 From: shubhang-eGov <70943369+shubhang-eGov@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:35:36 +0530 Subject: [PATCH] Develop to master works v1.1 (#1776) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set limit to null (#1748) * set limit to null * removed workflow util * revert pagination limit * set limit to null in bank accounts * Updated coderabit config for develop (#1754) Co-authored-by: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com> * PFM-5342 muster roll cherry pick (#1739) * PFM-5342 muster roll changes for sor migration * PFM-5342 added mdmsV2 host * PFM-4167-Rate analysis pdf (#1765) * PFM-4167-Rate analysis pdf transformation * added code for pdf * Added logic for pdf creation * changed host * Update config.js * changed end points * changed name * changed context path * c * added sorting of data for pdf * added sorting of data for pdf * added sorting of data for pdf * added not null tenant id criteria * added round off in amount * added round off in amount * added round off in amount * added round off in amount * added round off in amount * Pfm 4167 rate analysis pdf (#1766) * PFM-4167-Rate analysis pdf transformation * added code for pdf * Added logic for pdf creation * changed host * Update config.js * changed end points * changed name * changed context path * c * added sorting of data for pdf * added sorting of data for pdf * added sorting of data for pdf * added not null tenant id criteria * added round off in amount * added round off in amount * added round off in amount * added round off in amount * added round off in amount * added localization modules * Pfm 5343 labour migration cherry pick (#1740) * PFM-5343 Service changes to fetch rate from sor * PFM-5343 Changes rate details as a list in sor details * PFM-5343 added exceptions * PFM-5343 added mdms v2 host * changed validation time to string * PFM-5342 worflow model change * Updated version for works 1.1 relese (#1774) Co-authored-by: shailesh-egov * Master to develop sync (#1775) * HLM-5539, 5361: updated attendance module, updated individual search … (#1746) * HLM-5539, 5361: updated attendance module, updated individual search parameters * Update CHANGELOG.md * Update pom.xml * Update CHANGELOG.md --------- Co-authored-by: kavi_elrey@1993 <25226238+kavi-egov@users.noreply.github.com> * added rate analysis (#1747) * added redis config in disbursement (#1736) * added redis config in disbursement * added redis config in disbursement * added redis config in disbursement * added already created validation in consumer * Update DisbursementService.java Added functionality of saving pi in db beffore sending it to jit * Fixed shown disburement amount in pi Fixed shown disburement amount in pi * Update RedisService.java Added some log for redis * Update DisbursementService.java saved pi details before calling to JIT * added caching to check weather payment is already processed * added caching to check weather payment is already processed * Added test payment create and added better error handling in ifms * Added test payment create and added better error handling in ifms * Fixed issue of on disburse * rollback ifms service * added caching to check weather payment is already processed * removed ssl certification * removed ssl * added caching to check weather payment is already processed * Added error queue in ifms adapter. * fixed sort order in disburse query * Added queue for error * fixed disburse enrich * fixed on-disburse * fixed on-disburse * fixed disburse mukta ref id * fixed pi search * fixed disburse search * fixed disburse search * fixed disburse search --------- Co-authored-by: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com> * Update build-config.yml (#1751) * Updated coderabit config for master (#1753) Co-authored-by: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com> * HLM lts updated flyway for health attendance (#1749) * HLM 2.9lts updated flyway for health attendance * Update pom.xml * Updated CHANGELOG.md * Updated CHANGELOG.md, added flyway migration update changelog * Update backend/attendance/CHANGELOG.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: kavi_elrey@1993 <25226238+kavi-egov@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Hcm lts master flyway imageupdate (#1764) * HLM 2.9lts updated flyway for health attendance * Update pom.xml * Updated CHANGELOG.md * Updated CHANGELOG.md, added flyway migration update changelog * HLM added first staff insert check to validate the attendee for hcm use case --------- Co-authored-by: kavi_elrey@1993 <25226238+kavi-egov@users.noreply.github.com> --------- Co-authored-by: kanishq-egov <138671649+kanishq-egov@users.noreply.github.com> Co-authored-by: kavi_elrey@1993 <25226238+kavi-egov@users.noreply.github.com> Co-authored-by: ansh-egov <137172017+ansh-egov@users.noreply.github.com> Co-authored-by: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com> Co-authored-by: manastanmay-eGov <118505430+manastanmay-eGov@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Lokendra-egov <137176739+Lokendra-egov@users.noreply.github.com> Co-authored-by: ansh-egov <137172017+ansh-egov@users.noreply.github.com> Co-authored-by: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com> Co-authored-by: shailesh-egov Co-authored-by: kanishq-egov <138671649+kanishq-egov@users.noreply.github.com> Co-authored-by: kavi_elrey@1993 <25226238+kavi-egov@users.noreply.github.com> Co-authored-by: manastanmay-eGov <118505430+manastanmay-eGov@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- backend/bankaccounts/pom.xml | 2 +- .../main/java/org/egov/util/WorkflowUtil.java | 177 ----------- .../java/org/egov/web/models/Pagination.java | 4 +- backend/expense-calculator/pom.xml | 2 +- .../ExpenseCalculatorConfiguration.java | 6 + .../service/ExpenseCalculatorService.java | 104 ++++--- .../WageSeekerBillGeneratorService.java | 80 +++-- .../expense/calculator/util/BillUtils.java | 8 +- .../ExpenseCalculatorServiceConstants.java | 10 + .../expense/calculator/util/MdmsUtils.java | 43 ++- .../BillCalculatorRequestInfoWrapper.java | 1 - .../calculator/web/models/RateDetail.java | 29 ++ .../calculator/web/models/SorDetail.java | 41 +++ .../src/main/resources/application.properties | 283 +++++++++--------- backend/muster-roll/pom.xml | 2 +- .../MusterRollServiceConfiguration.java | 4 + .../org/egov/service/CalculationService.java | 4 +- .../org/egov/service/MusterRollService.java | 4 +- .../src/main/java/org/egov/util/MdmsUtil.java | 30 ++ .../org/egov/util/MusterRollServiceUtil.java | 14 +- .../src/main/resources/application.properties | 3 + utilities/works-pdf/package.json | 2 +- utilities/works-pdf/src/api.js | 57 +++- utilities/works-pdf/src/app.js | 4 + utilities/works-pdf/src/config.js | 12 +- .../works-pdf/src/routes/analysisStatement.js | 154 ++++++++++ .../src/routes/utilizationStatement.js | 154 ++++++++++ .../src/utils/transformStatementData.js | 118 ++++++++ 28 files changed, 947 insertions(+), 405 deletions(-) delete mode 100644 backend/bankaccounts/src/main/java/org/egov/util/WorkflowUtil.java create mode 100644 backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/RateDetail.java create mode 100644 backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/SorDetail.java create mode 100644 utilities/works-pdf/src/routes/analysisStatement.js create mode 100644 utilities/works-pdf/src/routes/utilizationStatement.js create mode 100644 utilities/works-pdf/src/utils/transformStatementData.js diff --git a/backend/bankaccounts/pom.xml b/backend/bankaccounts/pom.xml index dfc943411f..3324bc3575 100644 --- a/backend/bankaccounts/pom.xml +++ b/backend/bankaccounts/pom.xml @@ -5,7 +5,7 @@ bankaccounts jar bankaccounts - 0.1.3 + 0.1.4 1.8 ${java.version} diff --git a/backend/bankaccounts/src/main/java/org/egov/util/WorkflowUtil.java b/backend/bankaccounts/src/main/java/org/egov/util/WorkflowUtil.java deleted file mode 100644 index b669850a97..0000000000 --- a/backend/bankaccounts/src/main/java/org/egov/util/WorkflowUtil.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.egov.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import digit.models.coremodels.*; -import org.egov.common.contract.request.RequestInfo; -import org.egov.common.contract.request.User; -import org.egov.config.Configuration; -import org.egov.repository.ServiceRequestRepository; -import org.egov.tracer.model.CustomException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.*; -import java.util.stream.Collectors; - -@Service -public class WorkflowUtil { - - @Autowired - private ServiceRequestRepository repository; - - @Autowired - private ObjectMapper mapper; - - @Autowired - private Configuration configs; - - - /** - * Searches the BussinessService corresponding to the businessServiceCode - * Returns applicable BussinessService for the given parameters - * - * @param requestInfo - * @param tenantId - * @param businessServiceCode - * @return - */ - public BusinessService getBusinessService(RequestInfo requestInfo, String tenantId, String businessServiceCode) { - - StringBuilder url = getSearchURLWithParams(tenantId, businessServiceCode); - RequestInfoWrapper requestInfoWrapper = RequestInfoWrapper.builder().requestInfo(requestInfo).build(); - Object result = repository.fetchResult(url, requestInfoWrapper); - BusinessServiceResponse response = null; - try { - response = mapper.convertValue(result, BusinessServiceResponse.class); - } catch (IllegalArgumentException e) { - throw new CustomException("PARSING ERROR", "Failed to parse response of workflow business service search"); - } - - if (CollectionUtils.isEmpty(response.getBusinessServices())) - throw new CustomException("BUSINESSSERVICE_NOT_FOUND", "The businessService " + businessServiceCode + " is not found"); - - return response.getBusinessServices().get(0); - } - - /** - * Calls the workflow service with the given action and updates the status - * Returns the updated status of the application - * - * @param requestInfo - * @param tenantId - * @param businessId - * @param businessServiceCode - * @param workflow - * @param wfModuleName - * @return - */ - public String updateWorkflowStatus(RequestInfo requestInfo, String tenantId, - String businessId, String businessServiceCode, Workflow workflow, String wfModuleName) { - ProcessInstance processInstance = getProcessInstanceForWorkflow(requestInfo, tenantId, businessId, - businessServiceCode, workflow, wfModuleName); - ProcessInstanceRequest workflowRequest = new ProcessInstanceRequest(requestInfo, Collections.singletonList(processInstance)); - State state = callWorkFlow(workflowRequest); - - return state.getApplicationStatus(); - } - - /** - * Creates url for search based on given tenantId and businessServices - * - * @param tenantId - * @param businessService - * @return - */ - private StringBuilder getSearchURLWithParams(String tenantId, String businessService) { - StringBuilder url = new StringBuilder(configs.getWfHost()); - url.append(configs.getWfBusinessServiceSearchPath()); - url.append("?tenantId="); - url.append(tenantId); - url.append("&businessServices="); - url.append(businessService); - return url; - } - - /** - * Enriches ProcessInstance Object for Workflow - * - * @param requestInfo - * @param tenantId - * @param businessId - * @param businessServiceCode - * @param workflow - * @param wfModuleName - * @return - */ - private ProcessInstance getProcessInstanceForWorkflow(RequestInfo requestInfo, String tenantId, - String businessId, String businessServiceCode, Workflow workflow, String wfModuleName) { - - ProcessInstance processInstance = new ProcessInstance(); - processInstance.setBusinessId(businessId); - processInstance.setAction(workflow.getAction()); - processInstance.setModuleName(wfModuleName); - processInstance.setTenantId(tenantId); - processInstance.setBusinessService(getBusinessService(requestInfo, tenantId, businessServiceCode).getBusinessService()); - processInstance.setDocuments(workflow.getVerificationDocuments()); - processInstance.setComment(workflow.getComments()); - - if (!CollectionUtils.isEmpty(workflow.getAssignes())) { - List users = new ArrayList<>(); - - workflow.getAssignes().forEach(uuid -> { - User user = new User(); - user.setUuid(uuid); - users.add(user); - }); - - processInstance.setAssignes(users); - } - - return processInstance; - } - - /** - * Gets the workflow corresponding to the processInstance - * - * @param processInstances - * @return - */ - public Map getWorkflow(List processInstances) { - - Map businessIdToWorkflow = new HashMap<>(); - - processInstances.forEach(processInstance -> { - List userIds = null; - - if (!CollectionUtils.isEmpty(processInstance.getAssignes())) { - userIds = processInstance.getAssignes().stream().map(User::getUuid).collect(Collectors.toList()); - } - - Workflow workflow = Workflow.builder() - .action(processInstance.getAction()) - .assignes(userIds) - .comments(processInstance.getComment()) - .verificationDocuments(processInstance.getDocuments()) - .build(); - - businessIdToWorkflow.put(processInstance.getBusinessId(), workflow); - }); - - return businessIdToWorkflow; - } - - /** - * Method to take the ProcessInstanceRequest as parameter and set resultant status - * - * @param workflowReq - * @return - */ - private State callWorkFlow(ProcessInstanceRequest workflowReq) { - ProcessInstanceResponse response = null; - StringBuilder url = new StringBuilder(configs.getWfHost().concat(configs.getWfTransitionPath())); - Object optional = repository.fetchResult(url, workflowReq); - response = mapper.convertValue(optional, ProcessInstanceResponse.class); - return response.getProcessInstances().get(0).getState(); - } -} \ No newline at end of file diff --git a/backend/bankaccounts/src/main/java/org/egov/web/models/Pagination.java b/backend/bankaccounts/src/main/java/org/egov/web/models/Pagination.java index 06da1fa3b9..63c7d7e27d 100644 --- a/backend/bankaccounts/src/main/java/org/egov/web/models/Pagination.java +++ b/backend/bankaccounts/src/main/java/org/egov/web/models/Pagination.java @@ -23,8 +23,8 @@ public class Pagination { @JsonProperty("limit") - @DecimalMax("100") - private Double limit = 10d; +// @DecimalMax("100") + private Double limit = null; @JsonProperty("offSet") private Double offSet = 0d; diff --git a/backend/expense-calculator/pom.xml b/backend/expense-calculator/pom.xml index 3444e2ac30..1f1d99f08a 100644 --- a/backend/expense-calculator/pom.xml +++ b/backend/expense-calculator/pom.xml @@ -4,7 +4,7 @@ expense-calculator jar expense-calculator - 2.0.0 + 2.0.1 1.8 ${java.version} diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/config/ExpenseCalculatorConfiguration.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/config/ExpenseCalculatorConfiguration.java index 6fa0691b76..59c77c724a 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/config/ExpenseCalculatorConfiguration.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/config/ExpenseCalculatorConfiguration.java @@ -76,6 +76,12 @@ public class ExpenseCalculatorConfiguration { @Value("${egov.mdms.search.endpoint}") private String mdmsEndPoint; + @Value("${egov.mdms.v2.host}") + private String mdmsV2Host; + + @Value("${egov.mdms.v2.search.endpoint}") + private String mdmsV2EndPoint; + // MusterRoll @Value("${egov.musterroll.host}") private String musterRollHost; diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/ExpenseCalculatorService.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/ExpenseCalculatorService.java index a12a665bac..6fefe0f9f3 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/ExpenseCalculatorService.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/ExpenseCalculatorService.java @@ -21,27 +21,7 @@ import org.egov.digit.expense.calculator.util.MdmsUtils; import org.egov.digit.expense.calculator.util.ProjectUtil; import org.egov.digit.expense.calculator.validator.ExpenseCalculatorServiceValidator; -import org.egov.digit.expense.calculator.web.models.ApplicableCharge; -import org.egov.digit.expense.calculator.web.models.Bill; -import org.egov.digit.expense.calculator.web.models.BillMapper; -import org.egov.digit.expense.calculator.web.models.BillMetaRecords; -import org.egov.digit.expense.calculator.web.models.BillResponse; -import org.egov.digit.expense.calculator.web.models.BusinessService; -import org.egov.digit.expense.calculator.web.models.Calculation; -import org.egov.digit.expense.calculator.web.models.CalculationRequest; -import org.egov.digit.expense.calculator.web.models.CalculatorSearchCriteria; -import org.egov.digit.expense.calculator.web.models.CalculatorSearchRequest; -import org.egov.digit.expense.calculator.web.models.Contract; -import org.egov.digit.expense.calculator.web.models.Criteria; -import org.egov.digit.expense.calculator.web.models.HeadCode; -import org.egov.digit.expense.calculator.web.models.LabourCharge; -import org.egov.digit.expense.calculator.web.models.MusterRoll; -import org.egov.digit.expense.calculator.web.models.MusterRollRequest; -import org.egov.digit.expense.calculator.web.models.Payer; -import org.egov.digit.expense.calculator.web.models.PurchaseBill; -import org.egov.digit.expense.calculator.web.models.PurchaseBillRequest; -import org.egov.digit.expense.calculator.web.models.Workflow; -import org.egov.digit.expense.calculator.web.models.CalcEstimate; +import org.egov.digit.expense.calculator.web.models.*; import org.egov.tracer.model.CustomException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -100,11 +80,11 @@ public Calculation calculateEstimates(CalculationRequest calculationRequest) { Criteria criteria = calculationRequest.getCriteria(); if (criteria.getMusterRollId() != null && !criteria.getMusterRollId().isEmpty()) { - // Fetch wage seeker skills from MDMS - List labourCharges = fetchMDMSDataForLabourCharges(requestInfo, criteria.getTenantId()); // Fetch all the approved muster rolls for provided muster Ids List musterRolls = fetchApprovedMusterRolls(requestInfo, criteria, false); - return wageSeekerBillGeneratorService.calculateEstimates(requestInfo, criteria.getTenantId(), musterRolls, labourCharges); + // Fetch wage seeker skills from MDMS + List sorDetails = fetchMDMSDataForLabourCharges(requestInfo, criteria.getTenantId(), musterRolls); + return wageSeekerBillGeneratorService.calculateEstimates(requestInfo, criteria.getTenantId(), musterRolls, sorDetails); } else { List bills = fetchBills(requestInfo, criteria.getTenantId(), criteria.getContractId()); //TODO: Add check for empty bill list here and send back a response @@ -235,14 +215,15 @@ public List createWageOrSupervisionBills(CalculationRequest calculationReq private List createWageBill(RequestInfo requestInfo, Criteria criteria, Map metaInfo) { log.info("Create wage bill for musterRollIds :"+criteria.getMusterRollId() ); - // Fetch wage seeker skills from MDMS - List labourCharges = fetchMDMSDataForLabourCharges(requestInfo, criteria.getTenantId()); // Fetch musterRolls for given muster roll IDs List musterRolls = fetchApprovedMusterRolls(requestInfo,criteria,true); + // Fetch wage seeker skills from MDMS +// List labourCharges = fetchMDMSDataForLabourCharges(requestInfo, criteria.getTenantId(), musterRolls); + List sorDetails = fetchMDMSDataForLabourCharges(requestInfo, criteria.getTenantId(), musterRolls); // Contract project mapping Map contractProjectMapping = getContractProjectMapping(musterRolls); metaInfo.putAll(contractProjectMapping); - return wageSeekerBillGeneratorService.createWageSeekerBills(requestInfo,musterRolls,labourCharges,metaInfo); + return wageSeekerBillGeneratorService.createWageSeekerBills(requestInfo,musterRolls,sorDetails,metaInfo); } private List createSupervisionBill(RequestInfo requestInfo, Criteria criteria, Map metaInfo) { @@ -304,8 +285,8 @@ public void createAndPostWageSeekerBill(MusterRollRequest musterRollRequest){ Map context = new HashMap<>(); context.putAll(contractProjectMapping); // Fetch wage seeker skills from MDMS - List labourCharges = fetchMDMSDataForLabourCharges(requestInfo, musterRoll.getTenantId()); - List wageSeekerBills = wageSeekerBillGeneratorService.createWageSeekerBills(requestInfo,Collections.singletonList(musterRoll),labourCharges,context); + List sorDetails = fetchMDMSDataForLabourCharges(requestInfo, musterRoll.getTenantId(), Collections.singletonList(musterRoll)); + List wageSeekerBills = wageSeekerBillGeneratorService.createWageSeekerBills(requestInfo,Collections.singletonList(musterRoll),sorDetails,context); BillResponse billResponse = null; Workflow workflow = Workflow.builder() .action(WF_SUBMIT_ACTION_CONSTANT) @@ -343,17 +324,51 @@ private BillResponse postUpdateBill(RequestInfo requestInfo, Bill bill, Workflow return billUtils.postUpdateBill(requestInfo, bill, workflow); } - private List fetchMDMSDataForLabourCharges(RequestInfo requestInfo, String tenantId){ + private List fetchMDMSDataForLabourCharges(RequestInfo requestInfo, String tenantId, List musterRolls){ log.info("Fetch wage seeker skills MDMS"); - Object mdmsData = mdmsUtils.fetchMDMSDataForLabourCharges(requestInfo, tenantId); - List labourChargesJson = commonUtil.readJSONPathValue(mdmsData, JSON_PATH_FOR_LABOUR_CHARGES); - List labourCharges = new ArrayList<>(); - for(Object obj : labourChargesJson){ - LabourCharge labourCharge = mapper.convertValue(obj, LabourCharge.class); - labourCharges.add(labourCharge); +// Object mdmsData = mdmsUtils.fetchMDMSDataForLabourCharges(requestInfo, tenantId); + List sorList = getLabourSorFromMusterRolls(musterRolls); + if (sorList.isEmpty()) { + throw new CustomException("SOR_NOT_FOUND", "No sor found in additional details of muster roll"); + } + Object sorFromMDMSV2 = mdmsUtils.getLabourSorFromMDMSV2(requestInfo, tenantId, sorList, false); + List sorListJson = commonUtil.readJSONPathValue(sorFromMDMSV2, JSON_PATH_FOR_SOR); + List sorDetails = new ArrayList<>(); + for(Object obj : sorListJson){ + SorDetail sorDetail = mapper.convertValue(obj, SorDetail.class); + sorDetails.add(sorDetail); + } + List sorIds = sorDetails.stream().map(SorDetail::getId).collect(Collectors.toList()); + if (sorIds.isEmpty()) { + throw new CustomException("NO_SOR_FOUND", "No sor found in mdms"); } + Object ratesFromMDMV2 = mdmsUtils.getLabourSorFromMDMSV2(requestInfo, tenantId, sorIds, true); + List rateListJson = commonUtil.readJSONPathValue(ratesFromMDMV2, JSON_PATH_FOR_RATES); + List rateDetails = new ArrayList<>(); + for(Object obj : rateListJson){ + RateDetail rateDetail = mapper.convertValue(obj, RateDetail.class); + rateDetails.add(rateDetail); + } + for (RateDetail rateDetail : rateDetails) { + for (SorDetail sorDetail : sorDetails) { + if (rateDetail.getSorId().equalsIgnoreCase(sorDetail.getId())) { + if (sorDetail.getRateDetails() == null) { + sorDetail.setRateDetails(new ArrayList<>()); + sorDetail.getRateDetails().add(rateDetail); + } else { + sorDetail.getRateDetails().add(rateDetail); + } + } + } + } +// List labourChargesJson = commonUtil.readJSONPathValue(mdmsData, JSON_PATH_FOR_LABOUR_CHARGES); +// List labourCharges = new ArrayList<>(); +// for(Object obj : labourChargesJson){ +// LabourCharge labourCharge = mapper.convertValue(obj, LabourCharge.class); +// labourCharges.add(labourCharge); +// } log.info("Wage seeker skills fetched from MDMS"); - return labourCharges; + return sorDetails; } public List fetchApprovedMusterRolls(RequestInfo requestInfo, Criteria criteria, boolean onlyApproved) { @@ -444,5 +459,20 @@ private List fetchMDMSDataForBusinessService(RequestInfo reques log.info("Business Service fetched from MDMS"); return businessServices; } + + private List getLabourSorFromMusterRolls (List musterRolls) { + List individualEntries = musterRolls.stream().map(musterRoll -> musterRoll.getIndividualEntries()).flatMap(List::stream).collect(Collectors.toList()); +// List sorList = individualEntries.stream().filter(individualEntries -> individualEntries.getAdditionalDetails() != null && individualEntries.getAdditionalDetails().get("sor") != null).map(individualEntries -> (String) individualEntries.getAdditionalDetails().get("sor")).collect(Collectors.toList()); + return individualEntries.stream() // Stream + .filter(entry -> { + Map additionalDetails = (Map) entry.getAdditionalDetails(); // Cast to Map + return additionalDetails != null && additionalDetails.get("skillCode") != null; + }) + .map(entry -> { + Map additionalDetails = (Map) entry.getAdditionalDetails(); // Cast to Map + return (String) additionalDetails.get("skillCode"); + }) + .collect(Collectors.toList()); + } } diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/WageSeekerBillGeneratorService.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/WageSeekerBillGeneratorService.java index f4386f9d6e..0af5af1d2d 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/WageSeekerBillGeneratorService.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/service/WageSeekerBillGeneratorService.java @@ -55,18 +55,18 @@ public WageSeekerBillGeneratorService(ObjectMapper mapper, ExpenseCalculatorUtil } public Calculation calculateEstimates(RequestInfo requestInfo, String tenantId, List musterRolls, - List labourCharges) { + List sorDetails) { // Calculate estimate for each muster roll - List calcEstimates = createEstimatesForMusterRolls(requestInfo, musterRolls, labourCharges); + List calcEstimates = createEstimatesForMusterRolls(requestInfo, musterRolls, sorDetails); // Create Calculation log.info("Make calculation and return"); return makeCalculation(calcEstimates, tenantId); } public List createWageSeekerBills(RequestInfo requestInfo, List musterRolls, - List labourCharges, Map metaInfo) { + List sorDetails, Map metaInfo) { // Create bills for muster rolls - return createBillForMusterRolls(requestInfo, musterRolls, labourCharges, metaInfo); + return createBillForMusterRolls(requestInfo, musterRolls, sorDetails, metaInfo); } private Map> getMasterDataForCalculator(RequestInfo reqInfo, String tenantId) { @@ -82,7 +82,7 @@ private Map> getMasterDataForCalculator(RequestIn } private List createBillForMusterRolls(RequestInfo requestInfo, List musterRolls, - List labourCharges, Map metaInfo) { + List sorDetails, Map metaInfo) { List bills = new ArrayList<>(); List musterRollNumbers = new ArrayList<>(); @@ -126,8 +126,9 @@ private List createBillForMusterRolls(RequestInfo requestInfo, List createBillForMusterRolls(RequestInfo requestInfo, List calcEstimates, String ten } private List createEstimatesForMusterRolls(RequestInfo requestInfo, List musterRolls, - List labourCharges) { + List sorDetails) { List calcEstimates = new ArrayList<>(); for (MusterRoll musterRoll : musterRolls) { String musterRollNumber = musterRoll.getMusterRollNumber(); @@ -246,7 +247,7 @@ private List createEstimatesForMusterRolls(RequestInfo requestInfo for (IndividualEntry individualEntry : individualEntries) { String individualId = individualEntry.getIndividualId(); // Calculate net amount to pay to wage seeker - Double skillAmount = getWageSeekerSkillAmount(individualEntry, labourCharges,musterRollCreatedTime); + Double skillAmount = getWageSeekerSkillAmountFromV2(individualEntry, sorDetails,musterRollCreatedTime); //Round off BigDecimal actualAmountToPay = calculateAmount(individualEntry, BigDecimal.valueOf(skillAmount)).setScale(0, RoundingMode.HALF_UP); @@ -378,16 +379,27 @@ private BigDecimal calculateAmount(IndividualEntry individualEntry, BigDecimal s return totalAttendance.multiply(skillAmount); } - private Double getWageSeekerSkillAmount(IndividualEntry individualEntry, List labourCharges, Long musterRollCreatedTime) { + private Double getWageSeekerSkillAmountFromV2(IndividualEntry individualEntry, List sorDetails, Long musterRollCreatedTime) { String skill = getWageSeekerSkill(individualEntry); boolean isSkillCodePresent = false; - for (LabourCharge labourCharge : labourCharges) { - if (labourCharge.getCode().equalsIgnoreCase(skill)) { + for (SorDetail sorDetail : sorDetails) { + if (sorDetail.getId().equalsIgnoreCase(skill)) { isSkillCodePresent = true; - if((labourCharge.getEffectiveTo() != null && labourCharge.getEffectiveFrom().longValue() <= musterRollCreatedTime && labourCharge.getEffectiveTo().longValue() >= musterRollCreatedTime) || - (labourCharge.getEffectiveTo() == null && labourCharge.getEffectiveFrom().longValue() <= musterRollCreatedTime && labourCharge.getActive())) { - return labourCharge.getAmount(); + for (RateDetail rateDetail : sorDetail.getRateDetails()) { + long validFrom = Long.parseLong(rateDetail.getValidFrom()); + long validTo; + try { + validTo = rateDetail.getValidTo() == null ? 0 : Long.parseLong(rateDetail.getValidTo()); + } catch (NumberFormatException e) { + validTo = 0; + } + + if((validTo != 0 && validFrom <= musterRollCreatedTime && validTo >= musterRollCreatedTime) || + (validTo == 0 && validFrom <= musterRollCreatedTime)) { + return rateDetail.getRate(); + } } + } } @@ -399,13 +411,39 @@ private Double getWageSeekerSkillAmount(IndividualEntry individualEntry, List labourCharges) { +// private Double getWageSeekerSkillAmount(IndividualEntry individualEntry, List sorDetails, Long musterRollCreatedTime) { +// String skill = getWageSeekerSkill(individualEntry); +// boolean isSkillCodePresent = false; +// for (SorDetail sorDetail : sorDetails) { +// if (sorDetail.getId().equalsIgnoreCase(skill)) { +// isSkillCodePresent = true; +// for (RateDetail rateDetail : sorDetail.getRateDetails()) { +// try { +// long validTo = Long.parseLong(String.valueOf(rateDetail.getValidTo())); +// } +// if((rateDetail.getValidTo() != null && rateDetail.getValidFrom().longValue() <= musterRollCreatedTime && rateDetail.getValidTo().longValue() >= musterRollCreatedTime) || +// (rateDetail.getValidTo() == null && rateDetail.getValidFrom().longValue() <= musterRollCreatedTime)) { +// return rateDetail.getRate(); +// } +// } +// } +// } +// +// if(!isSkillCodePresent){ +// log.error("SKILL_CODE_MISSING_IN_MDMS", "Skill code " + skill + " is missing in MDMS"); +// throw new CustomException("SKILL_CODE_MISSING_IN_MDMS", "Skill code " + skill + " is missing in MDMS"); +// } +// log.error("SKILL_CODE_IS_NOT_MATCHING_WITH_DATE_RANGE", "Skill code " + skill + " is not matching with date range"); +// throw new CustomException("SKILL_CODE_IS_NOT_MATCHING_WITH_DATE_RANGE", "Skill code " + skill + " is not matching with date range"); +// } + + private String getWageSeekerSkillCodeId(IndividualEntry individualEntry, List sorDetails) { String skill = getWageSeekerSkill(individualEntry); String wageLabourChargeUnit = configs.getWageLabourChargeUnit(); - for (LabourCharge labourCharge : labourCharges) { - if (labourCharge.getCode().equalsIgnoreCase(skill) - && wageLabourChargeUnit.equalsIgnoreCase(labourCharge.getUnit())) { - return labourCharge.getId(); + for (SorDetail sorDetail : sorDetails) { + if (sorDetail.getId().equalsIgnoreCase(skill) + && wageLabourChargeUnit.equalsIgnoreCase(sorDetail.getUom())) { + return sorDetail.getId(); } } diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/BillUtils.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/BillUtils.java index 35da4b0401..b485c0e33e 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/BillUtils.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/BillUtils.java @@ -41,11 +41,11 @@ public BillResponse postUpdateBill(RequestInfo requestInfo, Bill bill, Workflow private BillResponse postBill(RequestInfo requestInfo, Bill bill, Workflow workflow, StringBuilder url) { // Update workflow object because in expense service it's using core service workflow - digit.models.coremodels.Workflow expenseWorkflow1 = digit.models.coremodels.Workflow.builder() + Workflow expenseWorkflow1 = Workflow.builder() .action(workflow.getAction()) - .assignes(workflow.getAssignees()) - .verificationDocuments(workflow.getDocuments()) - .comments(workflow.getComment()) + .assignees(workflow.getAssignees()) + .documents(workflow.getDocuments()) + .comment(workflow.getComment()) .build(); BillCalculatorRequestInfoWrapper requestInfoWrapper = BillCalculatorRequestInfoWrapper.builder() .requestInfo(requestInfo) diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/ExpenseCalculatorServiceConstants.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/ExpenseCalculatorServiceConstants.java index c5ee0ba5f4..8f8a38a893 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/ExpenseCalculatorServiceConstants.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/ExpenseCalculatorServiceConstants.java @@ -57,4 +57,14 @@ public class ExpenseCalculatorServiceConstants { public static final String DOCUMENTS_CONSTANT = "documents"; public static final String LINEITEM_STATUS_ACTIVE = "ACTIVE"; public static final String LINEITEM_STATUS_INACTIVE = "INACTIVE"; + public static final String FILTER_START = "[?("; + public static final String FILTER_END = "')]"; + public static final String ID_SEARCH_CONSTANT = "@.id=='"; + public static final String ID_SEARCH_CONSTANT_RATE = "@.sorId=='"; + public static final String FILTER_OR_CONSTANT = "'||"; + public static final String WORKS_SOR_CONSTANT = "WORKS-SOR"; + public static final String SOR_CONSTANT = "SOR"; + public static final String RATES_CONSTANT = "Rates"; + public static final String JSON_PATH_FOR_SOR = MDMS_RESP_CONSTANT+WORKS_SOR_CONSTANT + "." + SOR_CONSTANT; + public static final String JSON_PATH_FOR_RATES = MDMS_RESP_CONSTANT+WORKS_SOR_CONSTANT + "." + RATES_CONSTANT; } \ No newline at end of file diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/MdmsUtils.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/MdmsUtils.java index 7ce9276e18..3437402d02 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/MdmsUtils.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/util/MdmsUtils.java @@ -17,11 +17,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.egov.digit.expense.calculator.util.ExpenseCalculatorServiceConstants.*; @@ -221,4 +217,41 @@ private MdmsCriteriaReq getMDMSRequestForLabourChanges(RequestInfo requestInfo, return prepareMDMSCriteria(requestInfo,moduleDetails,tenantId); } + public Object getLabourSorFromMDMSV2(RequestInfo requestInfo, String tenantId, List sorList, Boolean isRate) { + String filter = getFilterFromSorList(sorList, isRate); + + MdmsCriteria mdmsCriteria = getMdmsCriteria(requestInfo, isRate ? RATES_CONSTANT : SOR_CONSTANT, tenantId, filter); + + return serviceRequestRepository.fetchResult(getMDMSV2SearchUrl(), MdmsCriteriaReq.builder().requestInfo(requestInfo).mdmsCriteria(mdmsCriteria).build()); + } + + + private MdmsCriteria getMdmsCriteria (RequestInfo requestInfo, String masterName, String tenantId, String filter) { + return MdmsCriteria.builder().tenantId(tenantId) + .moduleDetails(Collections.singletonList(ModuleDetail.builder() + .moduleName(WORKS_SOR_CONSTANT) + .masterDetails(Collections.singletonList(MasterDetail.builder() + .name(masterName) + .filter(filter) + .build())) + .build())) + .build(); + + } + + String getFilterFromSorList(List sorList, Boolean isRate) { + String idConstant = isRate ? ID_SEARCH_CONSTANT_RATE : ID_SEARCH_CONSTANT; + StringBuilder filterString = new StringBuilder(FILTER_START).append(idConstant).append(sorList.get(0)); + for (int i = 1; i < sorList.size(); i++) { + filterString.append(FILTER_OR_CONSTANT); + filterString.append(idConstant).append(sorList.get(i)); + } + filterString.append(FILTER_END); + return filterString.toString(); + } + + public StringBuilder getMDMSV2SearchUrl() { + return new StringBuilder().append(config.getMdmsV2Host()).append(config.getMdmsV2EndPoint()); + } + } diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/BillCalculatorRequestInfoWrapper.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/BillCalculatorRequestInfoWrapper.java index 76557e4901..4b8ec8c0fa 100644 --- a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/BillCalculatorRequestInfoWrapper.java +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/BillCalculatorRequestInfoWrapper.java @@ -1,7 +1,6 @@ package org.egov.digit.expense.calculator.web.models; import com.fasterxml.jackson.annotation.JsonProperty; -import digit.models.coremodels.Workflow; import lombok.Builder; import lombok.ToString; import org.egov.common.contract.request.RequestInfo; diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/RateDetail.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/RateDetail.java new file mode 100644 index 0000000000..2c27ac1df1 --- /dev/null +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/RateDetail.java @@ -0,0 +1,29 @@ +package org.egov.digit.expense.calculator.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RateDetail { + + @JsonProperty("rate") + private Double rate = null; + + @JsonProperty("sorId") + private String sorId = null; + + @JsonProperty("validFrom") + private String validFrom = null; + + @JsonProperty("validTo") + private String validTo = null; + +} diff --git a/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/SorDetail.java b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/SorDetail.java new file mode 100644 index 0000000000..8e111cc3c1 --- /dev/null +++ b/backend/expense-calculator/src/main/java/org/egov/digit/expense/calculator/web/models/SorDetail.java @@ -0,0 +1,41 @@ +package org.egov.digit.expense.calculator.web.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SorDetail { + + @JsonProperty("id") + private String id; + + @JsonProperty("uom") + private String uom; + + @JsonProperty("sorType") + private String sorType; + + @JsonProperty("quantity") + private Double quantity; + + @JsonProperty("sorSubType") + private String sorSubType; + + @JsonProperty("sorVariant") + private String sorVariant; + + @JsonProperty("description") + private String description; + + @JsonProperty("rateDetails") + private List rateDetails; + +} diff --git a/backend/expense-calculator/src/main/resources/application.properties b/backend/expense-calculator/src/main/resources/application.properties index 92b52712c2..ded9682d62 100644 --- a/backend/expense-calculator/src/main/resources/application.properties +++ b/backend/expense-calculator/src/main/resources/application.properties @@ -1,140 +1,145 @@ -server.contextPath=/expense-calculator -server.servlet.contextPath=/expense-calculator -server.port=8087 -app.timezone=UTC - -#DATABASE CONFIGURATION -spring.datasource.driver-class-name=org.postgresql.Driver -spring.datasource.url=jdbc:postgresql://localhost:5432/digit-works -spring.datasource.username=postgres -spring.datasource.password=1234 - -#FLYWAY CONFIGURATION -spring.flyway.url=jdbc:postgresql://localhost:5432/digit-works -spring.flyway.user=postgres -spring.flyway.password=1234 -spring.flyway.table=expense_calculator_schema -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.properties.spring.deserializer.value.delegate.class=org.springframework.kafka.support.serializer.JsonDeserializer -spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer -spring.kafka.consumer.group-id=expense-calculator -spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer -spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer -spring.kafka.listener.missing-topics-fatal=false -spring.kafka.consumer.properties.spring.json.use.type.headers=false - -# KAFKA CONSUMER CONFIGURATIONS -kafka.consumer.config.auto_commit=true -kafka.consumer.config.auto_commit_interval=100 -kafka.consumer.config.session_timeout=15000 -kafka.consumer.config.auto_offset_reset=earliest -# KAFKA PRODUCER CONFIGURATIONS -kafka.producer.config.retries_config=0 -kafka.producer.config.batch_size_config=16384 -kafka.producer.config.linger_ms_config=1 -kafka.producer.config.buffer_memory_config=33554432 - -#-------------- Topic Config------------------# -expense.calculator.consume.topic=calculate-musterroll -expense.calculator.create.topic=save-calculator -expense.calculator.error.topic=calculate-error -expense.calculator.create.bill.topic=calculate-billmeta - - -#Localization config -egov.localization.host=https://unified-dev.digit.org -egov.localization.workDir.path=/localization/messages/v1 -egov.localization.context.path=/localization/messages/v1 -egov.localization.search.endpoint=/_search -egov.localization.statelevel=true - -#mdms urls -egov.mdms.host=https://unified-dev.digit.org -#egov.mdms.host=http://localhost:8083 -egov.mdms.search.endpoint=/egov-mdms-service/v1/_search - -#hrms urls -egov.hrms.host=https://unified-dev.digit.org -egov.hrms.search.endpoint=/egov-hrms/employees/_search - -#musterroll urls -egov.musterroll.host=https://unified-dev.digit.org -#egov.musterroll.host=http://localhost:8084 -egov.musterroll.search.endpoint=/muster-roll/v1/_search - -#Contract service config -egov.contract.service.host=https://unified-dev.digit.org/ -#egov.contract.service.host=http://localhost:8085/ -egov.contract.service.search.endpoint=/contract/v1/_search - -#Organisation Service -egov.organisation.host=https://unified-dev.digit.org -egov.organisation.endpoint=/org-services/organisation/v1/_search - -#bill urls -egov.bill.host=https://unified-dev.digit.org -#egov.bill.host=http://localhost:8086 -egov.bill.create.endpoint=/expense/bill/v1/_create -egov.bill.update.endpoint=/expense/bill/v1/_update -egov.expense.bill.service.search.endpoint=/expense/bill/v1/_search - -#User config -egov.user.host=https://unified-dev.digit.org -egov.user.context.path=/user/users -egov.user.create.path=/_createnovalidate -egov.user.search.path=/user/_search -egov.user.update.path=/_updatenovalidate - -#Idgen Config -egov.idgen.host=https://unified-dev.digit.org/ -#egov.idgen.host=http://localhost:8285/ -egov.idgen.path=egov-idgen/id/_generate -egov.idgen.supervision.reference.number=supervision.reference.number - -#Workflow config -is.workflow.enabled=true -egov.workflow.host=http://localhost:8090 -egov.workflow.transition.path=/egov-workflow-v2/egov-wf/process/_transition -egov.workflow.businessservice.search.path=/egov-workflow-v2/egov-wf/businessservice/_search -egov.workflow.processinstance.search.path=/egov-workflow-v2/egov-wf/process/_search - -#url shortner -egov.url.shortner.host=https://unified-dev.digit.org -egov.url.shortner.endpoint=/egov-url-shortening/shortener - -egov.sms.notification.topic=egov.core.notification.sms -kafka.topics.receipt.create=dss-collection - -#--------------project service config----------------# -project.service.host=https://unified-dev.digit.org/ -project.search.path=project/v1/_search - -# The value of the following field should be changed to service specific name -kafka.topics.consumer=service-consumer-topic - -# Expense Service specific configs -egov.works.expense.wage.head.code=WEG -egov.works.expense.payer.type=ULB -egov.works.expense.wage.labour.charge.unit=day -egov.works.expense.wage.payee.type=INDIVIDUAL -egov.works.expense.wage.business.service=EXPENSE.WAGES -egov.works.expense.purchase.business.service=EXPENSE.PURCHASE -egov.works.expense.supervision.business.service=EXPENSE.SUPERVISION - -works.wages.master.category=works.wages - -#IDGen key names defined in MDMS -egov.works.expense.purchasebill.referenceId.format=purchase.reference.number -egov.works.expense.superbill.referenceId.format=supervision.reference.number -egov.works.expense.wagebill.referenceId.format=wage.reference.number - -# search configs -expense.billing.default.limit=100 -expense.billing.default.offset=0 +server.contextPath=/expense-calculator +server.servlet.contextPath=/expense-calculator +server.port=8087 +app.timezone=UTC + +#DATABASE CONFIGURATION +spring.datasource.driver-class-name=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/digit-works +spring.datasource.username=postgres +spring.datasource.password=1234 + +#FLYWAY CONFIGURATION +spring.flyway.url=jdbc:postgresql://localhost:5432/digit-works +spring.flyway.user=postgres +spring.flyway.password=1234 +spring.flyway.table=expense_calculator_schema +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.properties.spring.deserializer.value.delegate.class=org.springframework.kafka.support.serializer.JsonDeserializer +spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer +spring.kafka.consumer.group-id=expense-calculator +spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer +spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.listener.missing-topics-fatal=false +spring.kafka.consumer.properties.spring.json.use.type.headers=false + +# KAFKA CONSUMER CONFIGURATIONS +kafka.consumer.config.auto_commit=true +kafka.consumer.config.auto_commit_interval=100 +kafka.consumer.config.session_timeout=15000 +kafka.consumer.config.auto_offset_reset=earliest +# KAFKA PRODUCER CONFIGURATIONS +kafka.producer.config.retries_config=0 +kafka.producer.config.batch_size_config=16384 +kafka.producer.config.linger_ms_config=1 +kafka.producer.config.buffer_memory_config=33554432 + +#-------------- Topic Config------------------# +expense.calculator.consume.topic=calculate-musterroll +expense.calculator.create.topic=save-calculator +expense.calculator.error.topic=calculate-error +expense.calculator.create.bill.topic=calculate-billmeta + + +#Localization config +egov.localization.host=https://unified-dev.digit.org +egov.localization.workDir.path=/localization/messages/v1 +egov.localization.context.path=/localization/messages/v1 +egov.localization.search.endpoint=/_search +egov.localization.statelevel=true + +#mdms urls +egov.mdms.host=https://unified-dev.digit.org +#egov.mdms.host=http://localhost:8083 +egov.mdms.search.endpoint=/egov-mdms-service/v1/_search + +#mdmsV2 +egov.mdms.v2.host=https://unified-dev.digit.org +egov.mdms.v2.search.endpoint=/mdms-v2/v1/_search + + +#hrms urls +egov.hrms.host=https://unified-dev.digit.org +egov.hrms.search.endpoint=/egov-hrms/employees/_search + +#musterroll urls +egov.musterroll.host=https://unified-dev.digit.org +#egov.musterroll.host=http://localhost:8084 +egov.musterroll.search.endpoint=/muster-roll/v1/_search + +#Contract service config +egov.contract.service.host=https://unified-dev.digit.org/ +#egov.contract.service.host=http://localhost:8085/ +egov.contract.service.search.endpoint=/contract/v1/_search + +#Organisation Service +egov.organisation.host=https://unified-dev.digit.org +egov.organisation.endpoint=/org-services/organisation/v1/_search + +#bill urls +egov.bill.host=https://unified-dev.digit.org +#egov.bill.host=http://localhost:8086 +egov.bill.create.endpoint=/expense/bill/v1/_create +egov.bill.update.endpoint=/expense/bill/v1/_update +egov.expense.bill.service.search.endpoint=/expense/bill/v1/_search + +#User config +egov.user.host=https://unified-dev.digit.org +egov.user.context.path=/user/users +egov.user.create.path=/_createnovalidate +egov.user.search.path=/user/_search +egov.user.update.path=/_updatenovalidate + +#Idgen Config +egov.idgen.host=https://unified-dev.digit.org/ +#egov.idgen.host=http://localhost:8285/ +egov.idgen.path=egov-idgen/id/_generate +egov.idgen.supervision.reference.number=supervision.reference.number + +#Workflow config +is.workflow.enabled=true +egov.workflow.host=http://localhost:8090 +egov.workflow.transition.path=/egov-workflow-v2/egov-wf/process/_transition +egov.workflow.businessservice.search.path=/egov-workflow-v2/egov-wf/businessservice/_search +egov.workflow.processinstance.search.path=/egov-workflow-v2/egov-wf/process/_search + +#url shortner +egov.url.shortner.host=https://unified-dev.digit.org +egov.url.shortner.endpoint=/egov-url-shortening/shortener + +egov.sms.notification.topic=egov.core.notification.sms +kafka.topics.receipt.create=dss-collection + +#--------------project service config----------------# +project.service.host=https://unified-dev.digit.org/ +project.search.path=project/v1/_search + +# The value of the following field should be changed to service specific name +kafka.topics.consumer=service-consumer-topic + +# Expense Service specific configs +egov.works.expense.wage.head.code=WEG +egov.works.expense.payer.type=ULB +egov.works.expense.wage.labour.charge.unit=day +egov.works.expense.wage.payee.type=INDIVIDUAL +egov.works.expense.wage.business.service=EXPENSE.WAGES +egov.works.expense.purchase.business.service=EXPENSE.PURCHASE +egov.works.expense.supervision.business.service=EXPENSE.SUPERVISION + +works.wages.master.category=works.wages + +#IDGen key names defined in MDMS +egov.works.expense.purchasebill.referenceId.format=purchase.reference.number +egov.works.expense.superbill.referenceId.format=supervision.reference.number +egov.works.expense.wagebill.referenceId.format=wage.reference.number + +# search configs +expense.billing.default.limit=100 +expense.billing.default.offset=0 expense.billing.search.max.limit=200 \ No newline at end of file diff --git a/backend/muster-roll/pom.xml b/backend/muster-roll/pom.xml index 211bbc09af..a0932ec190 100644 --- a/backend/muster-roll/pom.xml +++ b/backend/muster-roll/pom.xml @@ -5,7 +5,7 @@ muster-roll jar muster-roll - 1.0.0 + 1.0.1 1.8 ${java.version} diff --git a/backend/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java b/backend/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java index 9e9c0e859f..3853ed2d2d 100644 --- a/backend/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java +++ b/backend/muster-roll/src/main/java/org/egov/config/MusterRollServiceConfiguration.java @@ -26,6 +26,10 @@ public class MusterRollServiceConfiguration { private String mdmsHost; @Value("${egov.mdms.search.endpoint}") private String mdmsEndPoint; + @Value("${egov.mdms.v2.host}") + private String mdmsV2Host; + @Value("${egov.mdms.v2.search.endpoint}") + private String mdmsV2EndPoint; //Idgen Config @Value("${egov.idgen.host}") diff --git a/backend/muster-roll/src/main/java/org/egov/service/CalculationService.java b/backend/muster-roll/src/main/java/org/egov/service/CalculationService.java index 4498db961d..120ab78133 100644 --- a/backend/muster-roll/src/main/java/org/egov/service/CalculationService.java +++ b/backend/muster-roll/src/main/java/org/egov/service/CalculationService.java @@ -14,7 +14,6 @@ import org.egov.util.MdmsUtil; import org.egov.util.MusterRollServiceUtil; import org.egov.web.models.*; -import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -74,6 +73,7 @@ public void createAttendance(MusterRollRequest musterRollRequest, boolean isCrea MusterRoll musterRoll = musterRollRequest.getMusterRoll(); String tenantId = musterRoll.getTenantId(); Object mdmsData = mdmsUtils.mDMSCallMuster(musterRollRequest, tenantId); + Object mdmsV2Data = mdmsUtils.mDMSV2CallMuster(musterRollRequest, tenantId); //fetch the log events for all individuals in a muster roll @@ -204,7 +204,7 @@ public void createAttendance(MusterRollRequest musterRollRequest, boolean isCrea .findFirst().orElse(null); if (individual != null /* && bankAccount != null */) { - setAdditionalDetails(entry, individualEntriesFromRequest, mdmsData, individual, + setAdditionalDetails(entry, individualEntriesFromRequest, mdmsV2Data, individual, bankAccount, isCreate); } else { log.info( diff --git a/backend/muster-roll/src/main/java/org/egov/service/MusterRollService.java b/backend/muster-roll/src/main/java/org/egov/service/MusterRollService.java index 66ef78fb50..34a2b91d11 100644 --- a/backend/muster-roll/src/main/java/org/egov/service/MusterRollService.java +++ b/backend/muster-roll/src/main/java/org/egov/service/MusterRollService.java @@ -195,6 +195,8 @@ public MusterRollRequest updateMusterRoll(MusterRollRequest musterRollRequest) { //fetch MDMS data for muster - skill level String tenantId = existingMusterRoll.getTenantId(); Object mdmsData = mdmsUtils.mDMSCallMuster(musterRollRequest, tenantId); + Object mdmsV2Data = mdmsUtils.mDMSV2CallMuster(musterRollRequest, tenantId); + //fetch the update additionalDetails from the request and persist it for verification if (!isComputeAttendance) { @@ -202,7 +204,7 @@ public MusterRollRequest updateMusterRoll(MusterRollRequest musterRollRequest) { existingMusterRoll.setAdditionalDetails(additionalDetails); } - enrichmentService.enrichMusterRollOnUpdate(musterRollRequest,existingMusterRoll,mdmsData); + enrichmentService.enrichMusterRollOnUpdate(musterRollRequest,existingMusterRoll,mdmsV2Data); if (isComputeAttendance) { RequestInfo requestInfo = musterRollRequest.getRequestInfo(); musterRollValidator.isValidUser(existingMusterRoll, requestInfo); diff --git a/backend/muster-roll/src/main/java/org/egov/util/MdmsUtil.java b/backend/muster-roll/src/main/java/org/egov/util/MdmsUtil.java index 7be89ba955..6c1fba34b8 100644 --- a/backend/muster-roll/src/main/java/org/egov/util/MdmsUtil.java +++ b/backend/muster-roll/src/main/java/org/egov/util/MdmsUtil.java @@ -58,6 +58,24 @@ public Object mDMSCallMuster(MusterRollRequest request, String tenantId) { return serviceRequestRepository.fetchResult(getMdmsSearchUrl(), mdmsCriteriaReq); } + public Object mDMSV2CallMuster(MusterRollRequest request, String tenantId) { + RequestInfo requestInfo = request.getRequestInfo(); + MdmsCriteriaReq mdmsCriteriaReq = getMDMSRequestMusterV2(requestInfo, tenantId); + return serviceRequestRepository.fetchResult(getMdmsV2SearchUrl(), mdmsCriteriaReq); + } + + public MdmsCriteriaReq getMDMSRequestMusterV2(RequestInfo requestInfo, String tenantId) { + ModuleDetail musterRollModuleDetail = getMusterRollModuleRequestDataV2(); + + List moduleDetails = new LinkedList<>(); + moduleDetails.add(musterRollModuleDetail); + + MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId) + .build(); + return MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria) + .requestInfo(requestInfo).build(); + } + /** * Returns mdms search criteria based on the tenantId * @@ -128,6 +146,15 @@ private ModuleDetail getMusterRollModuleRequestData() { .moduleName(MDMS_COMMON_MASTERS_MODULE_NAME).build(); } + private ModuleDetail getMusterRollModuleRequestDataV2() { + + List musterRollMasterDetails = new ArrayList<>(); + MasterDetail musterWageSeekerSkillMasterDetails = MasterDetail.builder().name("SOR").filter("[?(@.sorType=='L')]").build(); + musterRollMasterDetails.add(musterWageSeekerSkillMasterDetails); + return ModuleDetail.builder().masterDetails(musterRollMasterDetails) + .moduleName("WORKS-SOR").build(); + } + /** * Returns the url for mdms search endpoint * @@ -136,5 +163,8 @@ private ModuleDetail getMusterRollModuleRequestData() { public StringBuilder getMdmsSearchUrl() { return new StringBuilder().append(config.getMdmsHost()).append(config.getMdmsEndPoint()); } + public StringBuilder getMdmsV2SearchUrl() { + return new StringBuilder().append(config.getMdmsV2Host()).append(config.getMdmsV2EndPoint()); + } } \ No newline at end of file diff --git a/backend/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java b/backend/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java index 60380a3637..9776f322f5 100644 --- a/backend/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java +++ b/backend/muster-roll/src/main/java/org/egov/util/MusterRollServiceUtil.java @@ -73,8 +73,8 @@ public AuditDetails getAuditDetails(String by, MusterRoll musterRoll, Boolean is */ public void populateAdditionalDetails(Object mdmsData, IndividualEntry individualEntry, String skillCode, Individual matchedIndividual, BankAccount bankAccount, boolean isCreate) { - final String jsonPathForWorksMuster = "$.MdmsRes." + MDMS_COMMON_MASTERS_MODULE_NAME + "." - + MASTER_WAGER_SEEKER_SKILLS + ".*"; + final String jsonPathForWorksMuster = "$.MdmsRes." + "WORKS-SOR" + "." + + "SOR" + ".*"; List> musterRes = null; try { @@ -89,7 +89,7 @@ public void populateAdditionalDetails(Object mdmsData, IndividualEntry individua String skillValue = ""; if (skillCode != null && !CollectionUtils.isEmpty(musterRes)) { for (LinkedHashMap codeValueMap : musterRes) { - if (codeValueMap.get("code").equalsIgnoreCase(skillCode)) { + if (codeValueMap.get("id").equalsIgnoreCase(skillCode)) { skillValue = codeValueMap.get("name"); break; } @@ -146,15 +146,15 @@ private void populateSkillsInEstimateDetails(boolean isCreate,Individual matched if (!isCreate && !CollectionUtils.isEmpty(matchedIndividual.getSkills())) { List skillList = new ArrayList<>(); for (Skill skill : matchedIndividual.getSkills()) { - skillList.add(skill.getLevel() + "." + skill.getType()); + skillList.add(skill.getLevel()); } additionalDetails.put(SKILL_CODE, skillList); } } public void updateAdditionalDetails(Object mdmsData, IndividualEntry individualEntry, String skillCode) { - final String jsonPathForWorksMuster = "$.MdmsRes." + MDMS_COMMON_MASTERS_MODULE_NAME + "." - + MASTER_WAGER_SEEKER_SKILLS + ".*"; + final String jsonPathForWorksMuster = "$.MdmsRes." + "WORKS-SOR" + "." + + "SOR" + ".*"; List> musterRes = null; try { @@ -169,7 +169,7 @@ public void updateAdditionalDetails(Object mdmsData, IndividualEntry individualE String skillValue = ""; if (skillCode != null && !CollectionUtils.isEmpty(musterRes)) { for (LinkedHashMap codeValueMap : musterRes) { - if (codeValueMap.get("code").equalsIgnoreCase(skillCode)) { + if (codeValueMap.get("id").equalsIgnoreCase(skillCode)) { skillValue = codeValueMap.get("name"); break; } diff --git a/backend/muster-roll/src/main/resources/application.properties b/backend/muster-roll/src/main/resources/application.properties index de09ab6dd4..15b4cd38de 100644 --- a/backend/muster-roll/src/main/resources/application.properties +++ b/backend/muster-roll/src/main/resources/application.properties @@ -53,6 +53,9 @@ kafka.topics.notification.sms=egov.core.notification.sms egov.mdms.host=https://works-dev.digit.org egov.mdms.search.endpoint=/egov-mdms-service/v1/_search +egov.mdms.v2.host=https://unified-dev.digit.org +egov.mdms.v2.search.endpoint=/mdms-v2/v1/_search + #-----------------User config---------------------# egov.user.host=https://works-dev.digit.org egov.user.context.path=/user/users diff --git a/utilities/works-pdf/package.json b/utilities/works-pdf/package.json index becc9a1c77..5ee9d5d885 100644 --- a/utilities/works-pdf/package.json +++ b/utilities/works-pdf/package.json @@ -1,6 +1,6 @@ { "name": "works-pdf", - "version": "1.0.2", + "version": "1.0.3", "main": "index.js", "license": "MIT", "private": true, diff --git a/utilities/works-pdf/src/api.js b/utilities/works-pdf/src/api.js index 28d0368b30..cc89987ec5 100644 --- a/utilities/works-pdf/src/api.js +++ b/utilities/works-pdf/src/api.js @@ -205,6 +205,7 @@ async function search_localization(request, lang, module, tenantId) { async function create_pdf(tenantId, key, data, requestinfo) { var oj = Object.assign(requestinfo, data); + console.log(Object.assign(requestinfo, data)) return await axios({ responseType: "stream", method: "post", @@ -490,6 +491,57 @@ async function search_measurementBookDetails(tenantId, requestinfo,contractNumbe }); } +async function search_rateAnalysisStatementDetails(tenantId, requestinfo, referenceId) { + const search_endpoint = config.paths.analysis_statement_search; + const url = new URL(search_endpoint, config.host.statements); + requestinfo = requestinfo.RequestInfo; + const data = { + RequestInfo: requestinfo, + searchCriteria: { + tenantId: tenantId, + referenceId: referenceId + } + }; + + return await axios.post(url.href, data); +} +async function search_rateAnalysisUtilizationDetails(tenantId, requestinfo, referenceId) { + const search_endpoint = config.paths.analysis_utilization_search; + const url = new URL(search_endpoint, config.host.statements); + requestinfo = requestinfo.RequestInfo; + const data = { + RequestInfo: requestinfo, + searchCriteria: { + tenantId: tenantId, + referenceId: referenceId + } + }; + + return await axios.post(url.href, data); +} + +async function search_projectDetails_by_ID(tenantId, requestinfo, projectId) { + var params = { + tenantId: tenantId, + limit: 1, + offset: 0 + }; + + var searchEndpoint = config.paths.projectDetails_search; + var data = { + "Projects": [{ + "tenantId": tenantId, + "id": projectId + }] + } + return await axios({ + method: "post", + url: url.resolve(config.host.projectDetails, searchEndpoint), + data: Object.assign(requestinfo, data), + params, + }); +} + module.exports = { pool, create_pdf, @@ -514,5 +566,8 @@ module.exports = { create_eg_payments_excel, reset_eg_payments_excel, exec_query_eg_payments_excel, - search_measurementBookDetails + search_measurementBookDetails, + search_rateAnalysisStatementDetails, + search_projectDetails_by_ID, + search_rateAnalysisUtilizationDetails }; diff --git a/utilities/works-pdf/src/app.js b/utilities/works-pdf/src/app.js index 9cc6c92e10..dc36e5b8a6 100644 --- a/utilities/works-pdf/src/app.js +++ b/utilities/works-pdf/src/app.js @@ -12,6 +12,8 @@ var groupBills = require("./routes/groupBill"); const deviationStatementRouter = require("./routes/deviationStatement"); const measurementBookRouter = require("./routes/measurementBook"); const detailedEstimateRouter = require("./routes/detailedEstimate"); +const rateAnalysisStatement = require("./routes/analysisStatement") +const rateAnalysisUtilization = require("./routes/utilizationStatement") @@ -40,6 +42,8 @@ app.use(config.app.contextPath + "/bill", groupBills); app.use(config.app.contextPath + "/download/deviationStatement", deviationStatementRouter); app.use(config.app.contextPath + "/download/measurementBook", measurementBookRouter); app.use(config.app.contextPath + "/download/detailedEstimate", detailedEstimateRouter); +app.use(config.app.contextPath + "/download/analysisStatement", rateAnalysisStatement); +app.use(config.app.contextPath + "/download/utilizationStatement", rateAnalysisUtilization); diff --git a/utilities/works-pdf/src/config.js b/utilities/works-pdf/src/config.js index 1f8182a1be..311c3eda4d 100644 --- a/utilities/works-pdf/src/config.js +++ b/utilities/works-pdf/src/config.js @@ -37,19 +37,20 @@ module.exports = { deviationStatement_template: process.env.MEASUREMENT_TEMPLATE || "deviation-statement", measurement_template: process.env.MEASUREMENT_TEMPLATE || "measurement-book", detailedEstimate_template: process.env.DETAILED_ESTIMATE_TEMPLATE || "detailed-estimate", - + rateAnalysisStatement_template: process.env.RATE_ANALYSIS_TEMPLATE || "analysis-statement", + rateAnalysisUtilization_template: process.env.RATE_ANALYSIS_TEMPLATE || "utilization-statement", }, app: { port: parseInt(process.env.APP_PORT) || 8080, host: HOST, - contextPath: process.env.CONTEXT_PATH || "/egov-pdf", + contextPath: process.env.CONTEXT_PATH || "/works-pdf", }, host: { mdms: process.env.EGOV_MDMS_HOST || 'http://localhost:8083', - pdf: process.env.EGOV_PDF_HOST || 'http://localhost:8091', + pdf: process.env.EGOV_PDF_HOST || 'http://localhost:8081', user: process.env.EGOV_USER_HOST || HOST, workflow: process.env.EGOV_WORKFLOW_HOST || HOST, - projectDetails: process.env.EGOV_PROJECT_HOST || 'http://localhost:8081/', + projectDetails: process.env.EGOV_PROJECT_HOST || 'http://localhost:8082/', estimates: process.env.EGOV_ESTIMATE_HOST || 'http://localhost:8084/', musterRoll: process.env.EGOV_MUSTER_ROLL_HOST || 'http://localhost:8085', contract: process.env.EGOV_CONTRACT_HOST || 'http://localhost:8086', @@ -60,6 +61,7 @@ module.exports = { filestore: process.env.EGOV_FILESTORE_SERVICE_HOST || 'http://localhost:8092', expense_calculator: process.env.EXPENSE_CALCULATOR_SERVICE_HOST || 'http://localhost:8093', measurements: process.env.EGOV_MEASUREMENT_HOST || 'http://localhost:8099', + statements: process.env.RATE_ANALYSIS_STATEMENTS_HOST || 'https://unified-qa.digit.org' }, paths: { pdf_create: "/pdf-service/v1/_createnosave", @@ -80,6 +82,8 @@ module.exports = { localization_search: "/localization/messages/v1/_search", deviationStatement_search: "/estimate/v1/_search", measurement_book_search: "/mukta-services/measurement/_search", + analysis_statement_search: "/statements/v1/analysis/_search", + analysis_utilization_search: "/statements/v1/utilization/_search" }, constraints: { "beneficiaryIdByHeadCode": "Deduction_{tanentId}_{headcode}" diff --git a/utilities/works-pdf/src/routes/analysisStatement.js b/utilities/works-pdf/src/routes/analysisStatement.js new file mode 100644 index 0000000000..ba20672559 --- /dev/null +++ b/utilities/works-pdf/src/routes/analysisStatement.js @@ -0,0 +1,154 @@ +const express = require("express"); +const router = express.Router(); +const config = require("../config"); + + +const {asyncMiddleware} = require("../utils/asyncMiddleware"); +const {transformStatementData} = require("../utils/transformStatementData"); +const {search_rateAnalysisStatementDetails, search_localization} = require("../api"); +const {search_projectDetails_by_ID} = require("../api"); +const {create_pdf} = require("../api"); +const get = require("lodash.get"); +const {getCityLocalizationPrefix, getLocalizationByKey, getStateLocalizationModule, getCityLocalizationModule} = require("../utils/localization"); + +function renderError(res, errorMessage, errorCode) { + if (errorCode == undefined) errorCode = 500; + res.status(errorCode).send({ errorMessage }) + +} + +const desiredOrder = ["Material", "Labour", "Machinery"]; + +function sortData(data, order) { + return data.sort((a, b) => { + const indexA = order.indexOf(a.sorType); + const indexB = order.indexOf(b.sorType); + return indexA - indexB; + }); +} + +function updateLocalization(pdfData, localizationMaps, tenantId) { + if (pdfData.city) { + pdfData.city = pdfData.city.toUpperCase(); + cityKey = "TENANT_TENANTS_" + pdfData.city.split(".").join("_"); + pdfData.city = getLocalizationByKey(cityKey, localizationMaps); + } + if (pdfData.locality) { + let localityKey = getCityLocalizationPrefix(tenantId); + localityKey = localityKey + "_ADMIN_" + pdfData.locality; + pdfData.locality = getLocalizationByKey(localityKey, localizationMaps); + } + if (pdfData.ward) { + let boundaryKey = getCityLocalizationPrefix(tenantId); + boundaryKey = boundaryKey + "_ADMIN_" + pdfData.ward; + pdfData.ward = getLocalizationByKey(boundaryKey, localizationMaps); + } + + return pdfData; +} + +router.post("/analysis-statement", asyncMiddleware(async function (req, res, next) { + const requestInfo = req.body; + const tenantId = req.query.tenantId; + const referenceId = req.query.referenceId; + if(requestInfo == undefined) { + return renderError(res, "requestinfo can not be null", 400); + } + if(!tenantId){ + return renderError(res, "tenantId is mandatory to generate the receipt", 400); + } + if(!referenceId){ + return renderError(res, "referenceId is mandatory to generate the receipt", 400); + } + let projectData; + try { + try { + analysisStatement = await search_rateAnalysisStatementDetails(tenantId, requestInfo, referenceId); + } catch (ex) { + return renderError(res, "Failed to query details of the rate analysis statement", 500); + } + var statementData = analysisStatement.data.statement; + if (statementData.length <= 0) { + return renderError(res, "Statement Not Found", 500); + } + try { + const projectId = statementData[0].additionalDetails.projectId; + project = await search_projectDetails_by_ID(tenantId, requestInfo, projectId); + } catch (ex) { + return renderError(res, "Failed to get project details", 500); + } + projectData = project.data.Project[0]; + projectBoundary = projectData.address.boundary; + const AnalysisStatement = transformStatementData(statementData, projectData); + if (AnalysisStatement) { + let lang = "en_IN"; + let localizationMap = {}; + try { + let localizationReq = {}; + localizationReq['RequestInfo'] = requestInfo.RequestInfo; + let msgId = requestInfo.RequestInfo.msgId; + if (msgId) { + msgId = msgId.split("|") + lang = msgId.length == 2 ? msgId[1] : lang; + } + let stateLocalizationModule = getStateLocalizationModule(tenantId); + let cityLocalizationModule = getCityLocalizationModule(tenantId); + + let module = ["rainmaker-statement", stateLocalizationModule, cityLocalizationModule].join(","); + let localizations = await search_localization(localizationReq, lang, module,tenantId ); + localizations.data.messages.forEach(localObj => { + localizationMap[localObj.code] = localObj.message; + }); + + AnalysisStatement.data.forEach(data => { + data = updateLocalization(data, localizationMap, tenantId); + if(localizationMap[data.sorType]){ + data.sorType = localizationMap[data.sorType]; + } + }) + + AnalysisStatement.data = sortData(AnalysisStatement.data, desiredOrder); + } + catch (ex) { + if (ex.response && ex.response.data) console.log(ex.response.data); + } + var pdfResponse; + const pdfKey = config.pdf.rateAnalysisStatement_template; + try { + + pdfResponse = await create_pdf( + tenantId, + pdfKey, + AnalysisStatement, + requestInfo + ) + const filename = `${pdfKey}_${new Date().getTime()}`; + + res.writeHead(200, { + + "Content-Type": "application/pdf", + "Content-Disposition": `attachment; filename=${filename}.pdf`, + }); + + pdfResponse.data.pipe(res); + + } catch (ex) { + + return renderError(res, "Failed to generate PDF for analysis statement", 500); + + } + } else { + + return renderError( + res, + "There is no statement created using this estimate id", + 404 + ); + + } + } catch (ex) { + return renderError(res, "Failed to transform analysis statement", 500); + } +})) + +module.exports = router; diff --git a/utilities/works-pdf/src/routes/utilizationStatement.js b/utilities/works-pdf/src/routes/utilizationStatement.js new file mode 100644 index 0000000000..022de456ca --- /dev/null +++ b/utilities/works-pdf/src/routes/utilizationStatement.js @@ -0,0 +1,154 @@ +const express = require("express"); +const router = express.Router(); +const config = require("../config"); + + +const {asyncMiddleware} = require("../utils/asyncMiddleware"); +const {transformStatementData} = require("../utils/transformStatementData"); +const {search_rateAnalysisUtilizationDetails, search_localization} = require("../api"); +const {search_projectDetails_by_ID} = require("../api"); +const {create_pdf} = require("../api"); +const get = require("lodash.get"); +const {getLocalizationByKey, getCityLocalizationPrefix, getStateLocalizationModule, getCityLocalizationModule} = require("../utils/localization"); + +function renderError(res, errorMessage, errorCode) { + if (errorCode == undefined) errorCode = 500; + res.status(errorCode).send({ errorMessage }) + +} + +const desiredOrder = ["Material", "Labour", "Machinery"]; + +function sortData(data, order) { + return data.sort((a, b) => { + const indexA = order.indexOf(a.sorType); + const indexB = order.indexOf(b.sorType); + return indexA - indexB; + }); +} + + +function updateLocalization(pdfData, localizationMaps, tenantId) { + if (pdfData.city) { + pdfData.city = pdfData.city.toUpperCase(); + cityKey = "TENANT_TENANTS_" + pdfData.city.split(".").join("_"); + pdfData.city = getLocalizationByKey(cityKey, localizationMaps); + } + if (pdfData.locality) { + let localityKey = getCityLocalizationPrefix(tenantId); + localityKey = localityKey + "_ADMIN_" + pdfData.locality; + pdfData.locality = getLocalizationByKey(localityKey, localizationMaps); + } + if (pdfData.ward) { + let boundaryKey = getCityLocalizationPrefix(tenantId); + boundaryKey = boundaryKey + "_ADMIN_" + pdfData.ward; + pdfData.ward = getLocalizationByKey(boundaryKey, localizationMaps); + } + + return pdfData; +} + +router.post("/utilization-statement", asyncMiddleware(async function (req, res, next) { + const requestInfo = req.body; + const tenantId = req.query.tenantId; + const referenceId = req.query.referenceId; + if(requestInfo == undefined) { + return renderError(res, "requestinfo can not be null", 400); + } + if(!tenantId){ + return renderError(res, "tenantId is mandatory to generate the receipt", 400); + } + if(!referenceId){ + return renderError(res, "referenceId is mandatory to generate the receipt", 400); + } + let projectData; + try { + try { + analysisStatement = await search_rateAnalysisUtilizationDetails(tenantId, requestInfo, referenceId); + } catch (ex) { + return renderError(res, "Failed to query details of the rate analysis statement", 500); + } + var statementData = analysisStatement.data.statement; + if (statementData.length <= 0) { + return renderError(res, "Statement Not Found", 500); + } + try { + const projectId = statementData[0].additionalDetails.projectId; + project = await search_projectDetails_by_ID(tenantId, requestInfo, projectId); + } catch (ex) { + return renderError(res, "Failed to get project details", 500); + } + projectData = project.data.Project[0]; + const AnalysisStatement = transformStatementData(statementData, projectData); + if (AnalysisStatement) { + let lang = "en_IN"; + let localizationMap = {}; + try { + let localizationReq = {}; + localizationReq['RequestInfo'] = requestInfo.RequestInfo; + let msgId = requestInfo.RequestInfo.msgId; + if (msgId) { + msgId = msgId.split("|") + lang = msgId.length == 2 ? msgId[1] : lang; + } + let stateLocalizationModule = getStateLocalizationModule(tenantId); + let cityLocalizationModule = getCityLocalizationModule(tenantId); + + let module = ["rainmaker-statement", stateLocalizationModule, cityLocalizationModule].join(","); + let localizations = await search_localization(localizationReq, lang, module,tenantId ); + localizations.data.messages.forEach(localObj => { + localizationMap[localObj.code] = localObj.message; + }); + + AnalysisStatement.data.forEach(data => { + data = updateLocalization(data, localizationMap, tenantId); + if(localizationMap[data.sorType]){ + data.sorType = localizationMap[data.sorType]; + } + }) + + AnalysisStatement.data = sortData(AnalysisStatement.data, desiredOrder); + } + catch (ex) { + if (ex.response && ex.response.data) console.log(ex.response.data); + } + var pdfResponse; + const pdfKey = config.pdf.rateAnalysisUtilization_template; + try { + + pdfResponse = await create_pdf( + tenantId, + pdfKey, + AnalysisStatement, + requestInfo + ) + const filename = `${pdfKey}_${new Date().getTime()}`; + + res.writeHead(200, { + + "Content-Type": "application/pdf", + "Content-Disposition": `attachment; filename=${filename}.pdf`, + }); + + pdfResponse.data.pipe(res); + + } catch (ex) { + + return renderError(res, "Failed to generate PDF for utilization statement", 500); + + } + } else { + + return renderError( + res, + "There is no utilization created using this estimate number", + 404 + ); + + } + } catch (ex) { + return renderError(res, "Failed to transform analysis statement", 500); + } +})) + +module.exports = router; diff --git a/utilities/works-pdf/src/utils/transformStatementData.js b/utilities/works-pdf/src/utils/transformStatementData.js new file mode 100644 index 0000000000..5c94c18267 --- /dev/null +++ b/utilities/works-pdf/src/utils/transformStatementData.js @@ -0,0 +1,118 @@ +const e = require("express"); +const { logger } = require("../logger"); + +function formatInLakh(totalAmount) { + // Ensure totalAmount is a number + let amount = Number(totalAmount).toFixed(2); // Ensure two decimal places + let parts = amount.split("."); // Split the amount into whole and decimal parts + let wholePart = parts[0]; + let decimalPart = parts.length > 1 ? "." + parts[1] : ""; + + // Regular expression to insert commas for lakhs + wholePart = wholePart.replace(/\B(?=(\d{2})+(?!\d))/g, ",").replace(/^(\d+),/, '$1,'); + + return wholePart + decimalPart; +} + +const transformStatementData = (data, project) => { + try { + const sorMap = new Map(); + statement = data[0]; + const tenantId = statement['tenantId']; + const estimateNumber = statement.additionalDetails.estimateNumber; + const measurementBookNumber = statement.additionalDetails.measurementNumber; + const ProjectID = project.projectNumber; + const ProjectName = project.name; + const locality = project.additionalDetails && project.additionalDetails.locality != null ? project.additionalDetails.locality : project.address.locality; + const ward = project.address.boundary; + const city = project.address.city; + const ProjectDescription = project.description; + + statement["sorDetails"].forEach(sorDetail => { + if(sorDetail["lineItems"] != null && sorDetail["lineItems"].length > 0){ + sorDetail["lineItems"].forEach(lineItem => { + const amountDetail = lineItem["basicSorDetails"][0]; + const sorDetailInLineItem = lineItem["additionalDetails"]["sorDetails"]; + const rateDetails = lineItem["additionalDetails"]["rateDetails"]; + + if(sorMap.has(lineItem["sorId"])){ + const amountDetailDup = sorMap.get(lineItem["sorId"]); + amountDetailDup.amount += amountDetail.amount; + amountDetailDup.quantity += amountDetail.quantity; + sorMap.set(lineItem["sorId"], amountDetailDup); + }else{ + amountDetail.description = "Code: "+ sorDetailInLineItem.id+ " \n \n" + sorDetailInLineItem.description; + amountDetail.sorType = lineItem.sorType; + amountDetail.unit = sorDetailInLineItem.uom; + amountDetail.rate = rateDetails.rate; + sorMap.set(lineItem["sorId"], amountDetail); + } + }); + }else{ + const sorId = sorDetail["sorId"]; + sorDetail["basicSorDetails"].forEach(basicSorDetail => { + const sorDetailInLineItem = sorDetail["additionalDetails"]["sorDetails"]; + const rateDetails = sorDetail["additionalDetails"]["rateDetails"]; + if(sorMap.has(sorId)){ + const amountDetail = sorMap.get(sorId); + amountDetail.amount += basicSorDetail.amount; + amountDetail.quantity += basicSorDetail.quantity; + sorMap.set(sorId, amountDetail); + }else{ + basicSorDetail.description = "Code: "+ sorDetailInLineItem.id+ " \n \n" + sorDetailInLineItem.description; + basicSorDetail.sorType = sorDetailInLineItem.sorType; + basicSorDetail.unit = sorDetailInLineItem.uom; + basicSorDetail.rate = rateDetails.rate; + sorMap.set(sorId,basicSorDetail); + } + }) + } + }); + const sorTypeToSorMap = new Map(); + for(let value of sorMap.values()){ + if(!sorTypeToSorMap.has(value.type)){ + sorTypeToSorMap.set(value.type, []); + } + value.quantity = value.quantity.toFixed(4); + value.Sno = sorTypeToSorMap.get(value.type).length + 1; + sorTypeToSorMap.get(value.type).push(value); + } + const sorTypeMap = { + "W": "WRK_WORKS", + "M": "WRK_MATERIALS", + "E": "WRK_MACHINERY", + "L": "WRK_LABOUR" + }; + const resultArray = [...sorTypeToSorMap.entries()].map(([sorType, amountDetails], index) => ({ + sorType : sorTypeMap[sorType], + amountDetails, + measurementBookNumber, + ProjectID, + ProjectName, + ProjectDescription, + locality, + ward, + city, + estimateNumber, + tenantId + })); + resultArray.forEach(result => { + var totalAmount = 0; + result.amountDetails.forEach(amountDetail => { + totalAmount += parseFloat(amountDetail.amount); + amountDetail.amount = formatInLakh(amountDetail.amount); + }) + result.totalEstimatedAmount = formatInLakh(totalAmount); + }) + const AnalysisStatement = { + "data": resultArray + }; + return AnalysisStatement; + }catch (ex){ + console.log(ex); + } +}; + +module.exports = { + transformStatementData +} \ No newline at end of file