/* * eGov suite of products aim to improve the internal efficiency,transparency, * accountability and the service delivery of the government organizations. * * Copyright (C) <2015> eGovernments Foundation * * The updated version of eGov suite of products as by eGovernments Foundation * is available at http://www.egovernments.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/ or * http://www.gnu.org/licenses/gpl.html . * * In addition to the terms of the GPL license to be adhered to in using this * program, the following additional terms are to be complied with: * * 1) All versions of this program, verbatim or modified must carry this * Legal Notice. * * 2) Any misrepresentation of the origin of the material is prohibited. It * is required that all modified versions of this material be marked in * reasonable ways as different from the original version. * * 3) This license does not grant any rights to any user of the program * with regards to rights under trademark law for use of the trade names * or trademarks of eGovernments Foundation. * * In case of any queries, you can reach eGovernments Foundation at contact@egovernments.org. */ package org.egov.dcb.service; import org.apache.log4j.Logger; import org.egov.DCBException; import org.egov.commons.Installment; import org.egov.commons.dao.InstallmentHibDao; import org.egov.dcb.bean.DCBDisplayInfo; import org.egov.dcb.bean.DCBRecord; import org.egov.dcb.bean.DCBReport; import org.egov.dcb.bean.Receipt; import org.egov.dcb.bean.ReceiptDetail; import org.egov.demand.dao.DemandGenericDao; import org.egov.demand.dao.EgDemandReasonMasterDao; import org.egov.demand.dao.EgReasonCategoryDao; import org.egov.demand.interfaces.Billable; import org.egov.demand.model.EgDemand; import org.egov.demand.model.EgDemandDetails; import org.egov.demand.model.EgDemandReasonMaster; import org.egov.demand.model.EgReasonCategory; import org.egov.demand.model.EgdmCollectedReceipt; import org.egov.infra.admin.master.entity.Module; import org.springframework.beans.factory.annotation.Autowired; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; public class DCBServiceImpl implements DCBService { private static final Logger LOGGER = Logger.getLogger(DCBServiceImpl.class); @Autowired private EgReasonCategoryDao egReasonCategoryDAO; @Autowired private EgDemandReasonMasterDao egDemandReasonMasterDAO; @Autowired private DemandGenericDao demandGenericDAO; @Autowired private InstallmentHibDao installmentDAO; private Billable billable; /** * This constructor will typically be used only for cases where this is a * spring bean. The billable MUST subsequently be set via the setBillable() * method. */ public DCBServiceImpl() { } /** * If you are using this class as-is and not as a spring-injected bean, use * this form of construction. * * @param billable */ public DCBServiceImpl(final Billable billable) { this.billable = billable; } @Override public DCBReport getCurrentDCBAndReceipts(final DCBDisplayInfo dcbDispInfo) { validate(); LOGGER.info("getCurrentDCBAndReceipts() called for: " + billable.getConsumerId()); final DCBReport dcbReport = new DCBReport(); populateDCB(dcbDispInfo, dcbReport); populateReceipts(dcbReport); LOGGER.info("getCurrentDCBAndReceipts() returned: " + dcbReport); return dcbReport; } @Override public DCBReport getCurrentDCBOnly(final DCBDisplayInfo dcbDispInfo) { validate(); LOGGER.info("getCurrentDCBOnly() called for: " + billable.getConsumerId()); final DCBReport dcbReport = new DCBReport(); populateDCB(dcbDispInfo, dcbReport); LOGGER.info("getCurrentDCBOnly() returned: " + dcbReport); return dcbReport; } @Override public DCBReport getReceiptsOnly() { validate(); LOGGER.info("getReceiptsOnly() called for: " + billable.getConsumerId()); final DCBReport dcbReport = new DCBReport(); populateReceipts(dcbReport); LOGGER.info("getReceiptsOnly() returned: " + dcbReport); return dcbReport; } private void populateDCB(final DCBDisplayInfo dcbDispInfo, final DCBReport report) { final EgDemand egDemand = billable.getCurrentDemand(); final List<EgDemandReasonMaster> reasonMaster = prepareReasonMasters(egDemand, dcbDispInfo); final List<String> fieldNames = prepareFieldNames(reasonMaster, dcbDispInfo); final Module module = getModuleFromDemand(egDemand); Map<Installment, DCBRecord> dcbReportMap = new TreeMap<Installment, DCBRecord>(); dcbReportMap = iterateDCB(demandGenericDAO.getDCB(egDemand, module), dcbReportMap, fieldNames, egDemand); report.setFieldNames(fieldNames); report.setRecords(dcbReportMap); LOGGER.debug("Got DCB..."); } private void populateReceipts(final DCBReport report) { final List<InstallmentReceiptTuple> tuples = enumerateTuples(); final Map<Installment, List<Receipt>> receipts = consolidateTuplesInstallmentWise(tuples); final Map<Receipt, List<ReceiptDetail>> receiptBreakups = consolidateTuplesReceiptWise(tuples); report.setReceipts(receipts); report.backfillReceiptDetails(receiptBreakups); LOGGER.debug("Got receipts..."); } /** * Method called internally to iterate the DCB List which we get from * Database with the EgDemand Parameter passed by the caller. A Map is being * created with Installment as key and DCBRecord(Demand , Collection and * Balance maps) as value * * @param dcbList * -List of the DCB Details which are retrieved from the * DataBase. * @param dcbReportMap * - * @param fieldNames * -Names of the ReasonMasters which are used to show in DCB * Screen. * @param dmd * - Demand Object which is provided by the caller in Billable * Object * @return java.util.Map<Installment, DCBRecord> */ @SuppressWarnings("unchecked") Map<Installment, DCBRecord> iterateDCB(final List dcbList, final Map<Installment, DCBRecord> dcbReportMap, final List<String> fieldNames, final EgDemand dmd) { if (dcbList != null && fieldNames != null && !fieldNames.isEmpty()) { Installment installment = null; Map<String, BigDecimal> demands = null; Map<String, BigDecimal> collections = null; Map<String, BigDecimal> rebates = null; for (int i = 0; i < dcbList.size();) { final Object[] dcbData = (Object[]) dcbList.get(i); installment = (Installment) installmentDAO.findById(Integer.parseInt(dcbData[0].toString()), false); DCBRecord dcbRecord = null; demands = new HashMap<String, BigDecimal>(); collections = new HashMap<String, BigDecimal>(); rebates = new HashMap<String, BigDecimal>(); initDemandAndCollectionMap(fieldNames, demands, collections, rebates); for (int j = i; j < dcbList.size(); j++, i++) { final Object[] dcbData2 = (Object[]) dcbList.get(i); if (dcbData[0].equals(dcbData2[0])) dcbRecord = prepareDCMap(dcbData2, dcbRecord, demands, collections, rebates, dmd, fieldNames); else break; dcbReportMap.put(installment, dcbRecord); } } } return dcbReportMap; } /** * Method called internally to iterate the DCB List and prepare the Demand * and Collection Maps with Field name(i,e either the EGReason Category * master name or EgDemandReasonMaster name depending upon the parameters * passed by the client) and Amount as the key. * * @param dcbData2 * - Object array which is retrieved from the Database with DCB * Details * @param dcbRecord * @param demands * @param collections * @param dmd * - Demand Object which is provided by the caller in Billable * Object * @param fieldNames * -Names of the ReasonMasters which are used to show in DCB * Screen. * @return java.util.Map<Installment, DCBRecord> */ DCBRecord prepareDCMap(final Object[] dcbData2, DCBRecord dcbRecord, final Map<String, BigDecimal> demands, final Map<String, BigDecimal> collections, final Map<String, BigDecimal> rebates, final EgDemand dmd, final List<String> fieldNames) { if (dcbData2 != null && dmd != null) { final String reason = getReason(dcbData2, fieldNames); if (reason != null && demands != null && demands.containsKey(reason)) { demands.put(reason, demands.get(reason).add(new BigDecimal(dcbData2[1].toString()))); collections.put(reason, collections.get(reason).add(new BigDecimal(dcbData2[2].toString()))); rebates.put(reason, rebates.get(reason).add(new BigDecimal(dcbData2[5].toString()))); } dcbRecord = new DCBRecord(demands, collections, rebates); } return dcbRecord; } /** * Method called internally to get the Reason(i,e either the EGReason * Category master name or EgDemandReasonMaster name) to put as a key to * Demand and collection Maps. * * @param dcbData2 * - Object array which is retrieved from the Database with DCB * Details * @param fieldNames * -Names of the ReasonMasters which are used to show in DCB * Screen. * @return reason - Master reason(i.e either the ReasonMaster name or * ReasonCategory name) */ String getReason(final Object[] dcbData2, final List<String> fieldNames) { String reason = ""; if (dcbData2 != null && fieldNames != null && !fieldNames.isEmpty()) { final EgReasonCategory reasonCategory = egReasonCategoryDAO.findById(Long.valueOf(dcbData2[4].toString()), false); final EgDemandReasonMaster reasonMaster = egDemandReasonMasterDAO.findById(Long.valueOf(dcbData2[3].toString()), false); if (reasonCategory != null && fieldNames != null && fieldNames.contains(reasonCategory.getName())) reason = reasonCategory.getName(); else if (reasonMaster != null && fieldNames != null && fieldNames.contains(reasonMaster.getReasonMaster())) reason = reasonMaster.getReasonMaster(); } return reason; } /** * Method called internally to prepare the Map with fieldNames * dynamically(i.e field names can be verified depending upon the client) * * @param prepareFieldNames * @param demand * @param collection */ void initDemandAndCollectionMap(final List<String> prepareFieldNames, final Map<String, BigDecimal> demand, final Map<String, BigDecimal> collection, final Map<String, BigDecimal> rebates) { if (prepareFieldNames != null && !prepareFieldNames.isEmpty()) for (final String fieldName : prepareFieldNames) { demand.put(fieldName, BigDecimal.ZERO); collection.put(fieldName, BigDecimal.ZERO); rebates.put(fieldName, BigDecimal.ZERO); } } /** * Method called internally to prepare the List of fieldNames * dynamically(i.e field names can be verified depending upon the client) * With the EgDemand reason masters (i.e either given by client or all the * Reason Master of that module) * * @param dmdReasonMasters * @param dcbDisPlayInfo * @return java.util.List<String> */ List<String> prepareFieldNames(final List<EgDemandReasonMaster> dmdReasonMasters, final DCBDisplayInfo dcbDisPlayInfo) { final List<String> fieldNames = new ArrayList<String>(); if (dmdReasonMasters != null && !dmdReasonMasters.isEmpty()) { List<String> reasonCatgoryCodes = null; if (dcbDisPlayInfo != null && dcbDisPlayInfo.getReasonCategoryCodes() != null && !dcbDisPlayInfo.getReasonCategoryCodes().isEmpty()) { reasonCatgoryCodes = dcbDisPlayInfo.getReasonCategoryCodes(); fieldNames.addAll(getNamesFromCodes(reasonCatgoryCodes)); } for (final EgDemandReasonMaster reasonMaster : dmdReasonMasters) if (reasonMaster != null && reasonMaster.getEgReasonCategory() != null) { final String categoryReason = reasonMaster.getEgReasonCategory().getName(); if (!fieldNames.contains(categoryReason)) fieldNames.add(reasonMaster.getReasonMaster()); } } return fieldNames; } public List<String> getNamesFromCodes(final List<String> reasonCatgoryCodes) { final List<String> categoryMasters = new ArrayList<String>(); for (final String reasonMasterCode : reasonCatgoryCodes) categoryMasters.add(demandGenericDAO.getReasonCategoryByCode(reasonMasterCode).getName()); return categoryMasters; } /** * Method called internally to prepare EgDemand reason masters (i.e either * given by client or all the Reason Master of that module) * * @param demand * @param dcbDisPlayInfo * @return java.util.List<EgDemandReasonMaster> * @throws org.egov.DCBException.DCBException * (If the Module is null For the EgDemand) */ List<EgDemandReasonMaster> prepareReasonMasters(final EgDemand demand, final DCBDisplayInfo dcbDisPlayInfo) { List<String> RsonMasterCodes = null; List<EgDemandReasonMaster> reasonMsters = null; if (demand != null) { final Module module = getModuleFromDemand(demand); if (module == null) throw new DCBException(" EgModule are missing for the provided EgDemand Id =" + demand.getId()); if (dcbDisPlayInfo != null && dcbDisPlayInfo.getReasonMasterCodes() != null && !dcbDisPlayInfo.getReasonMasterCodes().isEmpty()) RsonMasterCodes = dcbDisPlayInfo.getReasonMasterCodes(); if (RsonMasterCodes == null || RsonMasterCodes.isEmpty()) reasonMsters = getEgdemandReasonMasters(module); else reasonMsters = getEgdemandReasonMasters(RsonMasterCodes, module); } return reasonMsters; } /** * Method called internally to get all EgDemand reason masters (i.e either * given by client or all the Reason Master of that module) * * @param demandReasonMsterCodes * - ReasonMasters codes of which the module is used * @param module * @return java.util.List<EgDemandReasonMaster> - All the ReasonMaster * Objects for the given Master codes */ List<EgDemandReasonMaster> getEgdemandReasonMasters(final List<String> demandReasonMsterCodes, final Module module) { List<EgDemandReasonMaster> reasonMsters = null; if (demandReasonMsterCodes != null && !demandReasonMsterCodes.isEmpty() && module != null) { reasonMsters = new ArrayList<EgDemandReasonMaster>(); for (final String dmdReasonMasterCode : demandReasonMsterCodes) { final EgDemandReasonMaster dmdReasonMaster = demandGenericDAO.getDemandReasonMasterByCode( dmdReasonMasterCode, module); if (dmdReasonMaster != null) reasonMsters.add(dmdReasonMaster); } } return reasonMsters; } /** * Method called internally to get EgModule From Egdemand.Module is taken * from the ReasonMaster Object. * * @param demand * @return java.util.List<EgDemandReasonMaster> * @throws org.egov.DCBException.DCBException * (If the Module is null For the Provided EgDemand) */ Module getModuleFromDemand(final EgDemand demand) { Module module = null; List demandRsonMastersList = new ArrayList(); if (demand != null) { demandRsonMastersList = demandGenericDAO.getEgDemandReasonMasterIds(demand); if (demandRsonMastersList == null || demandRsonMastersList.isEmpty()) throw new DCBException("EgDemandReasonMasters missing for the provided EgDemand Id " + demand.getId()); module = egDemandReasonMasterDAO.findById(Long.valueOf(demandRsonMastersList.get(0).toString()), false) .getEgModule(); } return module; } /** * Method called internally to get all Reason masters (i.e either provided * by caller or get all the Reason Masters of particular module of the * Demand Provided) * * @param demandReasonMsterCodes * - ReasonMasters codes of which the module is used * @param module * @return java.util.List<EgDemandReasonMaster> - All the ReasonMaster * Objects for the given Master codes */ List<EgDemandReasonMaster> getEgdemandReasonMasters(final Module module) { List<EgDemandReasonMaster> reasonMsters = null; if (module != null) reasonMsters = demandGenericDAO.getDemandReasonMasterByModule(module); return reasonMsters; } /** * Constructs a list of (installment, receipt) tuples, including duplicates. * * @return */ private List<InstallmentReceiptTuple> enumerateTuples() { final List<InstallmentReceiptTuple> allReceiptTuples = new ArrayList<InstallmentReceiptTuple>(); InstallmentReceiptTuple tuple = null; List<EgDemand> allDemands = billable.getAllDemands(); if(allDemands != null) for (final EgDemand demand : allDemands) for (final EgDemandDetails det : demand.getEgDemandDetails()) for (final EgdmCollectedReceipt collReceipt : det.getEgdmCollectedReceipts()) { tuple = new InstallmentReceiptTuple(det.getEgDemandReason().getEgInstallmentMaster(), Receipt.mapFrom(collReceipt)); allReceiptTuples.add(tuple); } Collections.sort(allReceiptTuples); LOGGER.info("enumerateTuples() returned: " + allReceiptTuples); return allReceiptTuples; } /** * Constructs a list of receipts for each installment. * * @param tuples * @return */ private Map<Installment, List<Receipt>> consolidateTuplesInstallmentWise(final List<InstallmentReceiptTuple> tuples) { final Map<Installment, List<Receipt>> consolidated = new HashMap<Installment, List<Receipt>>(); for (final InstallmentReceiptTuple t : tuples) { List<Receipt> receiptsForInstallment = consolidated.get(t.installment); if (receiptsForInstallment == null) { // does not exist receiptsForInstallment = new ArrayList<Receipt>(); consolidated.put(t.installment, receiptsForInstallment); } if (!receiptsForInstallment.contains(t.receipt)) // data // crunching if // already there receiptsForInstallment.add(t.receipt); } LOGGER.info("consolidateTuplesInstallmentWise() returned: " + consolidated); return consolidated; } /** * Constructs a list of receipts, each having its installment-wise amount * breakups. * * @param tuples * @return */ private Map<Receipt, List<ReceiptDetail>> consolidateTuplesReceiptWise(final List<InstallmentReceiptTuple> tuples) { final Map<Receipt, List<ReceiptDetail>> consolidated = new HashMap<Receipt, List<ReceiptDetail>>(); List<ReceiptDetail> breakups = null; for (final InstallmentReceiptTuple t : tuples) { breakups = consolidated.get(t.receipt); if (breakups == null) { breakups = new ArrayList<ReceiptDetail>(); consolidated.put(t.receipt, breakups); } final ReceiptDetail det = t.receipt.getReceiptDetails().get(0); breakups.add(det); } LOGGER.info("consolidateTuplesReceiptWise() returned: " + consolidated); return consolidated; } @Override public void setBillable(final Billable billable) { this.billable = billable; } private void validate() { if (billable == null) throw new IllegalStateException("Please call the setBillable() method first!"); } /** * This class is used when constructing a list of installment-receipt * combinations. (We can't use a BidiMap because there is duplication of * both values.) */ private static class InstallmentReceiptTuple implements Comparable<InstallmentReceiptTuple> { private final Installment installment; private final Receipt receipt; private InstallmentReceiptTuple(final Installment installment, final Receipt receipt) { this.installment = installment; this.receipt = receipt; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(installment).append(":").append(receipt); return sb.toString(); } /** * Sorting based on installments. */ @Override public int compareTo(final InstallmentReceiptTuple other) { return installment.compareTo(other.installment); } } }