/*
* 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.demand.integration;
import org.apache.log4j.Logger;
import org.egov.InvalidAccountHeadException;
import org.egov.collection.entity.ReceiptDetail;
import org.egov.collection.integration.models.BillReceiptInfo;
import org.egov.collection.integration.models.ReceiptAccountInfo;
import org.egov.collection.integration.models.ReceiptInstrumentInfo;
import org.egov.collection.integration.services.BillingIntegrationService;
import org.egov.commons.Installment;
import org.egov.commons.dao.InstallmentHibDao;
import org.egov.demand.dao.DemandGenericDao;
import org.egov.demand.dao.EgBillDao;
import org.egov.demand.dao.EgBillDetailsDao;
import org.egov.demand.dao.EgBillReceiptDao;
import org.egov.demand.dao.EgdmCollectedReceiptDao;
import org.egov.demand.model.BillReceipt;
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.demand.model.EgdmCollectedReceipt;
import org.egov.demand.utils.DemandConstants;
import org.egov.infra.admin.master.entity.Module;
import org.egov.infra.exception.ApplicationRuntimeException;
import org.hibernate.ObjectNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* This class is used to persist Bills with Collection Details(i.e received from ErpCollection) .This is used for the integration
* of Collections .
*
* @author Sathish Reddy K
*
*/
public abstract class TaxCollection implements BillingIntegrationService {
private static final Logger LOGGER = Logger.getLogger(TaxCollection.class);
@Autowired
private EgBillDao egBillDAO;
@Autowired
private EgBillDetailsDao egBillDetailsDAO;
@Autowired
private EgBillReceiptDao egBillReceiptDAO;
@Autowired
private EgdmCollectedReceiptDao egdmCollectedReceiptDAO;
@Autowired
private DemandGenericDao demandGenericDAO;
@Autowired
private InstallmentHibDao installmentHibDao;
public TaxCollection() {
}
/**
*
* Called when there is a collection happened or a receipt cancelled or a cheque bounced.
*
* @param java .util.Set<BillReceiptInfo> billReceipts
*
* @return java.lang.Boolean status(If any error occurred then status is returned as false.)
*/
@Override
public void updateReceiptDetails(final Set<BillReceiptInfo> billReceipts) throws ApplicationRuntimeException{
LOGGER.debug("updateReceiptDetails : Receipt Details Updating Started...");
for (final BillReceiptInfo bri : billReceipts)
try {
LOGGER.debug("-----updateReceiptDetails is called----------------");
updateNewReceipt(bri);
} catch (final Exception e) {
LOGGER.error("Exception while updating receipt details in billing system", e);
throw new ApplicationRuntimeException("", e);
}
LOGGER.debug("updateReceiptDetails : Receipt Details Updating Finished...");
}
/**
* creates or updates the EgBillReceipt ,EgBill,EgBillDetails , EgDemand and EgDemandDetails with Collection Details. Called
* when there is a collection happened or a receipt cancelled or a cheque bounced
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
*/
private void updateNewReceipt(final BillReceiptInfo bri) throws InvalidAccountHeadException,
ObjectNotFoundException {
LOGGER.info("-----updateNewReceipt is called----------------");
linkBillToReceipt(bri);
updateBillDetails(bri);
updateDemandDetails(bri);
LOGGER.info("--end of Updation of all the Demand ");
}
/**
* Creates or updates the EgBill Receipt table depending upon the transaction i.e for Normal Collection new row will be
* created and for receipt cancel table gets updated
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
* @param org .egov.lib.rjbac.user.User user
*
* @return org.egov.demand.model.BillReceipt
*
* @throws InvalidAccountHeadException
*/
BillReceipt linkBillToReceipt(final BillReceiptInfo bri) throws InvalidAccountHeadException,
ObjectNotFoundException {
LOGGER.debug("-----Start of linkBillToReceipt----------------");
BillReceipt billRecpt = null;
if (bri == null)
throw new ApplicationRuntimeException(" BillReceiptInfo Object is null ");
final EgBill egBill = egBillDAO.findById(Long.valueOf(bri.getBillReferenceNum()),
false);
if (egBill == null)
throw new ApplicationRuntimeException(" EgBill Object is null for the Bill Number"
+ bri.getBillReferenceNum());
final List<EgBillDetails> billDetList = egBillDetailsDAO.getBillDetailsByBill(egBill);
final BigDecimal totalCollectedAmt = calculateTotalCollectedAmt(bri, billDetList);
if (bri.getEvent() == null)
throw new ApplicationRuntimeException(" Event in BillReceiptInfo Object is Null");
if (bri.getEvent().equals(BillingIntegrationService.EVENT_RECEIPT_CREATED)) {
billRecpt = prepareBillReceiptBean(bri, egBill, totalCollectedAmt);
egBillReceiptDAO.create(billRecpt);
} else if (bri.getEvent().equals(BillingIntegrationService.EVENT_RECEIPT_CANCELLED))
billRecpt = updateBillReceiptForCancellation(bri, egBill, totalCollectedAmt);
LOGGER.debug("-----End of linkBillToReceipt----------------");
return billRecpt;
}
/**
* Method will be called when a Collection has happened .Here in EGBillReceipt table a row will be created with the billnumber
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
* @param org .egov.demand.model.EgBill egBill
* @param java .math.BigDecimal totalCollAmt totalCollectedAmt
*
* @return org.egov.demand.model.BillReceipt billRecpt
*
*
*/
BillReceipt prepareBillReceiptBean(final BillReceiptInfo bri, final EgBill egBill,
final BigDecimal totalCollectedAmt) {
BillReceipt billRecpt = null;
if (bri != null && egBill != null && totalCollectedAmt != null) {
billRecpt = new BillReceipt();
billRecpt.setBillId(egBill);
billRecpt.setReceiptAmt(totalCollectedAmt);
billRecpt.setReceiptNumber(bri.getReceiptNum());
billRecpt.setReceiptDate(bri.getReceiptDate());
billRecpt.setCollectionStatus(bri.getReceiptStatus().getCode());
billRecpt.setCreatedBy(bri.getCreatedBy());
billRecpt.setModifiedBy(bri.getModifiedBy());
billRecpt.setCreatedDate(new Date());
billRecpt.setModifiedDate(new Date());
billRecpt.setIsCancelled(Boolean.FALSE);
}
return billRecpt;
}
/**
* Method will be called when a payment Receipt is Cancelled .Here the EGBillReceipt table is updated with the cancelled
* receipt amount and mark the receipt status i.e is_cancelled to true,but in case of cheque Bounce the status of the receipt
* will not be changed.
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo bri
* @param org .egov.demand.model.EgBill egBill
* @param java .math.BigDecimal totalCollAmt totalCollectedAmt
*
* @return org.egov.demand.model.BillReceipt billRecpt
*
*
*/
BillReceipt updateBillReceiptForCancellation(final BillReceiptInfo bri, final EgBill egBill,
final BigDecimal totalCollectedAmt) {
BillReceipt billRecpt = null;
if (bri == null)
throw new ApplicationRuntimeException(" BillReceiptInfo Object is null ");
if (egBill != null && totalCollectedAmt != null) {
billRecpt = egBillReceiptDAO.getBillReceiptByEgBill(egBill);
if (billRecpt == null)
throw new ApplicationRuntimeException(" Bill receipt Object is null for the EgBill "
+ egBill.getId());
if (bri.getEvent().equals(BillingIntegrationService.EVENT_RECEIPT_CANCELLED))
billRecpt.setIsCancelled(Boolean.TRUE);
billRecpt.setReceiptAmt(totalCollectedAmt.subtract(billRecpt.getReceiptAmt()));
} else
throw new ApplicationRuntimeException(" EgBill Object is null for the Bill Number"
+ bri.getBillReferenceNum() + "in updateBillReceiptForCancellation method");
return billRecpt;
}
/**
* API to update the bill details with the amount paid depending upon the event of the payment i.e payment or receipt
* cancellation or cheque bounce In case of Payment ,new EgBill is created and in case of cheque bounce only the EgBill is
* updated with necessary details(done for the DCB implementation)
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
*
* @return EgBill
*
* @throws InvalidAccountHeadException
*/
EgBill updateBillDetails(final BillReceiptInfo bri) throws InvalidAccountHeadException {
LOGGER.debug("-----Start of updateBillDetails----------------");
EgBill egBill = null;
if (bri == null)
throw new ApplicationRuntimeException(" BillReceiptInfo Object is null ");
egBill = egBillDAO.findById(Long.valueOf(bri.getBillReferenceNum()), false);
final List<EgBillDetails> billDetList = egBillDetailsDAO.getBillDetailsByBill(egBill);
if (bri.getEvent() != null
&& bri.getEvent().equals(BillingIntegrationService.EVENT_RECEIPT_CREATED)) {
final BigDecimal totalCollectedAmt = calculateTotalCollectedAmt(bri, billDetList);
egBill = updateBill(bri, egBill, totalCollectedAmt);
} else if (bri.getEvent() != null
&& bri.getEvent().equals(BillingIntegrationService.EVENT_INSTRUMENT_BOUNCED))
egBill = updateBillForChqBounce(bri, egBill, getTotalChequeAmt(bri));
else if (bri.getEvent() != null && bri.getEvent().equals(BillingIntegrationService.EVENT_RECEIPT_CANCELLED))
updateBillByCancelledRct(bri, egBill, bri.getTotalAmount());
LOGGER.debug("-----End of updateBillDetails----------------");
return egBill;
}
/**
* Method will be called when a Cheque is Bounced .Here the EGBill and EgBillDetails tables are updated with the cancelled
* Cheque amount.
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
* @param org .egov.demand.model.EgBill egBill
* @param java .math.BigDecimal totalCollAmt totalCollectedAmt
*
* @return org.egov.demand.model.EgBill egBill
*
*
*/
public EgBill updateBillForChqBounce(final BillReceiptInfo bri, final EgBill egBill, final BigDecimal totalChqAmt) {
final BigDecimal zeroVal = BigDecimal.ZERO;
if (totalChqAmt != null && !totalChqAmt.equals(zeroVal) && egBill != null) {
final List<EgBillDetails> billList = new ArrayList<EgBillDetails>(egBill.getEgBillDetails());
// Reversed the list because the knocking off the amount should
// start from current Installment to least Installment.
Collections.reverse(billList);
BigDecimal carry = totalChqAmt;
for (final EgBillDetails billdet : billList) {
BigDecimal balanceAmt = BigDecimal.ZERO;
BigDecimal remAmount = BigDecimal.ZERO;
balanceAmt = getEgBillDetailCollection(billdet);
if (balanceAmt != null && balanceAmt.compareTo(zeroVal) > 0) {
if (carry.compareTo(zeroVal) > 0
&& carry.subtract(balanceAmt).compareTo(zeroVal) > 0) {
carry = carry.subtract(balanceAmt);
remAmount = balanceAmt;
} else if (carry.compareTo(zeroVal) > 0
&& carry.subtract(balanceAmt).compareTo(zeroVal) <= 0) {
remAmount = carry;
carry = BigDecimal.ZERO;
}
if (remAmount.compareTo(zeroVal) > 0) {
billdet.setCollectedAmount(remAmount);
egBillDetailsDAO.update(billdet);
}
}
}
egBill.setTotalCollectedAmount(totalChqAmt);
egBillDAO.update(egBill);
}
return egBill;
}
/**
* Method will be called when a receipt is cancelled .Here the EGBill and EgBillDetails tables are updated with the cancelled
* amount and the mark the receipt status as cancelled in Eg_BillReceipt table. If the Glcode which exists in EgBillDetails is
* not matching with the Glcode the ErpCollection send ,then an exception will be raised(InvalidAccountHeadException).
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
* @param org .egov.demand.model.EgBill
* @param java .math.BigDecimal totalCollAmt
*
* @return org.egov.demand.model.EgBill egBill
*
* @throws InvalidAccountHeadException
*
*/
EgBill updateBillByCancelledRct(final BillReceiptInfo bri, final EgBill egBill, final BigDecimal totalCollectedAmt)
throws InvalidAccountHeadException {
if (bri != null && egBill != null && totalCollectedAmt != null) {
final List<EgBillDetails> billList = new ArrayList<EgBillDetails>(egBill.getEgBillDetails());
// Reversed the list because the knocking off the amount should
// start from current up to least installment.
Collections.reverse(billList);
for (final EgBillDetails billDet : billList) {
final BigDecimal balanceAmt = getEgBillDetailCollection(billDet);
Boolean glCodeExist = false;
for (final ReceiptAccountInfo acctDet :bri.getAccountDetails())
if (billDet.getGlcode().equals(acctDet.getGlCode())) {
glCodeExist = true;
billDet.setCollectedAmount(acctDet.getCrAmount().subtract(balanceAmt));
egBillDetailsDAO.update(billDet);
}
if (!glCodeExist)
throw new InvalidAccountHeadException("GlCode does not exist for "
+ billDet.getGlcode());
}
egBill.setTotalCollectedAmount(totalCollectedAmt);
egBillDAO.update(egBill);
}
return egBill;
}
/**
* Here the EGBill and EgBillDetails tables are updated with the collected amount. If the Glcode which exists in EgBillDetails
* is not matching with the Glcode the ErpCollection send ,then an exception will be raised(InvalidAccountHeadException).
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
* @param org .egov.demand.model.EgBill egBill
* @param java .math.BigDecimal totalCollAmt totalCollectedAmt
*
* @return org.egov.demand.model.EgBill egBill
*
* @throws InvalidAccountHeadException
*
*/
private EgBill updateBill(final BillReceiptInfo bri, final EgBill egBill, final BigDecimal totalCollectedAmt)
throws InvalidAccountHeadException {
if (bri != null) {
for (final EgBillDetails billDet : egBill.getEgBillDetails()) {
Boolean glCodeExist = false;
for (final ReceiptAccountInfo acctDet : bri.getAccountDetails()){
if(billDet.getGlcode().equals(acctDet.getGlCode()))
glCodeExist = true;
if (billDet.getOrderNo()!=null && acctDet.getOrderNumber()!=null && (billDet.getOrderNo().equals(acctDet.getOrderNumber().intValue())) &&
acctDet.getCrAmount() !=null && acctDet.getCrAmount().compareTo(BigDecimal.ZERO)>0) {
BigDecimal amtCollected = billDet.getCollectedAmount();
if (amtCollected == null)
amtCollected = BigDecimal.ZERO;
billDet.setCollectedAmount(acctDet.getCrAmount().subtract(amtCollected));
egBillDetailsDAO.update(billDet);
break;
}
else
{
billDet.setCollectedAmount(BigDecimal.ZERO);
egBillDetailsDAO.update(billDet);
}
}
if (!glCodeExist)
throw new InvalidAccountHeadException("GlCode does not exist for "
+ billDet.getGlcode());
}
egBill.setTotalCollectedAmount(totalCollectedAmt);
egBillDAO.update(egBill);
}
return egBill;
}
/**
* Called to calculate the total Receipt Amount i,e the amount which has been paid for the Bill From the BillReceiptInfo the
* amount will be calculated. If the Glcode which exists in EgBillDetails is not matching with the Glcode the ErpCollection
* send ,then an exception will be raised(InvalidAccountHeadException).
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
* @param java .util.List<EgBillDetails> billDetList
*
* @return java.math.BigDecimal totalCollAmt
*
* @throws InvalidAccountHeadException
*/
public BigDecimal calculateTotalCollectedAmt(final BillReceiptInfo bri,
final List<EgBillDetails> billDetList) throws InvalidAccountHeadException {
return bri.getTotalAmount();
}
/**
* Here we get the total cheque Amount (i.e the Amount in which the cheque gets bounced.)
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
*
* @return java.math.BigDecimal balanceAmt
*
* @exception org.egov.infra.exception.ApplicationRuntimeException
*/
public BigDecimal getTotalChequeAmt(final BillReceiptInfo bri) {
BigDecimal totalCollAmt = BigDecimal.ZERO;
try {
if (bri != null)
for (final ReceiptInstrumentInfo rctInst : bri.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;
}
/**
* Here we get the Bill DCB(Bill Amount - Collected amount).
*
* @param org .egov.demand.model.EgBillDetails billdet
*
* @return java.math.BigDecimal balanceAmt
*/
public BigDecimal getEgBillDetailCollection(final EgBillDetails billdet) {
BigDecimal collectedAmt = billdet.getCollectedAmount();
if (billdet.getCollectedAmount() == null)
collectedAmt = BigDecimal.ZERO;
return collectedAmt;
}
/**
* EgDemand and EgdemandDetails updation is a client specific .. So the Client which extends this abstract class needs to
* implement(override) this method.
*
* For Ex :- In PTIS Module: there will be only a base tax(basic Demand) in general, But in COCPTIS the base tax is divided
* into 4 individual taxes .
*
* If any info which is required is missed out then an exception needs to be raised with proper message
*
*
* @param org .egov.infstr.collections.integration.models.BillReceiptInfo
*
*/
public abstract void updateDemandDetails(BillReceiptInfo bri);
/**
* The module that defines the billing system.
*
* @return
*/
protected abstract Module module();
/**
* For a given date, finds the installment that contains it.
*
* @param date
* @return
*/
protected Installment getInstallmentForDate(final Date date) {
return installmentHibDao.getInsatllmentByModuleForGivenDate(module(),
date);
}
/**
* Finds the currently active installment.
*
* @return
*/
protected Installment getCurrentInstallment() {
return getInstallmentForDate(new Date());
}
/**
* Gets a list of all installments for the billing system.
*
* @return
*/
protected List<Installment> getAllInstallments() {
return installmentHibDao.getInsatllmentByModule(module());
}
/**
* Finds the demand-reason-master having the given code.
*
* @param code
* @return
*/
protected EgDemandReasonMaster getDemandReasonMaster(final String code) {
return demandGenericDAO.getDemandReasonMasterByCode(code, module());
}
/**
* Finds the demand-detail for the given installment and reason.
*
* @param egDemand
* @param instl
* @param code
* @return
*/
public EgDemandDetails getDemandDetail(final EgDemand egDemand, final Installment instl, final String code) {
EgDemandDetails dmdDet = null;
final List<EgDemandDetails> dmdDetList = demandGenericDAO.getDmdDetailList(egDemand, instl,
module(), getDemandReasonMaster(code));
if (!dmdDetList.isEmpty())
dmdDet = dmdDetList.get(0);
return dmdDet;
}
/**
* Finds the current reason for the given master and category.
*
* @param categoryCode
* @param reasonMasterCode
* @return
*/
protected EgDemandReason getCurrentReason(final String categoryCode, final String reasonMasterCode) {
final EgDemandReason reason = demandGenericDAO.getEgDemandReasonByCodeInstallmentModule(
reasonMasterCode, getCurrentInstallment(), module(), categoryCode);
return reason;
}
/**
* Persists the Receipt Details with the EgDemandDetail id as the reference and the status as active.
*
* @param egDemandDetails
* @param billRcptInfo
* @return egDmCollectedReceipt
*/
protected EgdmCollectedReceipt persistCollectedReceipts(final EgDemandDetails egDemandDetails,
final String receiptNumber, final BigDecimal receiptAmount, final Date receiptDate,
final BigDecimal reasonAmount) {
final EgdmCollectedReceipt egDmCollectedReceipt = new EgdmCollectedReceipt();
egDmCollectedReceipt.setReceiptNumber(receiptNumber);
egDmCollectedReceipt.setReceiptDate(receiptDate);
egDmCollectedReceipt.setAmount(receiptAmount);
egDmCollectedReceipt.setReasonAmount(reasonAmount);
egDmCollectedReceipt.setStatus(DemandConstants.NEWRECEIPT);
egDmCollectedReceipt.setEgdemandDetail(egDemandDetails);
egdmCollectedReceiptDAO.create(egDmCollectedReceipt);
return egDmCollectedReceipt;
}
/**
* When receipt is cancelled the status of the receipt in EgdmCollectedReceipts is updated as Cancelled.
*
* @param egDmCollectedReceipt
*/
protected void updateReceiptStatusWhenCancelled(final String receiptNumber) {
final List<EgdmCollectedReceipt> egdmCollectedReceipts = demandGenericDAO
.getAllEgdmCollectedReceipts(receiptNumber);
if (egdmCollectedReceipts != null && !egdmCollectedReceipts.isEmpty())
for (final EgdmCollectedReceipt egDmCollectedReceipt : egdmCollectedReceipts) {
egDmCollectedReceipt.setStatus(DemandConstants.CANCELLED_RECEIPT);
egDmCollectedReceipt.setUpdatedTime(new Date());
egdmCollectedReceiptDAO.update(egDmCollectedReceipt);
}
}
@Override
public void apportionPaidAmount(final String billReferenceNumber, final BigDecimal actualAmountPaid,
final ArrayList<ReceiptDetail> receiptDetails) {
apportionCollection(billReferenceNumber, actualAmountPaid, receiptDetails);
}
/**
* Billing system will implement this method when the billing system send "<enablebillapportioning> as true to
* Collection System in the bill-xml.
*
* @param billRefNo Bill Reference Number of the bill send by billing system
* @param amtPaid Acutal amount paid at the counter
* @param receiptDetails List of ReceiptDetails object associated with this bill
* @return void
*/
public void apportionCollection(final String billRefNo, final BigDecimal amtPaid,
final List<ReceiptDetail> receiptDetails) {
return;
}
}