From a54931bfcd06c5124cfaf1f9721929f548dee767 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Mon, 4 Nov 2013 22:08:36 +0530 Subject: [PATCH] fix for percentage based approach --- .../portfolio/loanaccount/domain/Loan.java | 57 +++++++++++++++++-- .../loanaccount/domain/LoanCharge.java | 48 +++++++++++----- .../service/LoanChargeAssembler.java | 2 +- 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index a7f8053bb..7b9306c51 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -58,6 +58,7 @@ import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.portfolio.accountdetails.domain.AccountType; 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.client.domain.Client; import org.mifosplatform.portfolio.collateral.data.CollateralData; @@ -452,15 +453,19 @@ public class Loan extends AbstractPersistable { loanCharge.update(this); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal chargeAmt = BigDecimal.ZERO; + BigDecimal totalChargeAmt = BigDecimal.ZERO; if(loanCharge.getChargeCalculation().isPercentageBased()){ chargeAmt = loanCharge.getPercentage(); + if(loanCharge.isInstalmentFee()){ + totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge); + } }else{ chargeAmt = loanCharge.amount(); if(loanCharge.isInstalmentFee()){ 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); return changedTransactionDetail; } @@ -636,7 +641,11 @@ public class Loan extends AbstractPersistable { validateLoanIsNotClosed(loanCharge); if (setOfLoanCharges().contains(loanCharge)) { BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); - final Map loanChargeChanges = loanCharge.update(command, amount); + BigDecimal totalChargeAmt = BigDecimal.ZERO; + if(loanCharge.isInstalmentFee()){ + totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge); + } + final Map loanChargeChanges = loanCharge.update(command, amount, totalChargeAmt); actualChanges.putAll(loanChargeChanges); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); } @@ -689,6 +698,32 @@ public class Loan extends AbstractPersistable { } 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, final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds) { @@ -803,15 +838,19 @@ public class Loan extends AbstractPersistable { loanCharge.update(this); BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal chargeAmt = BigDecimal.ZERO; + BigDecimal totalChargeAmt = BigDecimal.ZERO; if(loanCharge.getChargeCalculation().isPercentageBased()){ chargeAmt = loanCharge.getPercentage(); + if(loanCharge.isInstalmentFee()){ + totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge); + } }else{ chargeAmt = loanCharge.amount(); if(loanCharge.isInstalmentFee()){ 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); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); @@ -1062,15 +1101,19 @@ public class Loan extends AbstractPersistable { for (final LoanCharge loanCharge : possiblyModifedLoanCharges) { BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal chargeAmt = BigDecimal.ZERO; + BigDecimal totalChargeAmt = BigDecimal.ZERO; if(loanCharge.getChargeCalculation().isPercentageBased()){ chargeAmt = loanCharge.getPercentage(); + if(loanCharge.isInstalmentFee()){ + totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge); + } }else{ chargeAmt = loanCharge.amount(); if(loanCharge.isInstalmentFee()){ 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()); } } @@ -1204,15 +1247,19 @@ public class Loan extends AbstractPersistable { for (final LoanCharge loanCharge : setOfLoanCharges()) { BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); BigDecimal chargeAmt = BigDecimal.ZERO; + BigDecimal totalChargeAmt = BigDecimal.ZERO; if(loanCharge.getChargeCalculation().isPercentageBased()){ chargeAmt = loanCharge.getPercentage(); + if(loanCharge.isInstalmentFee()){ + totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge); + } }else{ chargeAmt = loanCharge.amount(); if(loanCharge.isInstalmentFee()){ 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()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java index abc4ec0aa..2f1edd6d9 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java @@ -117,18 +117,27 @@ public class LoanCharge extends AbstractPersistable { default: 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, - chargePaymentMode, null); + chargePaymentMode, null, loanCharge); } - + /* * loanPrincipal is required for charges that are percentage based */ public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate, 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() { @@ -137,7 +146,7 @@ public class LoanCharge extends AbstractPersistable { public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount, 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.charge = chargeDefinition; this.penaltyCharge = chargeDefinition.isPenalty(); @@ -176,11 +185,11 @@ public class LoanCharge extends AbstractPersistable { if (chargePaymentMode != null) { this.chargePaymentMode = chargePaymentMode.getValue(); } - populateDerivedFields(loanPrincipal, chargeAmount,numberOfRepayments); + populateDerivedFields(loanPrincipal, chargeAmount,numberOfRepayments, loanCharge); 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)) { case INVALID: @@ -213,7 +222,10 @@ public class LoanCharge extends AbstractPersistable { case PERCENT_OF_INTEREST: this.percentage = chargeAmount; 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.amountOutstanding = calculateOutstanding(); this.amountWaived = null; @@ -259,7 +271,7 @@ public class LoanCharge extends AbstractPersistable { 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) { this.dueDate = dueDate.toDate(); } @@ -283,14 +295,17 @@ public class LoanCharge extends AbstractPersistable { case PERCENT_OF_INTEREST: this.percentage = amount; 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(); break; } } } - public Map update(final JsonCommand command, final BigDecimal amount) { + public Map update(final JsonCommand command, final BigDecimal amount, BigDecimal loanCharge) { final Map actualChanges = new LinkedHashMap(7); @@ -328,7 +343,10 @@ public class LoanCharge extends AbstractPersistable { case PERCENT_OF_INTEREST: this.percentage = newValue; 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(); break; } @@ -349,7 +367,7 @@ public class LoanCharge extends AbstractPersistable { 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; } @@ -396,7 +414,11 @@ public class LoanCharge extends AbstractPersistable { 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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java index e598ac57b..66f615939 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeAssembler.java @@ -91,7 +91,7 @@ public class LoanChargeAssembler { final LoanCharge loanCharge = this.loanChargeRepository.findOne(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); }