/*
* 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.adtax.service.collection;
import org.apache.log4j.Logger;
import org.egov.adtax.entity.AdvertisementPermitDetail;
import org.egov.adtax.entity.AgencyWiseCollection;
import org.egov.adtax.entity.AgencyWiseCollectionDetail;
import org.egov.adtax.repository.AdvertisementPermitDetailRepository;
import org.egov.adtax.service.AdvertisementDemandService;
import org.egov.adtax.service.AdvertisementPermitDetailService;
import org.egov.adtax.service.AdvertisementPermitDetailUpdateIndexService;
import org.egov.adtax.service.AdvertisementService;
import org.egov.adtax.service.AgencyWiseCollectionService;
import org.egov.adtax.utils.constants.AdvertisementTaxConstants;
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.demand.dao.EgBillDao;
import org.egov.demand.integration.TaxCollection;
import org.egov.demand.model.EgBill;
import org.egov.demand.model.EgDemand;
import org.egov.demand.model.EgDemandDetails;
import org.egov.infra.admin.master.entity.Module;
import org.egov.infra.exception.ApplicationRuntimeException;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Service
@Transactional(readOnly = true)
public class AdvertisementTaxCollection extends TaxCollection {
private static final Logger LOGGER = Logger.getLogger(AdvertisementTaxCollection.class);
@Autowired
private EgBillDao egBillDAO;
@Autowired
private AdvertisementDemandService advertisementDemandService;
@Autowired
AgencyWiseCollectionService agencyWiseCollectionService;
@PersistenceContext
private EntityManager entityManager;
@Autowired
private AdvertisementService advertisementService;
@Autowired
private AdvertisementPermitDetailRepository advertisementPermitDetailRepository;
@Autowired
private AdvertisementPermitDetailService advertisementPermitDetailService;
@Autowired
private AdvertisementPermitDetailUpdateIndexService advertisementPermitDetailUpdateIndexService;
public Session getCurrentSession() {
return entityManager.unwrap(Session.class);
}
@Override
public List<ReceiptDetail> reconstructReceiptDetail(final String billReferenceNumber,
final BigDecimal actualAmountPaid, final List<ReceiptDetail> receiptDetailList) {
return null;
}
/**
* Collection will be possible either by agency wise or individual hoarding wise. Calling common api to update demand. If
* demand present in agency wise collection object, then we will consider collection happened by Agency wise.
*/
@Override
@Transactional
public void updateDemandDetails(final BillReceiptInfo billRcptInfo) {
final BigDecimal totalAmount = billRcptInfo.getTotalAmount();
if (LOGGER.isDebugEnabled())
LOGGER.debug("updateDemandDetails : Demand updation for advertisement started. ");
EgDemand demand = getDemandByBillReferenceNumber(Long.valueOf(billRcptInfo.getBillReferenceNum()));
final AgencyWiseCollection agencyWiseCollection = agencyWiseCollectionService.getAgencyWiseCollectionByDemand(demand);
if (agencyWiseCollection != null) {
agencyWiseCollection.setAmountCollected(Boolean.TRUE);
/*
* We are using demandupdated flag to check whether demand updated or not. We can use scheduler to update these
* records in bulk if required.
*/
agencyWiseCollection.setDemandUpdated(Boolean.TRUE);
if (billRcptInfo.getEvent().equals(EVENT_RECEIPT_CREATED))
updateAgencyWiseCollectionOnCreate(billRcptInfo, agencyWiseCollection, totalAmount);
else if (billRcptInfo.getEvent().equals(EVENT_RECEIPT_CANCELLED))
updateAgencyWiseCollectionOnCancel(demand, billRcptInfo, agencyWiseCollection, totalAmount);
} else {
demand = generalDemandUpdationForAdvertisement(billRcptInfo, totalAmount);
updateWorkflowState(demand);
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("updateDemandDetails : Demand updation processed. ");
}
/**
* Iterate each agency detail and update collected amount.Assumption is full amount will be collected from collection system.
* Penalty we need to add as fresh entry in demand detail.
* @param totalAmount
*/
@Transactional
private void updateAgencyWiseCollectionOnCreate(final BillReceiptInfo billRcptInfo,
final AgencyWiseCollection agencyWiseCollection, final BigDecimal totalAmount) {
/**
* Whether we will adjust arrears of all demand first ? or financial year wise first.. and then last financial year ?
* or clear individual advertisement wise ?
* make it configurable.
* final HashMap<EgDemandReason, BigDecimal> demandReasonWiseList;
*
* 1. Get List of agencywise collections group by demandid. Sort by arrears,tax and based on year.
* 2. Check whether full amount we are collected for this demand. If yes check whether record in workflow, then close workflow.
* 3. If no, mean.. amount remaining is less than amount to be collected for advertisement. Here we need to collect arrears , tax, encroachment
* fee in order. The percentage of penalty we need to get based on demand detail and penalty calculation date.
* Pending amount > balance + penalty then use that demand detail.
* else
* for pending amount decide penalty to be adjust + tax to be adjust.
*
* Check remaining amount is required to adjust with other demand detail.
* Add penalty to map by financial year and update demand detail on final stage.
*/
// group by arrears, last financial year and current year
// group by demandid return values.
for (final AgencyWiseCollectionDetail agencyDtl : agencyWiseCollection.getAgencyWiseCollectionDetails()) {
//if (agencyDtl.getDemandDetail() != null ) {
if (agencyDtl.getDemandDetail() != null &&
!AdvertisementTaxConstants.DEMANDREASON_PENALTY.equalsIgnoreCase(agencyDtl.getDemandreason()
.getEgDemandReasonMaster().getReasonMaster())) {
agencyDtl.getDemandDetail().setAmtCollected(
agencyDtl.getDemandDetail().getAmtCollected().add(agencyDtl.getAmount()));
//TODO: IF PENALTY IS PART OF DEMAND REASON, THEN WE NEED TO HANDLE IN THIS LOOP. HERE AMOUNT COLLECTED WILL BE MORE IN THAT CASE.
persistCollectedReceipts(agencyDtl.getDemandDetail(), billRcptInfo.getReceiptNum(), totalAmount,
billRcptInfo.getReceiptDate(), agencyDtl.getAmount());
agencyDtl.getDemand().addCollected(agencyDtl.getAmount());
} else {
final List<EgDemandDetails> penaltyDmtDtails = advertisementDemandService
.getDemandDetailByPassingDemandDemandReason(agencyDtl.getDemand(),
agencyDtl.getDemandreason());
/*
* Check whether penalty reason already present in current demand.
*/
if (penaltyDmtDtails != null && penaltyDmtDtails.size() > 0) {
penaltyDmtDtails.get(0).setAmount(
penaltyDmtDtails.get(0).getAmount().add(agencyDtl.getAmount()));
penaltyDmtDtails.get(0).setAmtCollected(
penaltyDmtDtails.get(0).getAmtCollected().add(agencyDtl.getAmount()));
persistCollectedReceipts(penaltyDmtDtails.get(0), billRcptInfo.getReceiptNum(), totalAmount,
billRcptInfo.getReceiptDate(), agencyDtl.getAmount());
} else {
/*
* Create new demand detail entry eg:for penalty
*/
final EgDemandDetails demandDetail = advertisementDemandService.createDemandDetails(
agencyDtl.getAmount(), agencyDtl.getDemandreason(), agencyDtl.getAmount());
agencyDtl.getDemand().addEgDemandDetails(demandDetail);
agencyDtl.getDemand().addBaseDemand(agencyDtl.getAmount());
agencyDtl.getDemand().addCollected(agencyDtl.getAmount());
getCurrentSession().flush();
persistCollectedReceipts(demandDetail, billRcptInfo.getReceiptNum(), totalAmount,
billRcptInfo.getReceiptDate(), agencyDtl.getAmount());
}
}
// }
/**
* If for new application, commissioner approved record and payment collection is pending. If user using agency wise
* collection screen then we need to update workflow.
*/
if (agencyDtl.getDemand() != null)
updateWorkflowState(agencyDtl.getDemand());
}
}
/**
* @param billRcptInfo
* @param totalAmount
* @return
*/
@Transactional
private EgDemand generalDemandUpdationForAdvertisement(final BillReceiptInfo billRcptInfo, final BigDecimal totalAmount) {
final EgDemand demand = getDemandByBillReferenceNumber(Long.valueOf(billRcptInfo.getBillReferenceNum()));
final String indexNo = ((BillReceiptInfoImpl) billRcptInfo).getReceiptMisc().getReceiptHeader()
.getConsumerCode();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("updateDemandDetails : Demand before proceeding : " + demand);
LOGGER.debug("updateDemandDetails : collection back update started for property : " + indexNo
+ " and receipt event is " + billRcptInfo.getEvent() + ". Total Receipt amount is." + totalAmount
+ " with receipt no." + billRcptInfo.getReceiptNum());
}
if (billRcptInfo.getEvent().equals(EVENT_INSTRUMENT_BOUNCED))
updateReceiptStatusWhenCancelled(billRcptInfo.getReceiptNum());
else if (billRcptInfo.getEvent().equals(EVENT_RECEIPT_CREATED))
updateDemandWithcollectdTaxDetails(demand, billRcptInfo, EVENT_RECEIPT_CREATED, totalAmount);
else if (billRcptInfo.getEvent().equals(EVENT_RECEIPT_CANCELLED))
updateDemandWithcollectdTaxDetails(demand, billRcptInfo, EVENT_RECEIPT_CANCELLED, totalAmount);
// updateReceiptStatusWhenCancelled(billRcptInfo.getReceiptNum());
return demand;
}
/**
* @param demand
* @param billReceiptInfo
* @param eventType
* @param totalAmount
* @return
*/
@Transactional
private BigDecimal updateDemandWithcollectdTaxDetails(final EgDemand demand, final BillReceiptInfo billReceiptInfo,
final String eventType, final BigDecimal totalAmount) {
BigDecimal totalAmountCollected = BigDecimal.ZERO;
for (final ReceiptAccountInfo recAccInfo : billReceiptInfo.getAccountDetails()) {
String demandMasterReasonDesc = null;
String financialYearDesc = null;
if (recAccInfo.getDescription() != null) {
demandMasterReasonDesc = recAccInfo
.getDescription()
.substring(
0,
recAccInfo.getDescription().indexOf(
AdvertisementTaxConstants.COLL_RECEIPTDETAIL_DESC_PREFIX))
.trim();
financialYearDesc=recAccInfo
.getDescription()
.substring(
recAccInfo.getDescription().indexOf(
AdvertisementTaxConstants.COLL_RECEIPTDETAIL_DESC_PREFIX)+AdvertisementTaxConstants.COLL_RECEIPTDETAIL_DESC_PREFIX.length())
.trim();
if (eventType.equals(EVENT_RECEIPT_CREATED))
totalAmountCollected = totalAmountCollected.add(createOrUpdateDemandDetails(demandMasterReasonDesc,financialYearDesc,
demand, billReceiptInfo, recAccInfo, totalAmount));
}
}
LOGGER.info("Demand before updateDemandDetails() processing: " + demand.getAmtCollected() + demand);
if (eventType.equals(EVENT_RECEIPT_CANCELLED)) {
cancelBill(Long.valueOf(billReceiptInfo.getBillReferenceNum()));
demand.setAmtCollected(demand.getAmtCollected().subtract(totalAmount));
updateDmdDetForRcptCancel(demand, billReceiptInfo);
}
demand.setModifiedDate(new Date());
return totalAmountCollected;
}
/* Iterate each agency detail and update collected amount. Assumption is full amount will be collected from collection system.
** @param totalAmount
*/
@Transactional
private void updateAgencyWiseCollectionOnCancel(EgDemand demand, final BillReceiptInfo billRcptInfo,
final AgencyWiseCollection agencyWiseCollection, final BigDecimal totalAmount) {
for (final AgencyWiseCollectionDetail agencyDtl : agencyWiseCollection.getAgencyWiseCollectionDetails()) {
if (agencyDtl.getDemandDetail() != null) {
updateCancelledDemandDetailAmount(agencyDtl.getDemandDetail(),agencyDtl.getDemand(),agencyDtl.getAmount());
}else
{
final List<EgDemandDetails> demandDetail = advertisementDemandService
.getDemandDetailByPassingDemandDemandReason(agencyDtl.getDemand(),
agencyDtl.getDemandreason());
if (demandDetail != null && demandDetail.size() > 0) {
updateCancelledDemandDetailAmount(demandDetail.get(0),demandDetail.get(0).getEgDemand(),agencyDtl.getAmount());
}//TODO: THROW error if demand not found.
}
}
cancelBill(Long.valueOf(billRcptInfo.getBillReferenceNum()));
demand.setAmtCollected(demand.getAmtCollected().subtract(totalAmount));
updateReceiptStatusWhenCancelled(billRcptInfo.getReceiptNum());
}
@Transactional
private void updateCancelledDemandDetailAmount(EgDemandDetails demandDtl,EgDemand demand, BigDecimal amount) {
if (demandDtl!=null && AdvertisementTaxConstants.DEMANDREASON_PENALTY.equalsIgnoreCase(demandDtl.getEgDemandReason()
.getEgDemandReasonMaster().getReasonMaster()))
{
demand.setBaseDemand(demand.getBaseDemand().subtract(amount));
demandDtl.setAmount(demandDtl.getAmount().subtract(amount));
}
if (demandDtl!=null && demandDtl.getAmtCollected().compareTo(amount) < 0)
throw new ApplicationRuntimeException(
"updateDmdDetForRcptCancel : Exception while updating cancel receipt, "
+ "to be deducted amount " + amount
+ " is greater than the collected amount " + demandDtl.getAmtCollected()
);
demandDtl.setAmtCollected(demandDtl.getAmtCollected().subtract(amount));
demand.setAmtCollected(demand.getAmtCollected().subtract(amount));
}
/**
* @param demand
* @param billRcptInfo
*/
@Transactional
private void updateDmdDetForRcptCancel(final EgDemand demand, final BillReceiptInfo billRcptInfo) {
LOGGER.debug("Entering method updateDmdDetForRcptCancel");
String demandMasterReasonDesc = null;
String financialYearDesc = null;
for (final ReceiptAccountInfo rcptAccInfo : billRcptInfo.getAccountDetails())
if (rcptAccInfo.getCrAmount() != null && rcptAccInfo.getCrAmount().compareTo(BigDecimal.ZERO) == 1
&& !rcptAccInfo.getIsRevenueAccount()) {
if (rcptAccInfo.getDescription() != null) {
demandMasterReasonDesc = rcptAccInfo
.getDescription().substring(0,rcptAccInfo.getDescription().indexOf(
AdvertisementTaxConstants.COLL_RECEIPTDETAIL_DESC_PREFIX))
.trim();
financialYearDesc=rcptAccInfo.getDescription().substring(
rcptAccInfo.getDescription().indexOf(
AdvertisementTaxConstants.COLL_RECEIPTDETAIL_DESC_PREFIX)+AdvertisementTaxConstants.COLL_RECEIPTDETAIL_DESC_PREFIX.length())
.trim();
for (final EgDemandDetails demandDetail : demand.getEgDemandDetails()){
if (demandMasterReasonDesc.equalsIgnoreCase(demandDetail.getEgDemandReason()
.getEgDemandReasonMaster().getReasonMaster()) && financialYearDesc!=null &&
demandDetail.getEgDemandReason()
.getEgInstallmentMaster().getFinYearRange().equalsIgnoreCase(financialYearDesc)
) {
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);
if (AdvertisementTaxConstants.DEMANDREASON_PENALTY.equalsIgnoreCase(demandMasterReasonDesc))
demandDetail.setAmount(demandDetail.getAmount().subtract(rcptAccInfo.getCrAmount()));
demandDetail
.setAmtCollected(demandDetail.getAmtCollected().subtract(rcptAccInfo.getCrAmount()));
}
}
}
}
updateReceiptStatusWhenCancelled(billRcptInfo.getReceiptNum());
LOGGER.debug("Exiting method updateDmdDetForRcptCancel");
}
@Transactional
private void cancelBill(final Long billId) {
if (billId != null) {
final EgBill egBill = egBillDAO.findById(billId, false);
egBill.setIs_Cancelled("Y");
}
}
/**
* @param demandMasterReasonDesc
* @param financialYearDesc
* @param demand
* @param billReceiptInfo
* @param recAccInfo
* @param totalAmount
* @return
*/
@Transactional
private BigDecimal createOrUpdateDemandDetails(final String demandMasterReasonDesc, String financialYearDesc, final EgDemand demand,
final BillReceiptInfo billReceiptInfo, final ReceiptAccountInfo recAccInfo, final BigDecimal totalAmount) {
BigDecimal totalAmountCollected = BigDecimal.ZERO;
Boolean demandReasonPartOfDemand = false;
if (recAccInfo.getCrAmount() != null && recAccInfo.getCrAmount().compareTo(BigDecimal.ZERO) > 0) {
// updating the existing demand detail..
for (final EgDemandDetails demandDetail : demand.getEgDemandDetails())
if (demandDetail.getEgDemandReason() != null
&& demandDetail.getEgDemandReason().getEgDemandReasonMaster() != null
&& demandDetail.getEgDemandReason().getEgDemandReasonMaster().getReasonMaster().trim()
.equalsIgnoreCase(demandMasterReasonDesc)
&& financialYearDesc != null
&& financialYearDesc.equalsIgnoreCase(demandDetail.getEgDemandReason().getEgInstallmentMaster()
.getFinYearRange())) {
// && (demandDetail.getAmount().compareTo(BigDecimal.ZERO) >
// 0)) {
if (AdvertisementTaxConstants.DEMANDREASON_PENALTY.equalsIgnoreCase(demandMasterReasonDesc))
demandDetail.setAmount(demandDetail.getAmount().add(recAccInfo.getCrAmount()));
demandDetail.addCollected(recAccInfo.getCrAmount());
/*
* Save bill detail and demand deatail relation in the intermediate table.
*/
persistCollectedReceipts(demandDetail, billReceiptInfo.getReceiptNum(), totalAmount,
billReceiptInfo.getReceiptDate(), recAccInfo.getCrAmount());
demand.setAmtCollected(demand.getAmtCollected().add(recAccInfo.getCrAmount()));
totalAmountCollected = totalAmountCollected.add(recAccInfo.getCrAmount());
demandDetail.setModifiedDate(new Date());
demandReasonPartOfDemand = true;
}
if (!demandReasonPartOfDemand) {
// Add new entry as part of demand. Eg: penalty is collected as
// part of collection system.
final EgDemandDetails demandDetail = advertisementDemandService.createDemandDetails(recAccInfo
.getCrAmount(), advertisementDemandService.getDemandReasonByCodeAndInstallment(
demandMasterReasonDesc, advertisementDemandService.getInstallmentByDescription(financialYearDesc)),
recAccInfo
.getCrAmount());
demand.addEgDemandDetails(demandDetail);
getCurrentSession().flush();
persistCollectedReceipts(demandDetail, billReceiptInfo.getReceiptNum(), totalAmount,
billReceiptInfo.getReceiptDate(), recAccInfo.getCrAmount());
}
demand.setModifiedDate(new Date());
}
return totalAmountCollected;
}
/**
* @param billId
* @return
*/
private EgDemand getDemandByBillReferenceNumber(final Long billId) {
EgDemand egDemand = null;
if (billId != null) {
final EgBill egBill = egBillDAO.findById(billId, false);
if (egBill != null)
egDemand = egBill.getEgDemand();
}
return egDemand;
}
@Override
protected Module module() {
return null;
}
@Override
public String constructAdditionalInfoForReceipt(final BillReceiptInfo billReceiptInfo) {
return null;
}
@Transactional
private void updateWorkflowState(final EgDemand demand) {
if (demand != null) {
final AdvertisementPermitDetail advertisementPermitDetail = advertisementService.getAdvertisementByDemand(demand)
.getActiveAdvertisementPermit();
/**
* If the current status of advertisement permit is approved, then only call next level workflow. Assumption: Payment
* collection is pending in this stage.
*/
if (advertisementPermitDetail != null
&& advertisementPermitDetail.getState() != null
&& advertisementPermitDetail.getStatus() != null
&& advertisementPermitDetail.getStatus().getCode()
.equalsIgnoreCase(AdvertisementTaxConstants.APPLICATION_STATUS_APPROVED)) {
advertisementPermitDetailService.updateStateTransition(advertisementPermitDetail, Long.valueOf(0),
AdvertisementTaxConstants.COLLECTION_REMARKS, advertisementPermitDetail.getPreviousapplicationid()!=null?AdvertisementTaxConstants.RENEWAL_ADDITIONAL_RULE: AdvertisementTaxConstants.CREATE_ADDITIONAL_RULE,
AdvertisementTaxConstants.WF_DEMANDNOTICE_BUTTON);
advertisementPermitDetailRepository.saveAndFlush(advertisementPermitDetail);
} else {
advertisementPermitDetailUpdateIndexService.updateAdvertisementPermitDetailIndexes(advertisementPermitDetail);
}
}
}
@Override
public ReceiptAmountInfo receiptAmountBifurcation(final BillReceiptInfo billReceiptInfo) {
return new ReceiptAmountInfo();
}
}