/* * 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.ptis.service.collection; import static org.egov.ptis.constants.PropertyTaxConstants.ARREAR_DEMANDRSN_GLCODE; import static org.egov.ptis.constants.PropertyTaxConstants.CHQ_BOUNCE_PENALTY; import static org.egov.ptis.constants.PropertyTaxConstants.CURRENTYEAR_FIRST_HALF; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_CODE_ADVANCE; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_CODE_CHQ_BOUNCE_PENALTY; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_CODE_GENERAL_TAX; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_CODE_LIBRARY_CESS; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_STR_CHQ_BOUNCE_PENALTY; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_STR_LIBRARY_CESS; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_STR_PENALTY_FINES; import static org.egov.ptis.constants.PropertyTaxConstants.DMD_STATUS_CHEQUE_BOUNCED; import static org.egov.ptis.constants.PropertyTaxConstants.FIRST_REBATETAX_PERC; import static org.egov.ptis.constants.PropertyTaxConstants.GLCODEMAP_FOR_ARREARTAX; import static org.egov.ptis.constants.PropertyTaxConstants.GLCODEMAP_FOR_CURRENTTAX; import static org.egov.ptis.constants.PropertyTaxConstants.GLCODES_FOR_ARREARTAX; import static org.egov.ptis.constants.PropertyTaxConstants.GLCODES_FOR_CURRENTTAX; import static org.egov.ptis.constants.PropertyTaxConstants.GLCODE_FOR_PENALTY; import static org.egov.ptis.constants.PropertyTaxConstants.GLCODE_FOR_TAXREBATE; import static org.egov.ptis.constants.PropertyTaxConstants.PTMODULENAME; import static org.egov.ptis.constants.PropertyTaxConstants.SECOND_REBATETAX_PERC; import static org.egov.ptis.constants.PropertyTaxConstants.STR_FOR_CASH; import static org.egov.ptis.constants.PropertyTaxConstants.STR_FOR_CASH_ADJUSTMENT; import static org.egov.ptis.constants.PropertyTaxConstants.STR_FOR_SUBMISSION; import static org.egov.ptis.constants.PropertyTaxConstants.STR_INSTRUMENTTYPE_CHEQUE; import static org.egov.ptis.constants.PropertyTaxConstants.STR_INSTRUMENTTYPE_DD; import static org.egov.ptis.constants.PropertyTaxConstants.STR_REALIZATION; import static org.egov.ptis.constants.PropertyTaxConstants.STR_WITH_AMOUNT; import static org.egov.ptis.constants.PropertyTaxConstants.CURRENTYEAR_SECOND_HALF; import static org.egov.ptis.constants.PropertyTaxConstants.DEMANDRSN_STR_ADVANCE; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.egov.collection.entity.ReceiptDetail; import org.egov.collection.integration.models.BillReceiptInfo; import org.egov.collection.integration.models.BillReceiptInfoImpl; import org.egov.collection.integration.models.ReceiptAccountInfo; import org.egov.collection.integration.models.ReceiptAmountInfo; import org.egov.collection.integration.models.ReceiptInstrumentInfo; import org.egov.collection.integration.services.CollectionIntegrationService; import org.egov.commons.Installment; import org.egov.commons.dao.ChartOfAccountsHibernateDAO; import org.egov.commons.dao.FunctionHibernateDAO; import org.egov.demand.dao.DemandGenericDao; import org.egov.demand.dao.EgBillDao; import org.egov.demand.integration.TaxCollection; import org.egov.demand.model.EgBill; import org.egov.demand.model.EgBillDetails; import org.egov.demand.model.EgDemand; import org.egov.demand.model.EgDemandDetails; import org.egov.demand.model.EgDemandReason; import org.egov.demand.model.EgDemandReasonMaster; import org.egov.infra.admin.master.entity.Module; import org.egov.infra.admin.master.entity.User; import org.egov.infra.admin.master.service.ModuleService; import org.egov.infra.exception.ApplicationRuntimeException; import org.egov.infra.messaging.MessagingService; import org.egov.infra.utils.MoneyUtils; import org.egov.infstr.services.PersistenceService; import org.egov.ptis.client.bill.PTBillServiceImpl; import org.egov.ptis.client.service.CollectionApportioner; import org.egov.ptis.client.util.PropertyTaxUtil; import org.egov.ptis.constants.PropertyTaxConstants; import org.egov.ptis.domain.entity.demand.Ptdemand; import org.egov.ptis.domain.entity.property.Property; import org.egov.ptis.service.utils.PropertyTaxCommonUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; /** * This class is used to persist Collections .This is used for the integration * of Collections and Bills and property tax. */ @Transactional public class PropertyTaxCollection extends TaxCollection { private static final Logger LOGGER = Logger.getLogger(PropertyTaxCollection.class); private PersistenceService persistenceService; private BigDecimal totalAmount = BigDecimal.ZERO; private Installment currInstallment = null; @Autowired private ModuleService moduleDao; @Autowired private EgBillDao egBillDAO; @Autowired private DemandGenericDao demandGenericDAO; @Autowired private PersistenceService<Property, Long> propertyImplService; @Autowired private MessagingService messagingService; @Autowired private FunctionHibernateDAO functionDAO; @Autowired private ChartOfAccountsHibernateDAO chartOfAccountsDAO; private PTBillServiceImpl ptBillServiceImpl; @Autowired PropertyTaxUtil propertyTaxUtil; @Autowired private PropertyTaxCommonUtils propertyTaxCommonUtils; @Autowired private CollectionIntegrationService collectionService; @Override protected Module module() { return moduleDao.getModuleByName(PTMODULENAME); } @Override public void updateDemandDetails(final BillReceiptInfo billRcptInfo) throws ApplicationRuntimeException { totalAmount = billRcptInfo.getTotalAmount(); currInstallment = propertyTaxCommonUtils.getCurrentInstallment(); LOGGER.debug("updateDemandDetails : Updating Demand Details Started, billRcptInfo : " + billRcptInfo); try { final EgDemand demand = getCurrentDemand(Long.valueOf(billRcptInfo.getBillReferenceNum())); final String assessmentNo = ((BillReceiptInfoImpl) billRcptInfo).getReceiptMisc().getReceiptHeader() .getConsumerCode(); LOGGER.info("updateDemandDetails : Demand before proceeding : " + demand); LOGGER.info("updateDemandDetails : collection back update started for property : " + assessmentNo + " and receipt event is " + billRcptInfo.getEvent() + ". Total Receipt amount is." + totalAmount + " with receipt no." + billRcptInfo.getReceiptNum()); if (billRcptInfo.getEvent().equals(EVENT_RECEIPT_CREATED)) { updateCollForRcptCreate(demand, billRcptInfo); activateDemand(demand); buildSMS(demand, billRcptInfo); } else if (billRcptInfo.getEvent().equals(EVENT_RECEIPT_CANCELLED)) updateCollForRcptCancel(demand, billRcptInfo); else if (billRcptInfo.getEvent().equals(EVENT_INSTRUMENT_BOUNCED)) updateCollForChequeBounce(demand, billRcptInfo); LOGGER.info("updateDemandDetails : Demand after processed : " + demand); } catch (final Exception e) { throw new ApplicationRuntimeException("Error occured during back update of DCB : " + e.getMessage(), e); } LOGGER.debug("updateDemandDetails : Updating Demand Details Finished..."); } private void buildSMS(final EgDemand demand, final BillReceiptInfo billRcptInfo) { final Property property = ((Ptdemand) demand).getEgptProperty(); final User user = property.getBasicProperty().getPrimaryOwner(); final String mobileNumber = user.getMobileNumber(); final StringBuilder smsMsg = new StringBuilder(100); String instNumber = ""; final List<String> instrumentType = new ArrayList<String>(); for (final ReceiptInstrumentInfo instrumentInfo : billRcptInfo.getInstrumentDetails()) { instrumentType.add(instrumentInfo.getInstrumentType()); instNumber = instrumentInfo.getInstrumentNumber(); } if (instrumentType.contains("cheque")) smsMsg.append(STR_INSTRUMENTTYPE_CHEQUE).append(instNumber).append(STR_WITH_AMOUNT) .append(billRcptInfo.getTotalAmount()).append(STR_FOR_SUBMISSION) .append(((BillReceiptInfoImpl) billRcptInfo).getReceiptMisc().getReceiptHeader().getConsumerCode()) .append(STR_REALIZATION); else if (instrumentType.contains("dd")) smsMsg.append(STR_INSTRUMENTTYPE_DD).append(instNumber).append(STR_WITH_AMOUNT) .append(billRcptInfo.getTotalAmount()).append(STR_FOR_SUBMISSION) .append(((BillReceiptInfoImpl) billRcptInfo).getReceiptMisc().getReceiptHeader().getConsumerCode()) .append(STR_REALIZATION); else if (instrumentType.contains("cash")) smsMsg.append(STR_FOR_CASH).append(billRcptInfo.getTotalAmount()).append(STR_FOR_CASH_ADJUSTMENT) .append(((BillReceiptInfoImpl) billRcptInfo).getReceiptMisc().getReceiptHeader().getConsumerCode()); if (mobileNumber != null) messagingService.sendSMS(mobileNumber, smsMsg.toString()); } /** * This method is invoked from Collections end when an event related to * receipt in bill generation occurs. */ @Override public void updateReceiptDetails(final Set<BillReceiptInfo> billReceipts) { LOGGER.debug("updateReceiptDetails : Updating Receipt Details Started, billReceipts : " + billReceipts); final Boolean status = false; if (billReceipts != null) super.updateReceiptDetails(billReceipts); LOGGER.debug("updateReceiptDetails : Updating Receipt Details Finished, status : " + status); } /** * Adds the collected amounts in the appropriate buckets. */ private void updateCollForRcptCreate(final EgDemand demand, final BillReceiptInfo billRcptInfo) { LOGGER.debug("updateCollForRcptCreate : Updating Collection Started For Demand : " + demand + " with BillReceiptInfo - " + billRcptInfo); LOGGER.info("updateCollForRcptCreate : Total amount collected : " + totalAmount); demand.addCollected(totalAmount); try { if (demand.getMinAmtPayable() != null && demand.getMinAmtPayable().compareTo(BigDecimal.ZERO) > 0) demand.setMinAmtPayable(BigDecimal.ZERO); updateDemandDetailForReceiptCreate(billRcptInfo.getAccountDetails(), demand, billRcptInfo); } catch (final Exception e) { throw new ApplicationRuntimeException( "Error occured during back update of DCB : updateCollForRcptCreate() " + e.getMessage(), e); } LOGGER.debug("updateCollForRcptCreate : Updating Demand For Collection finished..."); } /** * Deducts the collected amounts as per the amount of the cancelled receipt. */ private void updateCollForRcptCancel(final EgDemand demand, final BillReceiptInfo billRcptInfo) { LOGGER.debug("reconcileCollForRcptCancel : Updating Collection Started For Demand : " + demand + " with BillReceiptInfo - " + billRcptInfo); cancelBill(Long.valueOf(billRcptInfo.getBillReferenceNum())); if (demand.getAmtCollected() != null) demand.setAmtCollected(demand.getAmtCollected().subtract(billRcptInfo.getTotalAmount())); updateDmdDetForRcptCancel(demand, billRcptInfo); LOGGER.debug("reconcileCollForRcptCancel : Updating Collection finished For Demand : " + demand); } /** * Deducts the collected amounts as per the amount of the bounced cheque, * and also imposes a cheque-bounce penalty. */ private void updateCollForChequeBounce(final EgDemand demand, final BillReceiptInfo billRcptInfo) { LOGGER.debug("reconcileCollForChequeBounce : Updating Collection Started For Demand : " + demand + " with BillReceiptInfo - " + billRcptInfo); final BigDecimal totalCollChqBounced = getTotalChequeAmt(billRcptInfo); final BigDecimal chqBouncePenalty = getChqBouncePenaltyAmt(totalCollChqBounced); cancelBill(Long.valueOf(billRcptInfo.getBillReferenceNum())); EgDemandDetails dmdDet = null; final EgDemandDetails penaltyDmdDet = ptBillServiceImpl.getDemandDetail(demand, currInstallment, DEMANDRSN_STR_CHQ_BOUNCE_PENALTY); if (penaltyDmdDet == null) dmdDet = ptBillServiceImpl.insertDemandDetails(DEMANDRSN_CODE_CHQ_BOUNCE_PENALTY, chqBouncePenalty, currInstallment); else { BigDecimal existDmdDetAmt = penaltyDmdDet.getAmount(); existDmdDetAmt = existDmdDetAmt == null || existDmdDetAmt.equals(BigDecimal.ZERO) ? existDmdDetAmt = BigDecimal.ZERO : existDmdDetAmt; penaltyDmdDet.setAmount(existDmdDetAmt.add(chqBouncePenalty)); dmdDet = penaltyDmdDet; } // setting this min amount into demand to check next payment should be // min of this amount with mode of payment cash or DD demand.setMinAmtPayable(totalCollChqBounced.add(chqBouncePenalty)); demand.setAmtCollected(demand.getAmtCollected().subtract(billRcptInfo.getTotalAmount())); demand.setBaseDemand(demand.getBaseDemand().add(chqBouncePenalty)); demand.setStatus(DMD_STATUS_CHEQUE_BOUNCED); demand.addEgDemandDetails(dmdDet); updateDmdDetForRcptCancel(demand, billRcptInfo); LOGGER.debug("reconcileCollForChequeBounce : Updating Collection finished For Demand : " + demand); } /** * Update the collection to respective account heads paid * * @param accountDetails * @param demand * @param billRcptInfo */ @SuppressWarnings("unchecked") private void updateDemandDetailForReceiptCreate(final Set<ReceiptAccountInfo> accountDetails, final EgDemand demand, final BillReceiptInfo billRcptInfo) { LOGGER.debug("Entering method saveCollectionDetails"); BigDecimal rebateAmount = BigDecimal.ZERO; for (final ReceiptAccountInfo accInfo : accountDetails) { if (accInfo.getDescription() != null) { if (accInfo.getDescription().contains("REBATE")) { rebateAmount = accInfo.getDrAmount(); } } } LOGGER.info("saveCollectionDetails : Start get demandDetailList"); final List<EgDemandDetails> demandDetailList = persistenceService.findAllBy( "select dmdet FROM EgDemandDetails dmdet " + "left join fetch dmdet.egDemandReason dmdRsn " + "left join fetch dmdRsn.egDemandReasonMaster dmdRsnMstr " + "left join fetch dmdRsn.egInstallmentMaster installment WHERE dmdet.egDemand = ?", demand); LOGGER.info("saveCollectionDetails : End get demandDetailList"); final Map<String, Map<String, EgDemandDetails>> installmentWiseDemandDetailsByReason = new HashMap<String, Map<String, EgDemandDetails>>(); Map<String, EgDemandDetails> demandDetailByReason = new HashMap<String, EgDemandDetails>(); EgDemandReason dmdRsn = null; String installmentDesc = null; Map<String, Installment> currInstallments = propertyTaxUtil.getInstallmentsForCurrYear(new Date()); for (final EgDemandDetails dmdDtls : demandDetailList) if (dmdDtls.getAmount().compareTo(BigDecimal.ZERO) > 0 || dmdDtls.getEgDemandReason().getEgDemandReasonMaster().getCode().equalsIgnoreCase(DEMANDRSN_CODE_ADVANCE)) { dmdRsn = dmdDtls.getEgDemandReason(); installmentDesc = dmdRsn.getEgInstallmentMaster().getDescription(); demandDetailByReason = new HashMap<String, EgDemandDetails>(); if (installmentWiseDemandDetailsByReason.get(installmentDesc) == null) { demandDetailByReason.put(dmdRsn.getEgDemandReasonMaster().getReasonMaster(), dmdDtls); installmentWiseDemandDetailsByReason.put(installmentDesc, demandDetailByReason); } else installmentWiseDemandDetailsByReason.get(installmentDesc).put( dmdRsn.getEgDemandReasonMaster().getReasonMaster(), dmdDtls); } else LOGGER.info("saveCollectionDetails - demand detail amount is zero " + dmdDtls); LOGGER.info("saveCollectionDetails - installment demandDetails size = " + installmentWiseDemandDetailsByReason.size()); EgDemandDetails demandDetail = null; for (final ReceiptAccountInfo rcptAccInfo : accountDetails) if (rcptAccInfo.getDescription() != null && !rcptAccInfo.getDescription().isEmpty()) if (rcptAccInfo.getCrAmount() != null && rcptAccInfo.getCrAmount().compareTo(BigDecimal.ZERO) == 1) { final String[] desc = rcptAccInfo.getDescription().split("-", 2); final String reason = desc[0].trim(); final String instDesc = desc[1].trim(); if (reason.equalsIgnoreCase(DEMANDRSN_STR_ADVANCE)) { demandDetail = installmentWiseDemandDetailsByReason.get(currInstallments.get(CURRENTYEAR_SECOND_HALF).getDescription()) .get(reason); } else { demandDetail = installmentWiseDemandDetailsByReason.get(instDesc).get(reason); } if (rcptAccInfo.getGlCode().equalsIgnoreCase(PropertyTaxConstants.GLCODE_FOR_PENALTY)) { if (demandDetail == null) throw new ApplicationRuntimeException("Demand Details for reason " + reason + " and with installment " + instDesc + " is null "); else demandDetail.addCollected(rcptAccInfo.getCrAmount()); } else if(rcptAccInfo.getGlCode().equalsIgnoreCase(PropertyTaxConstants.GLCODE_FOR_ADVANCE)){ if (demandDetail != null) { demandDetail.setAmtCollected(demandDetail.getAmtCollected().add(rcptAccInfo.getCrAmount())); } else { demandDetail = insertAdvanceCollection(DEMANDRSN_CODE_ADVANCE,rcptAccInfo.getCrAmount(), currInstallments.get(CURRENTYEAR_SECOND_HALF)); demand.addEgDemandDetails(demandDetail); persistenceService.getSession().flush(); if (installmentWiseDemandDetailsByReason.get(currInstallments.get(CURRENTYEAR_SECOND_HALF).getDescription()) == null) { Map<String, EgDemandDetails> reasonAndDemandDetail = new HashMap<String, EgDemandDetails>(); reasonAndDemandDetail.put(DEMANDRSN_STR_ADVANCE, demandDetail); installmentWiseDemandDetailsByReason.put(currInstallments.get(CURRENTYEAR_SECOND_HALF).getDescription(), reasonAndDemandDetail); } else { installmentWiseDemandDetailsByReason.get(currInstallments.get(CURRENTYEAR_SECOND_HALF).getDescription()).put( DEMANDRSN_STR_ADVANCE, demandDetail); } } } else { demandDetail.addCollectedWithOnePaisaTolerance(rcptAccInfo.getCrAmount()); if (rebateAmount.compareTo(BigDecimal.ZERO) > 0 && instDesc.equals(currInstallments.get(CURRENTYEAR_FIRST_HALF).getDescription()) && demandDetail.getEgDemandReason().getEgDemandReasonMaster().getCode() .equals(DEMANDRSN_CODE_GENERAL_TAX)) { demandDetail.setAmtRebate(rebateAmount); } } persistCollectedReceipts(demandDetail, billRcptInfo.getReceiptNum(), totalAmount, billRcptInfo.getReceiptDate(), demandDetail.getAmtCollected()); LOGGER.info("Persisted demand and receipt details for tax : " + reason + " installment : " + instDesc + " with receipt No : " + billRcptInfo.getReceiptNum() + " for Rs. " + rcptAccInfo.getCrAmount()); } LOGGER.debug("Exiting method saveCollectionDetails"); } /** * Reconciles the collection for respective account heads thats been paid * with given cancel receipt * * @param demand * @param billRcptInfo */ private void updateDmdDetForRcptCancel(final EgDemand demand, final BillReceiptInfo billRcptInfo) { LOGGER.debug("Entering method updateDmdDetForRcptCancel"); ReceiptAccountInfo rebateRcptAccInfo = null; final Map<String, ReceiptAccountInfo> rebateReceiptAccInfoByInstallment = getRebteReceiptAccountInfosByInstallment(billRcptInfo); for (final ReceiptAccountInfo rcptAccInfo : billRcptInfo.getAccountDetails()) if (rcptAccInfo.getCrAmount() != null && rcptAccInfo.getCrAmount().compareTo(BigDecimal.ZERO) == 1 && !rcptAccInfo.getIsRevenueAccount()) { final String[] desc = rcptAccInfo.getDescription().split("-", 2); final String reason = desc[0].trim(); final String installment = desc[1].trim(); EgDemandReasonMaster demandReasonMaster = null; rebateRcptAccInfo = rebateReceiptAccInfoByInstallment.get(installment); for (final EgDemandDetails demandDetail : demand.getEgDemandDetails()) { demandReasonMaster = demandDetail.getEgDemandReason().getEgDemandReasonMaster(); if (reason.equalsIgnoreCase(demandReasonMaster.getReasonMaster())) if (reason.equalsIgnoreCase(DEMANDRSN_CODE_ADVANCE) || installment.equals(demandDetail.getEgDemandReason().getEgInstallmentMaster() .getDescription())) { if (rebateRcptAccInfo != null) if (demandDetail.getAmtRebate().compareTo(BigDecimal.ZERO) > 0 && (demandReasonMaster.getCode().equals(DEMANDRSN_CODE_GENERAL_TAX) || demandReasonMaster .getCode().equalsIgnoreCase(DEMANDRSN_CODE_ADVANCE))) demandDetail.setAmtRebate(demandDetail.getAmtRebate().subtract( rebateRcptAccInfo.getDrAmount())); if (demandDetail.getAmtCollected().compareTo(rcptAccInfo.getCrAmount()) < 0) throw new ApplicationRuntimeException( "updateDmdDetForRcptCancel : Exception while updating cancel receipt, " + "to be deducted amount " + rcptAccInfo.getCrAmount() + " is greater than the collected amount " + demandDetail.getAmtCollected() + " for demandDetail " + demandDetail); demandDetail.setAmtCollected(demandDetail.getAmtCollected().subtract( rcptAccInfo.getCrAmount())); LOGGER.info("Deducted Collected amount Rs." + rcptAccInfo.getCrAmount() + " for tax : " + reason + " and installment : " + installment); } } } updateReceiptStatusWhenCancelled(billRcptInfo.getReceiptNum()); LOGGER.debug("Exiting method updateDmdDetForRcptCancel"); } /** * Returns a map of Installment description and ReceiptAccountInfo * * @param billRcptInfo * @return */ private Map<String, ReceiptAccountInfo> getRebteReceiptAccountInfosByInstallment(final BillReceiptInfo billRcptInfo) { final Map<String, ReceiptAccountInfo> rebateReceiptAccInfoByInstallment = new HashMap<String, ReceiptAccountInfo>(); for (final ReceiptAccountInfo rcptAccInfo : billRcptInfo.getAccountDetails()) if (rcptAccInfo.getGlCode().equalsIgnoreCase(GLCODE_FOR_TAXREBATE) || rcptAccInfo.getGlCode().equalsIgnoreCase(PropertyTaxConstants.GLCODE_FOR_ADVANCE_REBATE)) rebateReceiptAccInfoByInstallment .put(rcptAccInfo.getDescription().split("-", 2)[1].trim(), rcptAccInfo); return rebateReceiptAccInfoByInstallment; } @Override public void apportionCollection(final String billRefNo, final BigDecimal amtPaid, final List<ReceiptDetail> receiptDetails) { boolean isEligibleForCurrentRebate = false; final boolean isEligibleForAdvanceRebate = false; if (propertyTaxUtil.isRebatePeriodActive()) isEligibleForCurrentRebate = true; final CollectionApportioner apportioner = new CollectionApportioner(isEligibleForCurrentRebate, isEligibleForAdvanceRebate, BigDecimal.ZERO); final Map<String, BigDecimal> instDemand = getInstDemand(receiptDetails); apportioner.apportion(amtPaid, receiptDetails, instDemand); } private EgDemand cancelBill(final Long billId) { final EgDemand egDemand = null; if (billId != null) { final EgBill egBill = egBillDAO.findById(billId, false); egBill.setIs_Cancelled("Y"); } return egDemand; } /** * Calculates Early Payment Rebate for given Tax Amount * * @param rebateApplTaxAmt * for which Rebate has to be calculated * @return rebate amount. */ public BigDecimal calcEarlyPayRebate(final BigDecimal instTaxAmount, final BigDecimal rebateApplTaxAmt, final BigDecimal collection) { BigDecimal rebate = BigDecimal.ZERO; final Date today = new Date(); final Calendar firstRebateDate = Calendar.getInstance(); final BigDecimal halfYearTax = instTaxAmount.divide(new BigDecimal(2)); LOGGER.debug("calcEarlyPayRebate instTaxAmount " + instTaxAmount + " halfYearTax " + halfYearTax + " rebateApplTaxAmt " + rebateApplTaxAmt + " collection " + collection); final int currMonth = firstRebateDate.get(Calendar.MONTH); if (currMonth <= 2) firstRebateDate.set(Calendar.YEAR, firstRebateDate.get(Calendar.YEAR) - 1); firstRebateDate.set(Calendar.DAY_OF_MONTH, 31); firstRebateDate.set(Calendar.MONTH, Calendar.MAY); firstRebateDate.set(Calendar.HOUR_OF_DAY, 23); firstRebateDate.set(Calendar.MINUTE, 59); firstRebateDate.set(Calendar.SECOND, 59); final Calendar secondRebateDate = Calendar.getInstance(); if (currMonth <= 2) secondRebateDate.set(Calendar.YEAR, secondRebateDate.get(Calendar.YEAR) - 1); secondRebateDate.set(Calendar.DAY_OF_MONTH, 30); secondRebateDate.set(Calendar.MONTH, Calendar.NOVEMBER); secondRebateDate.set(Calendar.HOUR_OF_DAY, 23); secondRebateDate.set(Calendar.MINUTE, 59); secondRebateDate.set(Calendar.SECOND, 59); if (today.before(firstRebateDate.getTime()) || today.equals(firstRebateDate.getTime())) { if (collection.compareTo(BigDecimal.ZERO) == 1) { if (collection.compareTo(halfYearTax) <= 0) rebate = MoneyUtils.roundOff(rebateApplTaxAmt.multiply(SECOND_REBATETAX_PERC).divide( BigDecimal.valueOf(100))); else rebate = BigDecimal.ZERO; } else rebate = MoneyUtils.roundOff(rebateApplTaxAmt.multiply(FIRST_REBATETAX_PERC).divide( BigDecimal.valueOf(100))); } else if (today.before(secondRebateDate.getTime()) || today.equals(secondRebateDate.getTime())) if (collection.compareTo(halfYearTax) <= 0) rebate = MoneyUtils.roundOff(rebateApplTaxAmt.multiply(SECOND_REBATETAX_PERC).divide( BigDecimal.valueOf(100))); else rebate = BigDecimal.ZERO; LOGGER.debug("calcEarlyPayRebate rebate " + rebate); return rebate; } /** * Gives the tax amount of Account head for which Rebate applicable * * @param List * of <code>ReceiptDetail</code> * @return rebate applicable tax amount. */ public BigDecimal getRebateApplAmount(final List<ReceiptDetail> receiptDetails) { BigDecimal taxAmount = BigDecimal.ZERO; for (final ReceiptDetail rd : receiptDetails) if (rd.getAccounthead().getGlcode().equals(GLCODEMAP_FOR_CURRENTTAX.get(DEMANDRSN_CODE_GENERAL_TAX))) { /* * getting rebate amount from getCramountToBePaid() because * before receipt created CrAmount is Zero and it will updated * as part of receipt creation. */ taxAmount = rd.getCramountToBePaid(); break; } return taxAmount; } public Map<String, BigDecimal> getInstDemand(final List<ReceiptDetail> receiptDetails) { final Map<String, BigDecimal> retMap = new HashMap<String, BigDecimal>(); String installment = ""; String[] desc; for (final ReceiptDetail rd : receiptDetails) { final String glCode = rd.getAccounthead().getGlcode(); installment = ""; desc = rd.getDescription().split("-", 2); installment = desc[1].trim(); if (!glCode.equalsIgnoreCase(GLCODE_FOR_TAXREBATE) && (GLCODEMAP_FOR_ARREARTAX.containsValue(glCode) || GLCODEMAP_FOR_CURRENTTAX.containsValue(glCode))) if (retMap.get(installment) == null) retMap.put(installment, rd.getCramountToBePaid()); else retMap.put(installment, retMap.get(installment).add(rd.getCramountToBePaid())); if (GLCODES_FOR_CURRENTTAX.contains(glCode) || GLCODES_FOR_ARREARTAX.contains(glCode)) prepareTaxMap(retMap, installment, rd, "FULLTAX"); else if (PropertyTaxConstants.GLCODE_FOR_ADVANCE.equalsIgnoreCase(glCode)) prepareTaxMap(retMap, installment, rd, "ADVANCE"); } return retMap; } /** * @param retMap * @param installment * @param rd */ private void prepareTaxMap(final Map<String, BigDecimal> retMap, final String installment, final ReceiptDetail rd, final String type) { if (retMap.get(installment + type) == null) retMap.put(installment + type, rd.getCramountToBePaid()); else retMap.put(installment + type, retMap.get(installment + type).add(rd.getCramountToBePaid())); } /** * Method used to calculate the Total Cheque amount from he BillreceiptInfo * object which is received from Collections Module. * * @param billRcptInfo * @return Total Cheque amount * @exception ApplicationRuntimeException */ @Override public BigDecimal getTotalChequeAmt(final BillReceiptInfo billRcptInfo) { BigDecimal totalCollAmt = BigDecimal.ZERO; try { if (billRcptInfo != null) for (final ReceiptInstrumentInfo rctInst : billRcptInfo.getBouncedInstruments()) if (rctInst.getInstrumentAmount() != null) totalCollAmt = totalCollAmt.add(rctInst.getInstrumentAmount()); } catch (final ApplicationRuntimeException e) { throw new ApplicationRuntimeException("Exception in calculate Total Collected Amt" + e); } return totalCollAmt; } /** * Gives the Cheque bounce penalty charges for given cheque amount * * @param totalChqAmount * @return {@link BigDecimal} */ public BigDecimal getChqBouncePenaltyAmt(final BigDecimal totalChqAmount) { return CHQ_BOUNCE_PENALTY; } /** * Method used to create new EgDemandDetail Object depending upon the * EgDemandReason , Collected amount and Demand amount(which are * compulsory),Other wise returns Empty EgDemandDetails Object. * * @param egDemandReason * @param amtCollected * @param dmdAmount * @return New EgDemandDetails Object */ public EgDemandDetails createDemandDetails(final EgDemandReason egDemandReason, final BigDecimal amtCollected, final BigDecimal dmdAmount) { return EgDemandDetails.fromReasonAndAmounts(dmdAmount, egDemandReason, amtCollected); } /** * Gives the Current EgDemand for billId * * @param upicNo * @return EgDemand */ @SuppressWarnings("unchecked") public EgDemand getCurrentDemand(final Long billId) { LOGGER.debug("Entered into getCurrentDemand"); final EgBill egBill = egBillDAO.findById(billId, false); final String query = "SELECT ptd FROM Ptdemand ptd " + "WHERE ptd.egInstallmentMaster = ? " + "AND ptd.egptProperty.basicProperty.upicNo = ? " + "AND (ptd.egptProperty.status = 'I' OR ptd.egptProperty.status = 'A') " + "AND ptd.egptProperty.basicProperty.active = true"; final EgDemand egDemand = (EgDemand) persistenceService.find(query, currInstallment, egBill.getConsumerId()); LOGGER.debug("Exiting from getCurrentDemand"); return egDemand; } /** * Method used to insert advance collection in EgDemandDetail table. * * @see createDemandDetails() -- EgDemand Details are created * @return New EgDemandDetails Object */ public EgDemandDetails insertAdvanceCollection(final String demandReason, final BigDecimal advanceCollectionAmount, final Installment installment) { EgDemandDetails demandDetail = null; if (advanceCollectionAmount != null && advanceCollectionAmount.compareTo(BigDecimal.ZERO) > 0) { final EgDemandReasonMaster egDemandReasonMaster = demandGenericDAO.getDemandReasonMasterByCode( demandReason, module()); if (egDemandReasonMaster == null) throw new ApplicationRuntimeException( " Advance Demand reason Master is null in method insertAdvanceCollection"); final EgDemandReason egDemandReason = demandGenericDAO.getDmdReasonByDmdReasonMsterInstallAndMod( egDemandReasonMaster, installment, module()); if (egDemandReason == null) throw new ApplicationRuntimeException( " Advance Demand reason is null in method insertAdvanceCollection "); demandDetail = createDemandDetails(egDemandReason, advanceCollectionAmount, BigDecimal.ZERO); } return demandDetail; } // Activating the demand on payment @Transactional private void activateDemand(final EgDemand demand) { final Property property = ((Ptdemand) demand).getEgptProperty(); if (property.getStatus().equals(PropertyTaxConstants.STATUS_DEMAND_INACTIVE)) { property.setStatus(PropertyTaxConstants.STATUS_ISACTIVE); propertyImplService.persist(property); } } public void setPersistenceService(final PersistenceService persistenceService) { this.persistenceService = persistenceService; } @Override public List<ReceiptDetail> reconstructReceiptDetail(final String billReferenceNumber, final BigDecimal actualAmountPaid, final List<ReceiptDetail> receiptDetailList) { final Long billID = Long.valueOf(billReferenceNumber); final List<EgBillDetails> billDetails = new ArrayList<EgBillDetails>(0); final EgBill bill = ptBillServiceImpl.updateBillWithLatest(billID); LOGGER.debug("Reconstruct consumer code :" + bill.getConsumerId() + ", with bill reference number: " + billReferenceNumber + ", for Amount Paid :" + actualAmountPaid); boolean isEligibleForCurrentRebate = false; final boolean isEligibleForAdvanceRebate = false; if (propertyTaxUtil.isRebatePeriodActive()) isEligibleForCurrentRebate = true; final CollectionApportioner apportioner = new CollectionApportioner(isEligibleForCurrentRebate, isEligibleForAdvanceRebate, actualAmountPaid); billDetails.addAll(bill.getEgBillDetails()); return apportioner.reConstruct(actualAmountPaid, billDetails, functionDAO, chartOfAccountsDAO); } @Override public String constructAdditionalInfoForReceipt(final BillReceiptInfo billReceiptInfo) { return null; } @Override public ReceiptAmountInfo receiptAmountBifurcation(final BillReceiptInfo billReceiptInfo) { final ReceiptAmountInfo receiptAmountInfo = new ReceiptAmountInfo(); BigDecimal currentInstallmentAmount = BigDecimal.ZERO; BigDecimal arrearAmount = BigDecimal.ZERO; BigDecimal latePaymentCharges = BigDecimal.ZERO; BigDecimal arrearLibCess = BigDecimal.ZERO; BigDecimal currLibCess = BigDecimal.ZERO; BigDecimal rebateAmount = BigDecimal.ZERO; final EgBill egBill = egBillDAO.findById(Long.valueOf(billReceiptInfo.getBillReferenceNum()), false); final List<EgBillDetails> billDetails = new ArrayList<EgBillDetails>(egBill.getEgBillDetails()); final List<ReceiptDetail> reciptDetailList = collectionService.getReceiptDetailListByReceiptNumber(billReceiptInfo .getReceiptNum()); for (final ReceiptAccountInfo rcptAccInfo : billReceiptInfo.getAccountDetails()) if (rcptAccInfo.getCrAmount() != null && rcptAccInfo.getCrAmount().compareTo(BigDecimal.ZERO) == 1) { final String[] desc = rcptAccInfo.getDescription().split("-", 2); final String reason = desc[0]; if (rcptAccInfo.getGlCode().equals(GLCODE_FOR_PENALTY) && reason.equals(DEMANDRSN_STR_PENALTY_FINES)) latePaymentCharges = latePaymentCharges.add(rcptAccInfo.getCrAmount()); else if (rcptAccInfo.getGlCode().equals(GLCODEMAP_FOR_CURRENTTAX.get(DEMANDRSN_CODE_LIBRARY_CESS)) && reason.equals(DEMANDRSN_STR_LIBRARY_CESS)) currLibCess = currLibCess.add(rcptAccInfo.getCrAmount()); else if (rcptAccInfo.getGlCode().equals(GLCODEMAP_FOR_ARREARTAX.get(DEMANDRSN_CODE_LIBRARY_CESS)) && reason.equals(DEMANDRSN_STR_LIBRARY_CESS)) arrearLibCess = arrearLibCess.add(rcptAccInfo.getCrAmount()); else if (rcptAccInfo.getGlCode().equals(ARREAR_DEMANDRSN_GLCODE)) arrearAmount = arrearAmount.add(rcptAccInfo.getCrAmount()); else currentInstallmentAmount = currentInstallmentAmount.add(rcptAccInfo.getCrAmount()); } else if (rcptAccInfo.getDrAmount() != null && rcptAccInfo.getDrAmount().compareTo(BigDecimal.ZERO) == 1) if (rcptAccInfo.getGlCode().equals(GLCODE_FOR_TAXREBATE)) rebateAmount = rebateAmount.add(rcptAccInfo.getDrAmount()); for (final EgBillDetails billDet : egBill.getEgBillDetails()) { final String[] desc = billDet.getDescription().split("-", 2); if (billDet.getOrderNo() == 1) { receiptAmountInfo.setInstallmentFrom(desc[1]); if (billDetails.size() == 1) { receiptAmountInfo.setInstallmentTo(desc[1]); break; } } if (billDetails.size() > 1) if (billDet.getCrAmount().compareTo(BigDecimal.ZERO) == 1 && reciptDetailList.get(0).getOrdernumber().equals(Long.valueOf(billDet.getOrderNo()))) { receiptAmountInfo.setInstallmentTo(desc[1]); } } receiptAmountInfo.setCurrentInstallmentAmount(currentInstallmentAmount); receiptAmountInfo.setLatePaymentCharges(latePaymentCharges); receiptAmountInfo.setArrearsAmount(arrearAmount); receiptAmountInfo.setCurrentCess(currLibCess); receiptAmountInfo.setArrearCess(arrearLibCess); receiptAmountInfo.setReductionAmount(rebateAmount); return receiptAmountInfo; } public void setPtBillServiceImpl(PTBillServiceImpl ptBillServiceImpl) { this.ptBillServiceImpl = ptBillServiceImpl; } }