fix for percentage based approach

This commit is contained in:
Pramod Nuthakki
2013-11-04 22:08:36 +05:30
parent d24e076f05
commit a54931bfcd
3 changed files with 88 additions and 19 deletions
@@ -58,6 +58,7 @@ import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.collateral.data.CollateralData; import org.mifosplatform.portfolio.collateral.data.CollateralData;
@@ -452,15 +453,19 @@ public class Loan extends AbstractPersistable<Long> {
loanCharge.update(this); loanCharge.update(this);
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
BigDecimal chargeAmt = BigDecimal.ZERO; BigDecimal chargeAmt = BigDecimal.ZERO;
BigDecimal totalChargeAmt = BigDecimal.ZERO;
if(loanCharge.getChargeCalculation().isPercentageBased()){ if(loanCharge.getChargeCalculation().isPercentageBased()){
chargeAmt = loanCharge.getPercentage(); chargeAmt = loanCharge.getPercentage();
if(loanCharge.isInstalmentFee()){
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
}
}else{ }else{
chargeAmt = loanCharge.amount(); chargeAmt = loanCharge.amount();
if(loanCharge.isInstalmentFee()){ if(loanCharge.isInstalmentFee()){
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
} }
} }
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments(), totalChargeAmt);
setOfLoanCharges().add(loanCharge); setOfLoanCharges().add(loanCharge);
return changedTransactionDetail; return changedTransactionDetail;
} }
@@ -636,7 +641,11 @@ public class Loan extends AbstractPersistable<Long> {
validateLoanIsNotClosed(loanCharge); validateLoanIsNotClosed(loanCharge);
if (setOfLoanCharges().contains(loanCharge)) { if (setOfLoanCharges().contains(loanCharge)) {
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
final Map<String, Object> loanChargeChanges = loanCharge.update(command, amount); BigDecimal totalChargeAmt = BigDecimal.ZERO;
if(loanCharge.isInstalmentFee()){
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
}
final Map<String, Object> loanChargeChanges = loanCharge.update(command, amount, totalChargeAmt);
actualChanges.putAll(loanChargeChanges); actualChanges.putAll(loanChargeChanges);
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
} }
@@ -689,6 +698,32 @@ public class Loan extends AbstractPersistable<Long> {
} }
return amount; return amount;
} }
private BigDecimal calculatePerInstallmentChargeAmount(final LoanCharge loanCharge){
return calculatePerInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage());
}
public BigDecimal calculatePerInstallmentChargeAmount(ChargeCalculationType calculationType,BigDecimal percentage){
Money amount = Money.zero(getCurrency());
for(final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments){
Money percentOf = Money.zero(getCurrency());
switch (calculationType) {
case PERCENT_OF_AMOUNT:
percentOf = installment.getPrincipal(getCurrency());
break;
case PERCENT_OF_AMOUNT_AND_INTEREST:
percentOf = installment.getPrincipal(getCurrency()).plus(installment.getInterestCharged(getCurrency()));
break;
case PERCENT_OF_INTEREST:
percentOf = installment.getInterestCharged(getCurrency());
break;
default:
break;
}
amount = amount.plus(LoanCharge.percentageOf(percentOf.getAmount(),percentage));
}
return amount.getAmount();
}
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine, public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) { final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
@@ -803,15 +838,19 @@ public class Loan extends AbstractPersistable<Long> {
loanCharge.update(this); loanCharge.update(this);
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
BigDecimal chargeAmt = BigDecimal.ZERO; BigDecimal chargeAmt = BigDecimal.ZERO;
BigDecimal totalChargeAmt = BigDecimal.ZERO;
if(loanCharge.getChargeCalculation().isPercentageBased()){ if(loanCharge.getChargeCalculation().isPercentageBased()){
chargeAmt = loanCharge.getPercentage(); chargeAmt = loanCharge.getPercentage();
if(loanCharge.isInstalmentFee()){
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
}
}else{ }else{
chargeAmt = loanCharge.amount(); chargeAmt = loanCharge.amount();
if(loanCharge.isInstalmentFee()){ if(loanCharge.isInstalmentFee()){
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
} }
} }
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments(), totalChargeAmt);
} }
setOfLoanCharges().addAll(loanCharges); setOfLoanCharges().addAll(loanCharges);
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
@@ -1062,15 +1101,19 @@ public class Loan extends AbstractPersistable<Long> {
for (final LoanCharge loanCharge : possiblyModifedLoanCharges) { for (final LoanCharge loanCharge : possiblyModifedLoanCharges) {
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
BigDecimal chargeAmt = BigDecimal.ZERO; BigDecimal chargeAmt = BigDecimal.ZERO;
BigDecimal totalChargeAmt = BigDecimal.ZERO;
if(loanCharge.getChargeCalculation().isPercentageBased()){ if(loanCharge.getChargeCalculation().isPercentageBased()){
chargeAmt = loanCharge.getPercentage(); chargeAmt = loanCharge.getPercentage();
if(loanCharge.isInstalmentFee()){
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
}
}else{ }else{
chargeAmt = loanCharge.amount(); chargeAmt = loanCharge.amount();
if(loanCharge.isInstalmentFee()){ if(loanCharge.isInstalmentFee()){
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
} }
} }
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments(), totalChargeAmt);
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
} }
} }
@@ -1204,15 +1247,19 @@ public class Loan extends AbstractPersistable<Long> {
for (final LoanCharge loanCharge : setOfLoanCharges()) { for (final LoanCharge loanCharge : setOfLoanCharges()) {
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
BigDecimal chargeAmt = BigDecimal.ZERO; BigDecimal chargeAmt = BigDecimal.ZERO;
BigDecimal totalChargeAmt = BigDecimal.ZERO;
if(loanCharge.getChargeCalculation().isPercentageBased()){ if(loanCharge.getChargeCalculation().isPercentageBased()){
chargeAmt = loanCharge.getPercentage(); chargeAmt = loanCharge.getPercentage();
if(loanCharge.isInstalmentFee()){
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
}
}else{ }else{
chargeAmt = loanCharge.amount(); chargeAmt = loanCharge.amount();
if(loanCharge.isInstalmentFee()){ if(loanCharge.isInstalmentFee()){
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
} }
} }
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments(), totalChargeAmt);
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
} }
@@ -117,18 +117,27 @@ public class LoanCharge extends AbstractPersistable<Long> {
default: default:
break; break;
} }
BigDecimal loanCharge = BigDecimal.ZERO;
if(ChargeTimeType.fromInt(chargeDefinition.getChargeTime()).equals(ChargeTimeType.INSTALMENT_FEE)){
BigDecimal percentage = amount;
if(percentage == null){
percentage = chargeDefinition.getAmount();
}
loanCharge = loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation()),percentage);
}
return new LoanCharge(loan, chargeDefinition, amountPercentageAppliedTo, amount, chargeTime, chargeCalculation, dueDate, return new LoanCharge(loan, chargeDefinition, amountPercentageAppliedTo, amount, chargeTime, chargeCalculation, dueDate,
chargePaymentMode, null); chargePaymentMode, null, loanCharge);
} }
/* /*
* loanPrincipal is required for charges that are percentage based * loanPrincipal is required for charges that are percentage based
*/ */
public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate,
final ChargePaymentMode chargePaymentMode,final Integer numberOfRepayments) { final ChargePaymentMode chargePaymentMode,final Integer numberOfRepayments) {
return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode, numberOfRepayments); return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode, numberOfRepayments, BigDecimal.ZERO);
} }
protected LoanCharge() { protected LoanCharge() {
@@ -137,7 +146,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate,
final ChargePaymentMode chargePaymentMode, Integer numberOfRepayments) { final ChargePaymentMode chargePaymentMode, Integer numberOfRepayments, BigDecimal loanCharge) {
this.loan = loan; this.loan = loan;
this.charge = chargeDefinition; this.charge = chargeDefinition;
this.penaltyCharge = chargeDefinition.isPenalty(); this.penaltyCharge = chargeDefinition.isPenalty();
@@ -176,11 +185,11 @@ public class LoanCharge extends AbstractPersistable<Long> {
if (chargePaymentMode != null) { if (chargePaymentMode != null) {
this.chargePaymentMode = chargePaymentMode.getValue(); this.chargePaymentMode = chargePaymentMode.getValue();
} }
populateDerivedFields(loanPrincipal, chargeAmount,numberOfRepayments); populateDerivedFields(loanPrincipal, chargeAmount,numberOfRepayments, loanCharge);
this.paid = determineIfFullyPaid(); this.paid = determineIfFullyPaid();
} }
private void populateDerivedFields(final BigDecimal amountPercentageAppliedTo, final BigDecimal chargeAmount,Integer numberOfRepayments) { private void populateDerivedFields(final BigDecimal amountPercentageAppliedTo, final BigDecimal chargeAmount,Integer numberOfRepayments, BigDecimal loanCharge) {
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) { switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case INVALID: case INVALID:
@@ -213,7 +222,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
case PERCENT_OF_INTEREST: case PERCENT_OF_INTEREST:
this.percentage = chargeAmount; this.percentage = chargeAmount;
this.amountPercentageAppliedTo = amountPercentageAppliedTo; this.amountPercentageAppliedTo = amountPercentageAppliedTo;
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); if(loanCharge.compareTo(BigDecimal.ZERO) == 0){
loanCharge = percentageOf(this.amountPercentageAppliedTo);
}
this.amount = minimumAndMaximumCap(loanCharge);
this.amountPaid = null; this.amountPaid = null;
this.amountOutstanding = calculateOutstanding(); this.amountOutstanding = calculateOutstanding();
this.amountWaived = null; this.amountWaived = null;
@@ -259,7 +271,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
this.loan = loan; this.loan = loan;
} }
public void update(final BigDecimal amount, final LocalDate dueDate, final BigDecimal loanPrincipal, Integer numberOfRepayments) { public void update(final BigDecimal amount, final LocalDate dueDate, final BigDecimal loanPrincipal, Integer numberOfRepayments, BigDecimal loanCharge) {
if (dueDate != null) { if (dueDate != null) {
this.dueDate = dueDate.toDate(); this.dueDate = dueDate.toDate();
} }
@@ -283,14 +295,17 @@ public class LoanCharge extends AbstractPersistable<Long> {
case PERCENT_OF_INTEREST: case PERCENT_OF_INTEREST:
this.percentage = amount; this.percentage = amount;
this.amountPercentageAppliedTo = loanPrincipal; this.amountPercentageAppliedTo = loanPrincipal;
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); if(loanCharge.compareTo(BigDecimal.ZERO) == 0){
loanCharge = percentageOf(this.amountPercentageAppliedTo);
}
this.amount = minimumAndMaximumCap(loanCharge);
this.amountOutstanding = calculateOutstanding(); this.amountOutstanding = calculateOutstanding();
break; break;
} }
} }
} }
public Map<String, Object> update(final JsonCommand command, final BigDecimal amount) { public Map<String, Object> update(final JsonCommand command, final BigDecimal amount, BigDecimal loanCharge) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
@@ -328,7 +343,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
case PERCENT_OF_INTEREST: case PERCENT_OF_INTEREST:
this.percentage = newValue; this.percentage = newValue;
this.amountPercentageAppliedTo = amount; this.amountPercentageAppliedTo = amount;
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); if(loanCharge.compareTo(BigDecimal.ZERO) == 0){
loanCharge = percentageOf(this.amountPercentageAppliedTo);
}
this.amount = minimumAndMaximumCap(loanCharge);
this.amountOutstanding = calculateOutstanding(); this.amountOutstanding = calculateOutstanding();
break; break;
} }
@@ -349,7 +367,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.INSTALMENT_FEE); return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.INSTALMENT_FEE);
} }
private boolean isGreaterThanZero(final BigDecimal value) { private static boolean isGreaterThanZero(final BigDecimal value) {
return value.compareTo(BigDecimal.ZERO) == 1; return value.compareTo(BigDecimal.ZERO) == 1;
} }
@@ -396,7 +414,11 @@ public class LoanCharge extends AbstractPersistable<Long> {
return this.amount.subtract(totalAccountedFor); return this.amount.subtract(totalAccountedFor);
} }
private BigDecimal percentageOf(final BigDecimal value, final BigDecimal percentage) { public BigDecimal percentageOf(final BigDecimal value) {
return percentageOf(value, this.percentage);
}
public static BigDecimal percentageOf(final BigDecimal value,BigDecimal percentage) {
BigDecimal percentageOf = BigDecimal.ZERO; BigDecimal percentageOf = BigDecimal.ZERO;
@@ -91,7 +91,7 @@ public class LoanChargeAssembler {
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId); final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); } if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
loanCharge.update(amount, dueDate, principal, numberOfRepayments); loanCharge.update(amount, dueDate, principal, numberOfRepayments, BigDecimal.ZERO);
loanCharges.add(loanCharge); loanCharges.add(loanCharge);
} }