/*
* Copyright (c) 2005-2011 Grameen Foundation USA
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* See also http://www.apache.org/licenses/LICENSE-2.0.html for an
* explanation of the license and how it is applied.
*/
package org.mifos.accounts.loan.business;
import static org.mifos.framework.util.helpers.NumberUtils.min;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.joda.time.LocalDate;
import org.mifos.accounts.business.AccountActionDateEntity;
import org.mifos.accounts.business.AccountBO;
import org.mifos.accounts.business.AccountFeesActionDetailEntity;
import org.mifos.accounts.business.AccountPaymentEntity;
import org.mifos.accounts.loan.persistance.LegacyLoanDao;
import org.mifos.accounts.loan.schedule.domain.Installment;
import org.mifos.accounts.loan.util.helpers.LoanConstants;
import org.mifos.accounts.loan.util.helpers.RepaymentScheduleInstallment;
import org.mifos.accounts.util.helpers.AccountActionTypes;
import org.mifos.accounts.util.helpers.AccountConstants;
import org.mifos.accounts.util.helpers.OverDueAmounts;
import org.mifos.accounts.util.helpers.PaymentStatus;
import org.mifos.application.master.business.MifosCurrency;
import org.mifos.config.business.service.ConfigurationBusinessService;
import org.mifos.customers.business.CustomerBO;
import org.mifos.customers.personnel.business.PersonnelBO;
import org.mifos.dto.domain.LoanCreationInstallmentDto;
import org.mifos.framework.util.helpers.Money;
import org.mifos.platform.util.CollectionUtils;
public class LoanScheduleEntity extends AccountActionDateEntity {
private Money principal;
private Money principalPaid;
private Money interest;
private Money interestPaid;
private Money penalty;
private Money penaltyPaid;
private Money extraInterest;
private Money extraInterestPaid;
private Money miscFee;
private Money miscFeePaid;
private Money miscPenalty;
private Money miscPenaltyPaid;
private Set<AccountFeesActionDetailEntity> accountFeesActionDetails = new HashSet<AccountFeesActionDetailEntity>();
private Set<LoanPenaltyScheduleEntity> loanPenaltiesSchedule = new HashSet<LoanPenaltyScheduleEntity>();
private int versionNo;
private PaymentAllocation paymentAllocation;
private static ConfigurationBusinessService configService = new ConfigurationBusinessService();
protected LoanScheduleEntity() {
super(null, null, null, null, null);
}
public LoanScheduleEntity(AccountBO account, CustomerBO customer, Short installmentId, java.sql.Date actionDate,
PaymentStatus paymentStatus, Money principal, Money interest) {
super(account, customer, installmentId, actionDate, paymentStatus);
this.principal = principal;
this.interest = interest;
zeroRemainingFields(principal.getCurrency());
}
private void zeroRemainingFields(MifosCurrency currency) {
this.penalty = new Money(currency);
this.extraInterest = new Money(currency);
this.miscFee = new Money(currency);
this.miscPenalty = new Money(currency);
this.principalPaid = new Money(currency);
this.interestPaid = new Money(currency);
this.penaltyPaid = new Money(currency);
this.extraInterestPaid = new Money(currency);
this.miscFeePaid = new Money(currency);
this.miscPenaltyPaid = new Money(currency);
}
@Override
public MifosCurrency getCurrency() {
MifosCurrency currency = null;
if (this.account != null) {
currency = super.getCurrency();
} else {
currency = this.principal.getCurrency();
}
return currency;
}
public Money getInterest() {
return interest;
}
public void setInterest(Money interest) {
this.interest = interest;
}
public Money getInterestPaid() {
return interestPaid;
}
public void setInterestPaid(Money interestPaid) {
this.interestPaid = interestPaid;
}
public void setPenalty(Money penalty) {
this.penalty = penalty;
}
public Money getPenaltyPaid() {
return penaltyPaid;
}
public Money getTotalPenaltyPaid() {
return penaltyPaid.add(miscPenaltyPaid);
}
public void setPenaltyPaid(Money penaltyPaid) {
this.penaltyPaid = penaltyPaid;
}
public Money getPrincipal() {
return principal;
}
public void setPrincipal(Money principal) {
this.principal = principal;
}
public Money getPrincipalPaid() {
return principalPaid;
}
public void setPrincipalPaid(Money principalPaid) {
this.principalPaid = principalPaid;
}
public Money getPrincipalDue() {
return principal.subtract(principalPaid);
}
public Money getInterestDue() {
return interest.subtract(interestPaid);
}
public Money getPenalty() {
return penalty;
}
public Set<AccountFeesActionDetailEntity> getAccountFeesActionDetails() {
return accountFeesActionDetails;
}
public void addAccountFeesAction(AccountFeesActionDetailEntity accountFeesAction) {
accountFeesActionDetails.add(accountFeesAction);
}
public Set<LoanPenaltyScheduleEntity> getLoanPenaltyScheduleEntities() {
return loanPenaltiesSchedule;
}
public void addLoanPenaltySchedule(LoanPenaltyScheduleEntity loanPenaltySchedule) {
loanPenaltiesSchedule.add(loanPenaltySchedule);
}
public Money getMiscFee() {
return miscFee;
}
public void setMiscFee(Money miscFee) {
this.miscFee = miscFee;
}
public Money getMiscFeePaid() {
return miscFeePaid;
}
public void setMiscFeePaid(Money miscFeePaid) {
this.miscFeePaid = miscFeePaid;
}
public Money getMiscPenalty() {
return miscPenalty;
}
public void setMiscPenalty(Money miscPenalty) {
this.miscPenalty = miscPenalty;
}
public Money getMiscPenaltyPaid() {
return miscPenaltyPaid;
}
public Money getMiscPenaltyDue() {
return miscPenalty.subtract(miscPenaltyPaid);
}
public void setMiscPenaltyPaid(Money miscPenaltyPaid) {
this.miscPenaltyPaid = miscPenaltyPaid;
}
public Money getPenaltyDue() {
return (penalty.add(miscPenalty)).subtract(penaltyPaid.add(miscPenaltyPaid));
}
public Money getTotalDue() {
return principal.subtract(principalPaid).add(getEffectiveInterestDue()).add(getPenaltyDue()).add(getMiscFeeDue());
}
public Money getTotalDueWithoutPrincipal() {
return getInterestDue().add(getPenaltyDue()).add(getMiscFeeDue());
}
public Money getTotalPenalty() {
return penalty.add(miscPenalty);
}
public Money getTotalDueWithFees() {
return getTotalDue().add(getTotalFeesDue());
}
public Money getTotalScheduleAmountWithFees() {
return principal.add(
interest.add(penalty).add(getTotalScheduledFeeAmountWithMiscFee()).add(miscPenalty));
}
public OverDueAmounts getDueAmnts() {
OverDueAmounts overDueAmounts = new OverDueAmounts();
overDueAmounts.setFeesOverdue(getTotalFeesDue().add(getMiscFeeDue()));
overDueAmounts.setInterestOverdue(getInterestDue());
overDueAmounts.setPenaltyOverdue(getPenaltyDue());
overDueAmounts.setPrincipalOverDue(getPrincipalDue());
overDueAmounts.setTotalPrincipalPaid(getPrincipalPaid());
return overDueAmounts;
}
void makeEarlyRepaymentEntries(String payFullOrPartial, Money interestDue, Date paymentDate) {
setPrincipalPaid(getPrincipalPaid().add(getPrincipalDue()));
setExtraInterestPaid(getExtraInterestPaid().add(getExtraInterestDue()));
if (payFullOrPartial.equals(LoanConstants.PAY_FEES_PENALTY_INTEREST)) {
setInterestPaid(getInterestPaid().add(interestDue));
setPenaltyPaid(getPenaltyPaid().add(getPenaltyDue()));
if (getMiscFeePaid().isLessThan(getMiscFee())) {
setMiscFeePaid(getMiscFeePaid().add(getMiscFee()));
}
if (getMiscPenaltyPaid().isLessThan(getMiscPenalty())) {
setMiscPenaltyPaid(getMiscPenaltyPaid().add(getMiscPenalty()));
}
} else if (payFullOrPartial.equals(LoanConstants.PAY_FEES_PENALTY)) {
setPenaltyPaid(getPenaltyPaid().add(getPenaltyDue()));
if (getMiscFeePaid().isLessThan(getMiscFee())) {
setMiscFeePaid(getMiscFeePaid().add(getMiscFee()));
}
if (getMiscPenaltyPaid().isLessThan(getMiscPenalty())) {
setMiscPenaltyPaid(getMiscPenaltyPaid().add(getMiscPenalty()));
}
}
makeRepaymentEntries(payFullOrPartial, paymentDate);
}
private void makeRepaymentEntries(String payFullOrPartial, Date paymentDate) {
setPaymentStatus(PaymentStatus.PAID);
setPaymentDate(new java.sql.Date(paymentDate.getTime()));
Set<AccountFeesActionDetailEntity> accountFeesActionDetailSet = this.getAccountFeesActionDetails();
for (AccountFeesActionDetailEntity accountFeesActionDetailEntity : accountFeesActionDetailSet) {
((LoanFeeScheduleEntity) accountFeesActionDetailEntity).makeRepaymentEnteries(payFullOrPartial);
}
Set<LoanPenaltyScheduleEntity> loanPenaltyScheduleSet = this.getLoanPenaltyScheduleEntities();
for (LoanPenaltyScheduleEntity loanPenaltyScheduleEntity : loanPenaltyScheduleSet) {
loanPenaltyScheduleEntity.makeRepaymentEnteries(payFullOrPartial);
}
}
public void updatePaymentDetailsForAdjustment(LoanTrxnDetailEntity loanReverseTrxn) {
CalculatedInterestOnPayment interestOnPayment = loanReverseTrxn.getCalculatedInterestOnPayment();
Money overdueInterestPaid = calculateExtraInterestPaid(interestOnPayment);
principalPaid = principalPaid.add(loanReverseTrxn.getPrincipalAmount());
interest = calculateAdjustedInterest(interestOnPayment, overdueInterestPaid, loanReverseTrxn);
interestPaid = interestPaid.add(loanReverseTrxn.getInterestAmount()).add(overdueInterestPaid);
penaltyPaid = penaltyPaid.add(loanReverseTrxn.getPenaltyAmount());
miscPenaltyPaid = miscPenaltyPaid.add(loanReverseTrxn.getMiscPenaltyAmount());
miscFeePaid = miscFeePaid.add(loanReverseTrxn.getMiscFeeAmount());
extraInterestPaid = extraInterestPaid.subtract(overdueInterestPaid);
}
private Money calculateExtraInterestPaid(CalculatedInterestOnPayment interestOnPayment) {
return interestOnPayment == null ? Money.zero(getCurrency()) : interestOnPayment.getExtraInterestPaid();
}
private Money calculateAdjustedInterest(CalculatedInterestOnPayment interestOnPayment, Money overdueInterestPaid,
LoanTrxnDetailEntity loanReverseTrxn) {
boolean isDecliningBalanceIR = ((LoanBO)account).isDecliningBalanceInterestRecalculation();
boolean isRecalculateInterestEnabled = configService.isRecalculateInterestEnabled();
boolean isDecliningBalanceEPI = ((LoanBO)account).isDecliningBalanceEqualPrincipleCalculation();
if (interestOnPayment != null
&& ( isDecliningBalanceIR || ( isRecalculateInterestEnabled && isDecliningBalanceEPI ) ) ) {
return interestOnPayment.getOriginalInterest().subtract(loanReverseTrxn.getInterestAmount()).subtract(overdueInterestPaid.
add(interestOnPayment.getInterestDueTillPaid()));
}
return interest;
}
Money waiveFeeCharges() {
Money chargeWaived = new Money(getCurrency());
chargeWaived = chargeWaived.add(getMiscFeeDue());
setMiscFee(getMiscFeePaid());
for (AccountFeesActionDetailEntity accountFeesActionDetailEntity : getAccountFeesActionDetails()) {
chargeWaived = chargeWaived.add(((LoanFeeScheduleEntity) accountFeesActionDetailEntity).waiveCharges());
}
getAccountFeesActionDetails().clear();
return chargeWaived;
}
// watch out, this relies on passing in the same reference as the object held in the collection
void removeAccountFeesActionDetailEntity(AccountFeesActionDetailEntity accountFeesActionDetailEntity) {
accountFeesActionDetails.remove(accountFeesActionDetailEntity);
}
void removeLoanPenaltySchedule(LoanPenaltyScheduleEntity loanPenaltySchedule) {
loanPenaltiesSchedule.remove(loanPenaltySchedule);
}
public Money getMiscFeeDue() {
return getMiscFee().subtract(getMiscFeePaid());
}
public Money getTotalFeesDue() {
Money totalFees = new Money(getCurrency());
for (AccountFeesActionDetailEntity obj : accountFeesActionDetails) {
totalFees = totalFees.add(obj.getFeeDue());
}
return totalFees;
}
public Money getTotalFeeAmountPaidWithMiscFee() {
Money totalFees = new Money(getCurrency());
for (AccountFeesActionDetailEntity obj : accountFeesActionDetails) {
totalFees = totalFees.add(obj.getFeeAmountPaid());
}
totalFees = totalFees.add(getMiscFeePaid());
return totalFees;
}
public Money getTotalScheduledFeeAmountWithMiscFee() {
Money totalFees = new Money(getCurrency());
for (AccountFeesActionDetailEntity obj : accountFeesActionDetails) {
totalFees = totalFees.add(obj.getFeeAmount());
}
totalFees = totalFees.add(getMiscFee());
return totalFees;
}
public Money getTotalFeesDueWithMiscFee() {
return miscFee.add(getTotalFeesDue());
}
public Money getTotalFees() {
Money totalFees = new Money(getCurrency());
for (AccountFeesActionDetailEntity obj : accountFeesActionDetails) {
totalFees = totalFees.add(obj.getFeeAmount());
}
return totalFees;
}
public Money getTotalFeesPaid() {
Money totalFees = new Money(getCurrency());
for (AccountFeesActionDetailEntity obj : accountFeesActionDetails) {
totalFees = totalFees.add(obj.getFeeAmountPaid());
}
return totalFees;
}
public Money getTotalFeeDueWithMiscFeeDue() {
return getMiscFeeDue().add(getTotalFeesDue());
}
public Money getTotalPaymentDue() {
return getTotalDue().add(getTotalFeesDue());
}
Money removeFees(Short feeId) {
Money feeAmount = null;
AccountFeesActionDetailEntity objectToRemove = null;
Set<AccountFeesActionDetailEntity> accountFeesActionDetailSet = this.getAccountFeesActionDetails();
for (AccountFeesActionDetailEntity accountFeesActionDetailEntity : accountFeesActionDetailSet) {
if (accountFeesActionDetailEntity.getFee().getFeeId().equals(feeId)
&& (accountFeesActionDetailEntity.getFeeAmountPaid() == null || accountFeesActionDetailEntity
.getFeeAmountPaid().isZero())) {
objectToRemove = accountFeesActionDetailEntity;
feeAmount = objectToRemove.getFeeAmount();
break;
} else if (accountFeesActionDetailEntity.getFee().getFeeId().equals(feeId)
&& accountFeesActionDetailEntity.getFeeAmountPaid() != null
&& accountFeesActionDetailEntity.getFeeAmountPaid().isGreaterThanZero()) {
feeAmount = accountFeesActionDetailEntity.getFeeAmount().subtract(
accountFeesActionDetailEntity.getFeeAmountPaid());
((LoanFeeScheduleEntity) accountFeesActionDetailEntity).setFeeAmount(accountFeesActionDetailEntity
.getFeeAmountPaid());
break;
}
}
if (objectToRemove != null) {
this.removeAccountFeesActionDetailEntity(objectToRemove);
}
return feeAmount;
}
public AccountFeesActionDetailEntity getAccountFeesAction(Short feeId) {
for (AccountFeesActionDetailEntity accountFeesAction : getAccountFeesActionDetails()) {
if (accountFeesAction.getFee().getFeeId().equals(feeId)) {
return accountFeesAction;
}
}
return null;
}
Money waivePenaltyCharges() {
Money chargeWaived = new Money(getCurrency());
chargeWaived = chargeWaived.add(getMiscPenaltyDue());
setMiscPenalty(getMiscPenaltyPaid());
setPenalty(getPenaltyPaid());
for (LoanPenaltyScheduleEntity loanPenaltyScheduleEntity : getLoanPenaltyScheduleEntities()) {
chargeWaived = chargeWaived.add(loanPenaltyScheduleEntity.waiveCharges());
}
return chargeWaived;
}
void applyMiscCharge(Short chargeType, Money charge) {
if (chargeType.equals(Short.valueOf(AccountConstants.MISC_FEES))) {
setMiscFee(getMiscFee().add(charge));
} else if (chargeType.equals(Short.valueOf(AccountConstants.MISC_PENALTY))) {
setMiscPenalty(getMiscPenalty().add(charge));
}
}
public boolean isPrincipalZero() {
return principal.isZero();
}
public boolean isFeeAlreadyAttatched(Short feeId) {
for (AccountFeesActionDetailEntity accountFeesActionDetailEntity : this.getAccountFeesActionDetails()) {
if (accountFeesActionDetailEntity.getFee().getFeeId().equals(feeId)) {
return true;
}
}
return false;
}
public boolean isPaymentAppliedToAccountFees() {
Money feesPaid = new Money(getCurrency(),"0.0");
for (AccountFeesActionDetailEntity accountFeesActionDetail : getAccountFeesActionDetails()) {
feesPaid = feesPaid.add(accountFeesActionDetail.getFeeAmountPaid());
}
return feesPaid.isNonZero();
}
public boolean isPaymentApplied() {
return getPrincipalPaid().isNonZero() || getEffectiveInterestPaid().isNonZero() || getMiscFeePaid().isNonZero()
|| getMiscPenaltyPaid().isNonZero() || isPaymentAppliedToAccountFees();
}
public void setVersionNo(int versionNo) {
this.versionNo = versionNo;
}
public int getVersionNo() {
return versionNo;
}
public List<AccountFeesActionDetailEntity> getAccountFeesActionDetailsSortedByFeeId() {
List<AccountFeesActionDetailEntity> sortedList = new ArrayList<AccountFeesActionDetailEntity>();
sortedList.addAll(this.getAccountFeesActionDetails());
Collections.sort(sortedList);
return sortedList;
}
public RepaymentScheduleInstallment toDto() {
return new RepaymentScheduleInstallment(this.installmentId,
this.actionDate, this.principal, this.interest,
this.getTotalFeesDue(), this.miscFee, this.miscPenalty);
}
public LoanCreationInstallmentDto toLoanCreationInstallmentDto(Short digitsAfterDecimal) {
Integer installmentNumber = getInstallmentId().intValue();
LocalDate dueDate = new LocalDate(getActionDate());
String principal = getPrincipal().toString(digitsAfterDecimal);
String interest = getInterest().toString(digitsAfterDecimal);
String fees = getTotalFees().toString(digitsAfterDecimal);
String penalty = "0.0";
String total = getPrincipal().add(getInterest()).add(getTotalFees()).toString(digitsAfterDecimal);
LoanCreationInstallmentDto installment = new LoanCreationInstallmentDto(installmentNumber, dueDate,
Double.valueOf(principal), Double.valueOf(interest), Double.valueOf(fees), Double.valueOf(penalty),
Double.valueOf(total));
return installment;
}
public boolean isSameAs(AccountActionDateEntity accountActionDateEntity) {
return getInstallmentId().equals(accountActionDateEntity.getInstallmentId());
}
public Money getExtraInterest() {
return extraInterest == null ? Money.zero(getCurrency()) : new Money(getCurrency(), extraInterest.getAmount());
}
public void setExtraInterest(Money extraInterest) {
this.extraInterest = extraInterest;
}
public Money getExtraInterestPaid() {
return extraInterestPaid == null ? Money.zero(getCurrency()) : new Money(getCurrency(), extraInterestPaid.getAmount());
}
public void setExtraInterestPaid(Money extraInterestPaid) {
this.extraInterestPaid = extraInterestPaid;
}
public Money getExtraInterestDue() {
return getExtraInterest().subtract(getExtraInterestPaid());
}
public Money getEffectiveInterestPaid() {
return interestPaid.add(getExtraInterestPaid());
}
public Money getEffectiveInterestDue() {
return getInterestDue().add(getExtraInterestDue());
}
private Money payMiscPenalty(final Money amount) {
Money payable = min(amount, getMiscPenaltyDue());
allocateMiscPenalty(payable);
return amount.subtract(payable);
}
private void allocateMiscPenalty(Money payable) {
paymentAllocation.allocateForMiscPenalty(payable);
miscPenaltyPaid = miscPenaltyPaid.add(payable);
}
private Money payPenalty(final Money amount) {
Money payable = min(amount, (getPenalty().subtract(getPenaltyPaid())));
Money balance = amount;
allocatePenalty(payable);
for (LoanPenaltyScheduleEntity loanPenaltyScheduleEntity : getLoanPenaltyScheduleEntities()) {
balance = loanPenaltyScheduleEntity.payPenalty(balance);
Integer penaltyId = loanPenaltyScheduleEntity.getLoanPenaltyScheduleEntityId();
if (penaltyId == null) { // special workaround for MIFOS-4517
penaltyId = loanPenaltyScheduleEntity.hashCode();
}
Money penaltyAllocated = loanPenaltyScheduleEntity.getPenaltyAllocated();
paymentAllocation.allocateForPenalty(penaltyId, penaltyAllocated);
}
return amount.subtract(payable);
}
private void allocatePenalty(Money payable) {
paymentAllocation.allocateForPenalty(payable);
penaltyPaid = penaltyPaid.add(payable);
}
private Money payMiscFees(final Money amount) {
Money payable = min(amount, getMiscFeeDue());
allocateMiscFees(payable);
return amount.subtract(payable);
}
private void allocateMiscFees(Money payable) {
paymentAllocation.allocateForMiscFees(payable);
miscFeePaid = miscFeePaid.add(payable);
}
private void allocateExtraInterest(Money payable) {
paymentAllocation.allocateForExtraInterest(payable);
extraInterestPaid = extraInterestPaid.add(payable);
}
private Money payFees(final Money amount) {
Money balance = amount;
for (AccountFeesActionDetailEntity accountFeesActionDetailEntity : getAccountFeesActionDetails()) {
balance = accountFeesActionDetailEntity.payFee(balance);
Integer feeId = accountFeesActionDetailEntity.getAccountFeesActionDetailId();
if (feeId == null) { // special workaround for MIFOS-4517
feeId = accountFeesActionDetailEntity.hashCode();
}
Money feeAllocated = accountFeesActionDetailEntity.getFeeAllocated();
paymentAllocation.allocateForFee(feeId, feeAllocated);
}
return balance;
}
private Money payInterest(final Money amount) {
Money payable = min(amount, getInterestDue());
allocateInterest(payable);
return amount.subtract(payable);
}
private void allocateInterest(Money payable) {
paymentAllocation.allocateForInterest(payable);
interestPaid = interestPaid.add(payable);
}
private Money reducePrincipalBy(final Money amount) {
Money reducedBy = min(amount, getPrincipalDue());
this.principal = this.principal.subtract(reducedBy);
return amount.subtract(reducedBy);
}
private Money payPrincipal(final Money amount) {
Money payable = min(amount, getPrincipalDue());
allocatePrincipal(payable);
return amount.subtract(payable);
}
private void allocatePrincipal(Money payable) {
paymentAllocation.allocateForPrincipal(payable);
principalPaid = principalPaid.add(payable);
}
public Money payComponents(Money paymentAmount, Date paymentDate) {
initPaymentAllocation(paymentAmount.getCurrency());
Money balanceAmount = paymentAmount;
balanceAmount = payMiscPenalty(balanceAmount);
balanceAmount = payPenalty(balanceAmount);
balanceAmount = payMiscFees(balanceAmount);
balanceAmount = payFees(balanceAmount);
balanceAmount = payInterest(balanceAmount);
balanceAmount = payPrincipal(balanceAmount);
recordPayment(paymentDate);
return balanceAmount;
}
public Money payInterestComponent(Money paymentAmount, Date paymentDate) {
initPaymentAllocation(paymentAmount.getCurrency());
Money balanceAmount = paymentAmount;
balanceAmount = payInterest(balanceAmount);
recordPayment(paymentDate);
return balanceAmount;
}
public void payComponents(Installment installment, MifosCurrency currency, Date paymentDate) {
initPaymentAllocation(currency);
allocatePrincipal(new Money(currency, installment.getCurrentPrincipalPaid()));
allocateInterest(new Money(currency, installment.getCurrentInterestPaid()));
allocateExtraInterest(new Money(currency, installment.getCurrentExtraInterestPaid()));
payFees(new Money(currency, installment.getCurrentFeesPaid()));
allocateMiscFees(new Money(currency, installment.getCurrentMiscFeesPaid()));
allocatePenalty(new Money(currency, installment.getCurrentPenaltyPaid()));
allocateMiscPenalty(new Money(currency, installment.getCurrentMiscPenaltyPaid()));
updateInterest(installment, currency);
setExtraInterest(new Money(currency, installment.getExtraInterest()));
recordPayment(paymentDate);
}
private void updateInterest(Installment installment, MifosCurrency currency) {
if (installment.hasEffectiveInterest()) {
setInterest(new Money(currency, installment.getEffectiveInterest().add(interestPaid.getAmount())));
} else {
setInterest(new Money(currency, installment.getInterest()));
}
}
private void initPaymentAllocation(MifosCurrency currency) {
paymentAllocation = new PaymentAllocation(currency);
}
public PaymentAllocation getPaymentAllocation() {
return paymentAllocation;
}
public ConfigurationBusinessService getConfigService() {
return configService;
}
public void setConfigService(ConfigurationBusinessService configService) {
LoanScheduleEntity.configService = configService;
}
void recordForAdjustment() {
setPaymentStatus(PaymentStatus.UNPAID);
setPaymentDate(getLastPaymentDate());
}
private java.sql.Date getLastPaymentDate() {
AccountPaymentEntity lastPaymentToBeAdjusted = getAccount().getLastPmntToBeAdjusted();
return lastPaymentToBeAdjusted == null ? null :
new java.sql.Date(lastPaymentToBeAdjusted.getPaymentDate().getTime());
}
void recordPayment(Date paymentDate) {
setPaymentDate(new java.sql.Date(paymentDate.getTime()));
setPaymentStatus(getTotalDueWithFees().isTinyAmount() ? PaymentStatus.PAID : PaymentStatus.UNPAID);
}
public double getPrincipalAsDouble() {
return principal.getAmount().doubleValue();
}
public double getInterestAsDouble() {
return interest.getAmount().doubleValue();
}
public double getPenaltyAsDouble() {
return penalty.getAmount().doubleValue();
}
public double getMiscFeeAsDouble() {
return miscFee.getAmount().doubleValue();
}
public double getMiscPenaltyAsDouble() {
return miscPenalty.getAmount().doubleValue();
}
public double getTotalFeesAsDouble() {
return getTotalFees().getAmount().doubleValue();
}
public double getPrincipalPaidAsDouble() {
return principalPaid.getAmount().doubleValue();
}
public double getInterestPaidAsDouble() {
return interestPaid.getAmount().doubleValue();
}
public double getPenaltyPaidAsDouble() {
return penaltyPaid.getAmount().doubleValue();
}
public double getMiscFeePaidAsDouble() {
return miscFeePaid.getAmount().doubleValue();
}
public double getMiscPenaltyPaidAsDouble() {
return miscPenaltyPaid.getAmount().doubleValue();
}
public double getTotalFeesPaidAsDouble() {
return getTotalFeesPaid().getAmount().doubleValue();
}
public double getPrincipalDueAsDouble() {
return getPrincipalDue().getAmount().doubleValue();
}
public double getInterestDueAsDouble() {
return getInterestDue().getAmount().doubleValue();
}
public double getPenaltyDueAsDouble() {
return getPenaltyDue().getAmount().doubleValue();
}
public double getMiscFeesDueAsDouble() {
return getMiscFeeDue().getAmount().doubleValue();
}
public double getMiscPenaltyDueAsDouble() {
return getMiscPenaltyDue().getAmount().doubleValue();
}
public double getTotalFeesDueAsDouble() {
return getTotalFeesDue().getAmount().doubleValue();
}
public LoanTrxnDetailEntity updateSummaryAndPerformanceHistory(AccountPaymentEntity accountPayment,
PersonnelBO personnel, Date transactionDate) {
LoanBO loanBO = (LoanBO) account;
LegacyLoanDao legacyLoanDao = loanBO.getlegacyLoanDao();
LoanTrxnDetailEntity loanTrxnDetailEntity = recordTransaction(accountPayment, personnel, transactionDate, legacyLoanDao);
loanBO.recordSummaryAndPerfHistory(isPaid(), paymentAllocation);
return loanTrxnDetailEntity;
}
private LoanTrxnDetailEntity recordTransaction(AccountPaymentEntity accountPayment, PersonnelBO personnel,
Date transactionDate, LegacyLoanDao legacyLoanDao) {
// TODO: Avoid passing the persistence instance in the constructor for reference data lookup
LoanTrxnDetailEntity loanTrxnDetailEntity = new LoanTrxnDetailEntity(accountPayment, this, personnel, transactionDate,
AccountActionTypes.LOAN_REPAYMENT, AccountConstants.PAYMENT_RCVD, legacyLoanDao);
accountPayment.addAccountTrxn(loanTrxnDetailEntity);
return loanTrxnDetailEntity;
}
public Money applyPayment(AccountPaymentEntity accountPaymentEntity, Money balance, PersonnelBO personnel, Date transactionDate) {
if (isNotPaid() && balance.isGreaterThanZero()) {
balance = payComponents(balance, transactionDate);
updateSummaryAndPerformanceHistory(accountPaymentEntity, personnel, transactionDate);
}
return balance;
}
public Money applyPaymentToInterest(AccountPaymentEntity accountPaymentEntity, Money balance, PersonnelBO personnel, Date transactionDate) {
if (isNotPaid() && balance.isGreaterThanZero()) {
balance = payInterestComponent(balance, transactionDate);
updateSummaryAndPerformanceHistory(accountPaymentEntity, personnel, transactionDate);
}
return balance;
}
public Money reducePrincipal(AccountPaymentEntity accountPaymentEntity,
Money balance, PersonnelBO personnel, Date transactionDate) {
if (isNotPaid() && balance.isGreaterThanZero() && getPrincipalDue().isGreaterThanZero()) {
initPaymentAllocation(balance.getCurrency());
balance = reducePrincipalBy(balance);
updateSummaryAndPerformanceHistory(accountPaymentEntity, personnel, transactionDate);
}
return balance;
}
public Money applyOverPayment(AccountPaymentEntity accountPaymentEntity, Money balance, PersonnelBO personnel, Date transactionDate) {
if (isNotPaid() && balance.isGreaterThanZero()) {
balance = payComponentsNewMechanism(balance, transactionDate);
updateSummaryAndPerformanceHistory(accountPaymentEntity, personnel, transactionDate);
}
return balance;
}
public Money payComponentsNewMechanism(Money paymentAmount, Date paymentDate) {
initPaymentAllocation(paymentAmount.getCurrency());
Money balanceAmount = paymentAmount;
balanceAmount = payPrincipal(balanceAmount);
if (isPrincipalZero()) {
// the entire principal was paid in advance so no interest due on this installment.
}
balanceAmount = payMiscPenalty(balanceAmount);
balanceAmount = payPenalty(balanceAmount);
balanceAmount = payMiscFees(balanceAmount);
balanceAmount = payFees(balanceAmount);
balanceAmount = payInterest(balanceAmount);
recordPayment(paymentDate);
return balanceAmount;
}
boolean hasFees() {
return CollectionUtils.isNotEmpty(accountFeesActionDetails);
}
boolean hasPenalties() {
return CollectionUtils.isNotEmpty(loanPenaltiesSchedule);
}
public void setPaymentAllocation(PaymentAllocation paymentAllocation) {
this.paymentAllocation = paymentAllocation;
}
double getExtraInterestPaidAsDouble() {
return getExtraInterestPaid().getAmount().doubleValue();
}
public Money getEffectiveInterest() {
return interest.add(getExtraInterest());
}
public void updatePrincipalPaidby(AccountPaymentEntity accountPayment, PersonnelBO personnel) {
initPaymentAllocation(getCurrency());
this.principalPaid = this.principalPaid.add(accountPayment.getAmount());
this.principal = this.principal.add(accountPayment.getAmount());
this.getPaymentAllocation().allocateForPrincipal(accountPayment.getAmount());
}
public LoanPenaltyScheduleEntity getPenaltyScheduleEntity(final Short penaltyId) {
List<LoanPenaltyScheduleEntity> list = new ArrayList<LoanPenaltyScheduleEntity>(getLoanPenaltyScheduleEntities());
LoanPenaltyScheduleEntity entity = null;
for(LoanPenaltyScheduleEntity item : list) {
if(item.getPenalty().getPenaltyId().equals(penaltyId)) {
entity = item;
break;
}
}
return entity;
}
Money removePenalties(Short penaltyId) {
Money penaltyAmount = null;
LoanPenaltyScheduleEntity objectToRemove = null;
Set<LoanPenaltyScheduleEntity> loanPenaltyScheduleEntitySet = this.getLoanPenaltyScheduleEntities();
for (LoanPenaltyScheduleEntity loanPenaltyScheduleEntity : loanPenaltyScheduleEntitySet) {
if (loanPenaltyScheduleEntity.getPenalty().getPenaltyId().equals(penaltyId)
&& (loanPenaltyScheduleEntity.getPenaltyAmountPaid() == null || loanPenaltyScheduleEntity
.getPenaltyAmountPaid().isZero())) {
objectToRemove = loanPenaltyScheduleEntity;
penaltyAmount = objectToRemove.getPenaltyAmount();
break;
} else if (loanPenaltyScheduleEntity.getPenalty().getPenaltyId().equals(penaltyId)
&& loanPenaltyScheduleEntity.getPenaltyAmountPaid() != null
&& loanPenaltyScheduleEntity.getPenaltyAmountPaid().isGreaterThanZero()) {
penaltyAmount = loanPenaltyScheduleEntity.getPenaltyAmount().subtract(
loanPenaltyScheduleEntity.getPenaltyAmountPaid());
loanPenaltyScheduleEntity.setPenaltyAmount(loanPenaltyScheduleEntity.getPenaltyAmountPaid());
setPenalty(getPenaltyPaid());
break;
}
}
if (objectToRemove != null) {
this.removeLoanPenaltySchedule(objectToRemove);
penalty = penalty.subtract(penaltyAmount);
}
return penaltyAmount;
}
public Money getTotalAmountOfInstallment(){
Money money = new Money(getCurrency());
money = money.add(getTotalFees())
.add(getTotalPenalty())
.add(getPrincipal())
.add(getInterest())
.add(getMiscFee());
return money;
}
public Money getTotalPaidAmount(){
Money money = new Money(getCurrency());
money = money.add(getInterestPaid())
.add(getTotalFeesPaid())
.add(getTotalPenaltyPaid())
.add(getPrincipalPaid())
.add(getMiscFeePaid());
return money;
}
public BigDecimal getPaidProportion() {
BigDecimal proportion = getTotalPaidAmount().divide(getTotalAmountOfInstallment());
if(proportion.compareTo(BigDecimal.ZERO)<0) {
proportion = BigDecimal.ZERO;
} else if(proportion.compareTo(BigDecimal.ONE)>0) {
proportion = BigDecimal.ONE;
}
return proportion;
}
public BigDecimal getProportionPaidBy(BigDecimal amount){
return new BigDecimal(amount.doubleValue()/getTotalAmountOfInstallment().getAmount().doubleValue());
}
public Money getAmountToBePaidToGetExpectedProportion(BigDecimal expected) {
Money amount = getTotalAmountOfInstallment().multiply(expected);
amount = amount.subtract(getTotalPaidAmount());
return amount;
}
public void removeAllFees() {
while(getAccountFeesActionDetails().iterator().hasNext()) {
AccountFeesActionDetailEntity fee = getAccountFeesActionDetails().iterator().next();
removeAccountFeesActionDetailEntity(fee);
}
}
public void removeAllPenalties() {
while(getLoanPenaltyScheduleEntities().iterator().hasNext()) {
LoanPenaltyScheduleEntity penalty = getLoanPenaltyScheduleEntities().iterator().next();
removePenalties(penalty.getPenalty().getPenaltyId());
}
}
}