/*
* 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.client.service;
import org.apache.log4j.Logger;
import org.egov.collection.entity.ReceiptDetail;
import org.egov.collection.integration.models.BillAccountDetails.PURPOSE;
import org.egov.commons.dao.ChartOfAccountsHibernateDAO;
import org.egov.commons.dao.FunctionHibernateDAO;
import org.egov.demand.model.EgBillDetails;
import org.egov.infra.validation.exception.ValidationError;
import org.egov.infra.validation.exception.ValidationException;
import org.egov.ptis.constants.PropertyTaxConstants;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.egov.ptis.constants.PropertyTaxConstants.GLCODES_FOR_CURRENTTAX;
import static org.egov.ptis.constants.PropertyTaxConstants.GLCODE_FOR_TAXREBATE;
public class CollectionApportioner {
private static final String REBATE_STR = "REBATE";
public static final String STRING_FULLTAX = "FULLTAX";
public static final String STRING_ADVANCE = "ADVANCE";
private static final Logger LOGGER = Logger.getLogger(CollectionApportioner.class);
private boolean isEligibleForCurrentRebate;
private boolean isEligibleForAdvanceRebate;
public CollectionApportioner(boolean isEligibleForCurrentRebate, boolean isEligibleForAdvanceRebate,
BigDecimal rebate) {
this.isEligibleForCurrentRebate = isEligibleForCurrentRebate;
this.isEligibleForAdvanceRebate = isEligibleForAdvanceRebate;
// this.rebate = rebate;
}
public void apportion(BigDecimal amtPaid, List<ReceiptDetail> receiptDetails, Map<String, BigDecimal> instDmdMap) {
LOGGER.info("receiptDetails before apportioning amount " + amtPaid + ": " + receiptDetails);
Boolean isFullPayment = Boolean.FALSE;
if (isEligibleForCurrentRebate) {
BigDecimal totalCrAmountToBePaid = BigDecimal.ZERO;
for (final ReceiptDetail receiptDetail : receiptDetails) {
totalCrAmountToBePaid = totalCrAmountToBePaid.add(receiptDetail.getCramountToBePaid());
}
if (amtPaid.compareTo(totalCrAmountToBePaid) >= 0) {
isFullPayment = Boolean.TRUE;
}
}
Amount balance = new Amount(amtPaid);
BigDecimal crAmountToBePaid = null;
for (ReceiptDetail rd : receiptDetails) {
if (balance.isZero()) {
// nothing left to apportion
rd.zeroDrAndCrAmounts();
continue;
}
crAmountToBePaid = rd.getCramountToBePaid();
if (rd.getDescription().contains(REBATE_STR)) {
if (isFullPayment) {
balance = balance.minus(crAmountToBePaid);
} else {
rd.setDramount(BigDecimal.ZERO);
}
} else {
if (balance.isLessThanOrEqualTo(crAmountToBePaid)) {
// partial or exact payment
rd.setCramount(balance.amount);
balance = Amount.ZERO;
} else { // excess payment
rd.setCramount(crAmountToBePaid);
balance = balance.minus(crAmountToBePaid);
}
}
}
if (balance.isGreaterThanZero()) {
LOGGER.error("Apportioning failed: excess payment!");
throw new ValidationException(Arrays.asList(new ValidationError(
"Paid Amount is greater than Total Amount to be paid",
"Paid Amount is greater than Total Amount to be paid")));
}
LOGGER.info("receiptDetails after apportioning: " + receiptDetails);
}
public List<ReceiptDetail> reConstruct(final BigDecimal amountPaid, final List<EgBillDetails> billDetails,
FunctionHibernateDAO functionDAO, ChartOfAccountsHibernateDAO chartOfAccountsDAO) {
final List<ReceiptDetail> receiptDetails = new ArrayList<ReceiptDetail>(0);
LOGGER.info("receiptDetails before reApportion amount " + amountPaid + ": " + receiptDetails);
LOGGER.info("billDetails before reApportion " + billDetails);
Amount balance = new Amount(amountPaid);
Boolean isFullPayment = Boolean.FALSE;
BigDecimal crAmountToBePaid = BigDecimal.ZERO;
if (isEligibleForCurrentRebate) {
BigDecimal totalCrAmountToBePaid = BigDecimal.ZERO;
for (final ReceiptDetail receiptDetail : receiptDetails) {
totalCrAmountToBePaid = totalCrAmountToBePaid.add(receiptDetail.getCramountToBePaid());
}
if (amountPaid.compareTo(totalCrAmountToBePaid) >= 0) {
isFullPayment = Boolean.TRUE;
}
}
for (final EgBillDetails billDetail : billDetails) {
final String glCode = billDetail.getGlcode();
final ReceiptDetail receiptDetail = new ReceiptDetail();
receiptDetail.setPurpose(PURPOSE.OTHERS.toString());
receiptDetail.setOrdernumber(Long.valueOf(billDetail.getOrderNo()));
receiptDetail.setDescription(billDetail.getDescription());
receiptDetail.setIsActualDemand(true);
if(billDetail.getFunctionCode() !=null){
receiptDetail.setFunction(functionDAO.getFunctionByCode(billDetail.getFunctionCode()));
}
receiptDetail.setAccounthead(chartOfAccountsDAO.getCChartOfAccountsByGlCode(glCode));
receiptDetail.setCramountToBePaid(balance.amount);
if (billDetail.getDescription().contains(REBATE_STR)) {
receiptDetail.setDramount(billDetail.getDrAmount());
} else {
receiptDetail.setDramount(BigDecimal.ZERO);
}
if (balance.isZero()) {
// nothing left to apportion
receiptDetail.zeroDrAndCrAmounts();
receiptDetails.add(receiptDetail);
continue;
}
crAmountToBePaid = billDetail.getCrAmount();
if (receiptDetail.getDescription().contains(REBATE_STR)) {
if (isFullPayment) {
balance = balance.minus(crAmountToBePaid);
} else {
receiptDetail.setDramount(BigDecimal.ZERO);
}
} else {
if (balance.isLessThanOrEqualTo(crAmountToBePaid)) {
// partial or exact payment
receiptDetail.setCramount(balance.amount);
receiptDetail.setCramountToBePaid(balance.amount);
balance = Amount.ZERO;
} else { // excess payment
receiptDetail.setCramount(crAmountToBePaid);
receiptDetail.setCramountToBePaid(crAmountToBePaid);
balance = balance.minus(crAmountToBePaid);
}
}
receiptDetails.add(receiptDetail);
}
if (balance.isGreaterThanZero()) {
LOGGER.error("reApportion failed: excess payment!");
throw new ValidationException(Arrays.asList(
new ValidationError("Paid Amount is greater than Total Amount to be paid",
"Paid Amount is greater than Total Amount to be paid")));
}
LOGGER.info("receiptDetails after reApportion: " + receiptDetails);
return receiptDetails;
}
private boolean isArrear(String glCode) {
return !GLCODES_FOR_CURRENTTAX.contains(glCode) && !glCode.equals(GLCODE_FOR_TAXREBATE);
}
private boolean isCurrent(String glCode) {
return GLCODES_FOR_CURRENTTAX.contains(glCode);
}
private boolean isAdvance(String glCode) {
return glCode.equals(PropertyTaxConstants.GLCODE_FOR_ADVANCE);
}
private boolean isRebate(String glCode) {
return glCode.equals(GLCODE_FOR_TAXREBATE)
|| glCode.equalsIgnoreCase(PropertyTaxConstants.GLCODE_FOR_ADVANCE_REBATE);
}
private static class Amount {
private BigDecimal amount;
private static Amount ZERO = new Amount(BigDecimal.ZERO);
private Amount(BigDecimal amount) {
this.amount = amount;
}
private boolean isZero() {
return amount.compareTo(BigDecimal.ZERO) == 0;
}
private boolean isGreaterThan(BigDecimal bd) {
return amount.compareTo(bd) > 0;
}
private boolean isGreaterThanZero() {
return isGreaterThan(BigDecimal.ZERO);
}
private boolean isGreaterThanOrEqualTo(BigDecimal bd) {
return amount.compareTo(bd) >= 0;
}
private boolean isLessThanOrEqualTo(BigDecimal bd) {
return amount.compareTo(bd) <= 0;
}
private boolean isLessThan(BigDecimal bd) {
return amount.compareTo(bd) < 0;
}
private boolean isGreaterThanOrEqualToZero() {
return isGreaterThanOrEqualTo(BigDecimal.ZERO);
}
private Amount minus(BigDecimal bd) {
return new Amount(amount.subtract(bd));
}
private Amount plus(BigDecimal bd) {
return new Amount(amount.add(bd));
}
private Amount multiply(BigDecimal bd) {
return new Amount(amount.multiply(bd));
}
private Amount divide(BigDecimal bd) {
return new Amount(amount.divide(bd));
}
}
void setEligibleForCurrentRebate(boolean isEligibleForCurrentRebate) {
this.isEligibleForCurrentRebate = isEligibleForCurrentRebate;
}
void setEligibleForAdvanceRebate(boolean isEligibleForAdvanceRebate) {
this.isEligibleForAdvanceRebate = isEligibleForAdvanceRebate;
}
}