Merge pull request #655 from pramodn02/MIFOSX-730
fix for percentage based approach
This commit is contained in:
+12
-3
@@ -11,14 +11,19 @@ public class LoanChargeCannotBeUpdatedException extends AbstractPlatformDomainRu
|
||||
|
||||
/*** enum of reasons of why Loan Charge cannot be waived **/
|
||||
public static enum LOAN_CHARGE_CANNOT_BE_UPDATED_REASON {
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE;
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE,CHARGE_INSTALLMENT_FEE;
|
||||
|
||||
public String errorMessage() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
return "This loan charge has been partially/completely paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "This loan charge has already been waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) { return "This charge cannot be updated as the loan it is associated with is not in submitted and pending approval stage"; }
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) {
|
||||
return "This charge cannot be updated as the loan it is associated with is not in submitted and pending approval stage";
|
||||
} else if(name().toString().equalsIgnoreCase("CHARGE_INSTALLMENT_FEE")){
|
||||
return "Installment charge update is not allowed";
|
||||
}
|
||||
|
||||
return name().toString();
|
||||
}
|
||||
|
||||
@@ -27,7 +32,11 @@ public class LoanChargeCannotBeUpdatedException extends AbstractPlatformDomainRu
|
||||
return "error.msg.loan.charge.already.paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "error.msg.loan.charge.already.waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) { return "error.msg.loan.charge.associated.loan.not.in.submitted.and.pending.approval.stage"; }
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE")) {
|
||||
return "error.msg.loan.charge.associated.loan.not.in.submitted.and.pending.approval.stage";
|
||||
} else if(name().toString().equalsIgnoreCase("CHARGE_INSTALLMENT_FEE")){
|
||||
return "error.msg.loan.charge.installment.fee.update.not.allowed";
|
||||
}
|
||||
return name().toString();
|
||||
}
|
||||
}
|
||||
|
||||
+52
-5
@@ -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<Long> {
|
||||
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<Long> {
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
if (setOfLoanCharges().contains(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);
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
@@ -689,6 +698,32 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
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<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
@@ -803,15 +838,19 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
+35
-13
@@ -117,18 +117,27 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
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<Long> {
|
||||
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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<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);
|
||||
|
||||
@@ -328,7 +343,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
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<Long> {
|
||||
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<Long> {
|
||||
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;
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+5
@@ -890,6 +890,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
// yet to be approved (are in submitted and pending status)
|
||||
if (!loan.status().isSubmittedAndPendingApproval()) { throw new LoanChargeCannotBeUpdatedException(
|
||||
LOAN_CHARGE_CANNOT_BE_UPDATED_REASON.LOAN_NOT_IN_SUBMITTED_AND_PENDING_APPROVAL_STAGE, loanCharge.getId()); }
|
||||
|
||||
if(loanCharge.isInstalmentFee()){
|
||||
throw new LoanChargeCannotBeUpdatedException(
|
||||
LOAN_CHARGE_CANNOT_BE_UPDATED_REASON.CHARGE_INSTALLMENT_FEE, loanCharge.getId());
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = loan.updateLoanCharge(loanCharge, command);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user