diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java index 99e607b5b..e5c4e7e27 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeCalculationType.java @@ -56,6 +56,14 @@ public enum ChargeCalculationType { return this.value.equals(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue()); } + public boolean isPercentageOfAmountAndInterest() { + return this.value.equals(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST.getValue()); + } + + public boolean isPercentageOfInterest() { + return this.value.equals(ChargeCalculationType.PERCENT_OF_INTEREST.getValue()); + } + public boolean isFlat() { return this.value.equals(ChargeCalculationType.FLAT.getValue()); } @@ -63,4 +71,8 @@ public enum ChargeCalculationType { public boolean isAllowedSavingsChargeCalculationType() { return isFlat() || isPercentageOfAmount(); } + + public boolean isPercentageBased(){ + return isPercentageOfAmount() || isPercentageOfAmountAndInterest() || isPercentageOfInterest(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java index 97f0d602b..2419dfe92 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/domain/ChargeTimeType.java @@ -16,7 +16,8 @@ public enum ChargeTimeType { SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings ANNUAL_FEE(6, "chargeTimeType.annualFee"), // only for savings - MONTHLY_FEE(7, "chargeTimeType.monthlyFee"); // only for savings + MONTHLY_FEE(7, "chargeTimeType.monthlyFee"), // only for savings + INSTALMENT_FEE(8,"chargeTimeType.instalmentFee");//only for loan charges private final Integer value; private final String code; @@ -47,34 +48,37 @@ public enum ChargeTimeType { public static ChargeTimeType fromInt(final Integer chargeTime) { ChargeTimeType chargeTimeType = ChargeTimeType.INVALID; if (chargeTime != null) { - switch (chargeTime) { - case 1: - chargeTimeType = DISBURSEMENT; - break; - case 2: - chargeTimeType = SPECIFIED_DUE_DATE; - break; - case 3: - chargeTimeType = SAVINGS_ACTIVATION; - break; - case 4: - chargeTimeType = SAVINGS_CLOSURE; - break; - case 5: - chargeTimeType = WITHDRAWAL_FEE; - break; - case 6: - chargeTimeType = ANNUAL_FEE; - break; - case 7: - chargeTimeType = MONTHLY_FEE; - break; - default: - chargeTimeType = INVALID; - break; - } + switch (chargeTime) { + case 1: + chargeTimeType = DISBURSEMENT; + break; + case 2: + chargeTimeType = SPECIFIED_DUE_DATE; + break; + case 3: + chargeTimeType = SAVINGS_ACTIVATION; + break; + case 4: + chargeTimeType = SAVINGS_CLOSURE; + break; + case 5: + chargeTimeType = WITHDRAWAL_FEE; + break; + case 6: + chargeTimeType = ANNUAL_FEE; + break; + case 7: + chargeTimeType = MONTHLY_FEE; + break; + case 8: + chargeTimeType = INSTALMENT_FEE; + break; + default: + chargeTimeType = INVALID; + break; } - return chargeTimeType; + } + return chargeTimeType; } public boolean isTimeOfDisbursement() { @@ -105,8 +109,13 @@ public enum ChargeTimeType { return this.value.equals(ChargeTimeType.MONTHLY_FEE.getValue()); } + + public boolean isInstalmentFee() { + return this.value.equals(ChargeTimeType.INSTALMENT_FEE.getValue()); + } + public boolean isAllowedLoanChargeTime() { - return isTimeOfDisbursement() || isOnSpecifiedDueDate(); + return isTimeOfDisbursement() || isOnSpecifiedDueDate() || isInstalmentFee(); } public boolean isAllowedSavingsChargeTime() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBePayedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBePayedException.java index 5db80c6f7..0d01e1b2a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBePayedException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBePayedException.java @@ -11,7 +11,7 @@ public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRule /*** enum of reasons of why Loan Charge cannot be waived **/ public static enum LOAN_CHARGE_CANNOT_BE_PAYED_REASON { - ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, CHARGE_NOT_ACCOUNT_TRANSFER; + ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, CHARGE_NOT_ACCOUNT_TRANSFER,CHARGE_NOT_PAYABLE; public String errorMessage() { if (name().toString().equalsIgnoreCase("ALREADY_PAID")) { @@ -20,7 +20,11 @@ public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRule return "This loan charge has already been waived"; } else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { return "This loan charge can be payed as the loan associated with it is currently inactive"; - } else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) { return "This loan charge can be payed as the charge payment mode is not account transfer"; } + } else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) { + return "This loan charge can be payed as the charge payment mode is not account transfer"; + }else if (name().toString().equalsIgnoreCase("CHARGE_NOT_PAYABLE")) { + return "This loan charge is not Payable through account transfer"; + } return name().toString(); } @@ -32,7 +36,11 @@ public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRule return "error.msg.loan.charge.already.waived"; } else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { return "error.msg.loan.charge.associated.loan.inactive"; - } else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) { return "error.msg.loan.charge.payment.mode.not.account.transfer"; } + } else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) { + return "error.msg.loan.charge.payment.mode.not.account.transfer"; + }else if (name().toString().equalsIgnoreCase("CHARGE_NOT_PAYABLE")) { + return "error.msg.loan.charge.payment.not.allowed.account.transfer"; + } return name().toString(); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBeWaivedException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBeWaivedException.java index 24edfdd3f..3ad6c9e57 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBeWaivedException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/exception/LoanChargeCannotBeWaivedException.java @@ -11,14 +11,19 @@ public class LoanChargeCannotBeWaivedException extends AbstractPlatformDomainRul /*** enum of reasons of why Loan Charge cannot be waived **/ public static enum LOAN_CHARGE_CANNOT_BE_WAIVED_REASON { - ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE; + ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, WAIVE_NOT_ALLOWED_FOR_CHARGE; public String errorMessage() { if (name().toString().equalsIgnoreCase("ALREADY_PAID")) { return "This loan charge has been completely paid"; } else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) { return "This loan charge has already been waived"; - } else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { return "This loan charge can be waived as the loan associated with it is currently inactive"; } + } else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { + return "This loan charge can be waived as the loan associated with it is currently inactive"; + } else if(name().toString().equalsIgnoreCase("WAIVE_NOT_ALLOWED_FOR_CHARGE")){ + return "This loan charge can be waived"; + } + return name().toString(); } @@ -27,7 +32,11 @@ public class LoanChargeCannotBeWaivedException extends AbstractPlatformDomainRul 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_INACTIVE")) { return "error.msg.loan.charge.associated.loan.inactive"; } + } else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { + return "error.msg.loan.charge.associated.loan.inactive"; + } else if(name().toString().equalsIgnoreCase("WAIVE_NOT_ALLOWED_FOR_CHARGE")) { + return "error.msg.loan.charge.waive.not.allowed"; + } return name().toString(); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java index bbad5faa1..648219dc6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeDropdownReadPlatformServiceImpl.java @@ -27,9 +27,9 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead public List retrieveCalculationTypes() { return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), - chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT) - // chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST), - // chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST) + chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT), + chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST), + chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST) ); } @@ -64,12 +64,12 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead @Override public List retrieveLoanCalculationTypes() { - return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT)); + return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST), chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST)); } @Override public List retrieveLoanCollectionTimeTypes() { - return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE)); + return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE),chargeTimeType(ChargeTimeType.INSTALMENT_FEE)); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java index 113e709bd..95ba5e3da 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/service/ChargeEnumerations.java @@ -43,6 +43,9 @@ public class ChargeEnumerations { case MONTHLY_FEE: optionData = new EnumOptionData(ChargeTimeType.MONTHLY_FEE.getValue().longValue(), ChargeTimeType.MONTHLY_FEE.getCode(), "Monthly Fee"); break; + case INSTALMENT_FEE: + optionData = new EnumOptionData(ChargeTimeType.INSTALMENT_FEE.getValue().longValue(), ChargeTimeType.INSTALMENT_FEE.getCode(), "Instalment Fee"); + break; default: optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid"); break; 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 10559abb0..a7f8053bb 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 @@ -427,9 +427,6 @@ public class Loan extends AbstractPersistable { validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); - loanCharge.update(this); - setOfLoanCharges().add(loanCharge); - this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory @@ -452,6 +449,19 @@ public class Loan extends AbstractPersistable { } updateLoanSummaryDerivedFields(); + loanCharge.update(this); + BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); + BigDecimal chargeAmt = BigDecimal.ZERO; + if(loanCharge.getChargeCalculation().isPercentageBased()){ + chargeAmt = loanCharge.getPercentage(); + }else{ + chargeAmt = loanCharge.amount(); + if(loanCharge.isInstalmentFee()){ + chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); + } + } + loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); + setOfLoanCharges().add(loanCharge); return changedTransactionDetail; } @@ -624,9 +634,9 @@ public class Loan extends AbstractPersistable { final Map actualChanges = new LinkedHashMap(3); validateLoanIsNotClosed(loanCharge); - if (setOfLoanCharges().contains(loanCharge)) { - final Map loanChargeChanges = loanCharge.update(command, getPrincpal().getAmount()); + BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); + final Map loanChargeChanges = loanCharge.update(command, amount); actualChanges.putAll(loanChargeChanges); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); } @@ -656,6 +666,30 @@ public class Loan extends AbstractPersistable { return actualChanges; } + /** + * @param loanCharge + * @return + */ + private BigDecimal calculateAmountPercentageAppliedTo(final LoanCharge loanCharge) { + BigDecimal amount = BigDecimal.ZERO; + switch (loanCharge.getChargeCalculation()) { + case PERCENT_OF_AMOUNT: + amount = getPrincpal().getAmount(); + break; + case PERCENT_OF_AMOUNT_AND_INTEREST: + final BigDecimal totalInterestCharged = this.loanSummaryWrapper.calculateTotalInterestCharged( + repaymentScheduleInstallments, getCurrency()).getAmount(); + amount = getPrincpal().getAmount().add(totalInterestCharged); + break; + case PERCENT_OF_INTEREST: + amount = getSummary().getTotalInterestCharged(); + break; + default: + break; + } + return amount; + } + public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds) { @@ -765,7 +799,21 @@ public class Loan extends AbstractPersistable { public void updateLoanCharges(final Set loanCharges) { setOfLoanCharges().clear(); - setOfLoanCharges().addAll(associateChargesWithThisLoan(loanCharges)); + for (final LoanCharge loanCharge : loanCharges) { + loanCharge.update(this); + BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); + BigDecimal chargeAmt = BigDecimal.ZERO; + if(loanCharge.getChargeCalculation().isPercentageBased()){ + chargeAmt = loanCharge.getPercentage(); + }else{ + chargeAmt = loanCharge.amount(); + if(loanCharge.isInstalmentFee()){ + chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); + } + } + loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); + } + setOfLoanCharges().addAll(loanCharges); updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); } @@ -1012,6 +1060,17 @@ public class Loan extends AbstractPersistable { actualChanges.put("recalculateLoanSchedule", true); for (final LoanCharge loanCharge : possiblyModifedLoanCharges) { + BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); + BigDecimal chargeAmt = BigDecimal.ZERO; + if(loanCharge.getChargeCalculation().isPercentageBased()){ + chargeAmt = loanCharge.getPercentage(); + }else{ + chargeAmt = loanCharge.amount(); + if(loanCharge.isInstalmentFee()){ + chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); + } + } + loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate()); } } @@ -1143,6 +1202,17 @@ public class Loan extends AbstractPersistable { // charges are optional for (final LoanCharge loanCharge : setOfLoanCharges()) { + BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge); + BigDecimal chargeAmt = BigDecimal.ZERO; + if(loanCharge.getChargeCalculation().isPercentageBased()){ + chargeAmt = loanCharge.getPercentage(); + }else{ + chargeAmt = loanCharge.amount(); + if(loanCharge.isInstalmentFee()){ + chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments())); + } + } + loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments()); 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 0d97ec2a6..abc4ec0aa 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 @@ -103,9 +103,23 @@ public class LoanCharge extends AbstractPersistable { final ChargeTimeType chargeTime = null; final ChargeCalculationType chargeCalculation = null; final ChargePaymentMode chargePaymentMode = null; + BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO; + switch (ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation())) { + case PERCENT_OF_AMOUNT: + amountPercentageAppliedTo = loan.getPrincpal().getAmount(); + break; + case PERCENT_OF_AMOUNT_AND_INTEREST: + amountPercentageAppliedTo = loan.getPrincpal().getAmount().add(loan.getSummary().getTotalInterestCharged()); + break; + case PERCENT_OF_INTEREST: + amountPercentageAppliedTo = loan.getSummary().getTotalInterestCharged(); + break; + default: + break; + } - return new LoanCharge(loan, chargeDefinition, loan.getPrincpal().getAmount(), amount, chargeTime, chargeCalculation, dueDate, - chargePaymentMode); + return new LoanCharge(loan, chargeDefinition, amountPercentageAppliedTo, amount, chargeTime, chargeCalculation, dueDate, + chargePaymentMode, null); } /* @@ -113,8 +127,8 @@ public class LoanCharge extends AbstractPersistable { */ 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) { - return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode); + final ChargePaymentMode chargePaymentMode,final Integer numberOfRepayments) { + return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode, numberOfRepayments); } protected LoanCharge() { @@ -123,7 +137,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) { + final ChargePaymentMode chargePaymentMode, Integer numberOfRepayments) { this.loan = loan; this.charge = chargeDefinition; this.penaltyCharge = chargeDefinition.isPenalty(); @@ -162,12 +176,11 @@ public class LoanCharge extends AbstractPersistable { if (chargePaymentMode != null) { this.chargePaymentMode = chargePaymentMode.getValue(); } - - populateDerivedFields(loanPrincipal,chargeAmount); + populateDerivedFields(loanPrincipal, chargeAmount,numberOfRepayments); this.paid = determineIfFullyPaid(); } - private void populateDerivedFields(final BigDecimal loanPrincipal, final BigDecimal chargeAmount) { + private void populateDerivedFields(final BigDecimal amountPercentageAppliedTo, final BigDecimal chargeAmount,Integer numberOfRepayments) { switch (ChargeCalculationType.fromInt(this.chargeCalculation)) { case INVALID: @@ -181,40 +194,31 @@ public class LoanCharge extends AbstractPersistable { break; case FLAT: this.percentage = null; - this.amount = chargeAmount; this.amountPercentageAppliedTo = null; this.amountPaid = null; - this.amountOutstanding = chargeAmount; + if(this.isInstalmentFee()){ + if(numberOfRepayments == null){ + numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments(); + } + this.amount = chargeAmount.multiply(BigDecimal.valueOf(numberOfRepayments)); + }else{ + this.amount = chargeAmount; + } + this.amountOutstanding = this.amount; this.amountWaived = null; this.amountWrittenOff = null; break; case PERCENT_OF_AMOUNT: + case PERCENT_OF_AMOUNT_AND_INTEREST: + case PERCENT_OF_INTEREST: this.percentage = chargeAmount; - this.amountPercentageAppliedTo = loanPrincipal; + this.amountPercentageAppliedTo = amountPercentageAppliedTo; this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); this.amountPaid = null; this.amountOutstanding = calculateOutstanding(); this.amountWaived = null; this.amountWrittenOff = null; break; - case PERCENT_OF_AMOUNT_AND_INTEREST: - this.percentage = null; - this.amount = null; - this.amountPercentageAppliedTo = null; - this.amountPaid = null; - this.amountOutstanding = BigDecimal.ZERO; - this.amountWaived = null; - this.amountWrittenOff = null; - break; - case PERCENT_OF_INTEREST: - this.percentage = null; - this.amount = null; - this.amountPercentageAppliedTo = null; - this.amountPaid = null; - this.amountOutstanding = BigDecimal.ZERO; - this.amountWaived = null; - this.amountWrittenOff = null; - break; } } @@ -255,7 +259,7 @@ public class LoanCharge extends AbstractPersistable { this.loan = loan; } - public void update(final BigDecimal amount, final LocalDate dueDate, final BigDecimal loanPrincipal) { + public void update(final BigDecimal amount, final LocalDate dueDate, final BigDecimal loanPrincipal, Integer numberOfRepayments) { if (dueDate != null) { this.dueDate = dueDate.toDate(); } @@ -265,31 +269,28 @@ public class LoanCharge extends AbstractPersistable { case INVALID: break; case FLAT: - this.amount = amount; + if(this.isInstalmentFee()){ + if(numberOfRepayments == null){ + numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments(); + } + this.amount = amount.multiply(BigDecimal.valueOf(numberOfRepayments)); + }else{ + this.amount = amount; + } break; case PERCENT_OF_AMOUNT: + case PERCENT_OF_AMOUNT_AND_INTEREST: + case PERCENT_OF_INTEREST: this.percentage = amount; this.amountPercentageAppliedTo = loanPrincipal; this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); this.amountOutstanding = calculateOutstanding(); - break; - case PERCENT_OF_AMOUNT_AND_INTEREST: - this.percentage = amount; - this.amount = null; - this.amountPercentageAppliedTo = null; - this.amountOutstanding = null; - break; - case PERCENT_OF_INTEREST: - this.percentage = amount; - this.amount = null; - this.amountPercentageAppliedTo = null; - this.amountOutstanding = null; - break; + break; } } } - public Map update(final JsonCommand command, final BigDecimal loanPrincipal) { + public Map update(final JsonCommand command, final BigDecimal amount) { final Map actualChanges = new LinkedHashMap(7); @@ -316,25 +317,19 @@ public class LoanCharge extends AbstractPersistable { case INVALID: break; case FLAT: - this.amount = newValue; + if(this.isInstalmentFee()){ + this.amount = newValue.multiply(BigDecimal.valueOf(this.loan.repaymentScheduleDetail().getNumberOfRepayments())); + }else{ + this.amount = newValue; + } break; case PERCENT_OF_AMOUNT: - this.percentage = newValue; - this.amountPercentageAppliedTo = loanPrincipal; - this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); - this.amountOutstanding = calculateOutstanding(); - break; case PERCENT_OF_AMOUNT_AND_INTEREST: - this.percentage = newValue; - this.amount = null; - this.amountPercentageAppliedTo = null; - this.amountOutstanding = null; - break; case PERCENT_OF_INTEREST: this.percentage = newValue; - this.amount = null; - this.amountPercentageAppliedTo = null; - this.amountOutstanding = null; + this.amountPercentageAppliedTo = amount; + this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage)); + this.amountOutstanding = calculateOutstanding(); break; } } @@ -349,6 +344,10 @@ public class LoanCharge extends AbstractPersistable { public boolean isSpecifiedDueDate() { return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE); } + + public boolean isInstalmentFee() { + return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.INSTALMENT_FEE); + } private boolean isGreaterThanZero(final BigDecimal value) { return value.compareTo(BigDecimal.ZERO) == 1; @@ -367,11 +366,16 @@ public class LoanCharge extends AbstractPersistable { } private boolean determineIfFullyPaid() { + if(this.amount == null){ + return true; + } return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0; } private BigDecimal calculateOutstanding() { - + if(this.amount == null){ + return null; + } BigDecimal amountPaidLocal = BigDecimal.ZERO; if (this.amountPaid != null) { amountPaidLocal = this.amountPaid; @@ -573,4 +577,20 @@ public class LoanCharge extends AbstractPersistable { public ChargePaymentMode getChargePaymentMode() { return ChargePaymentMode.fromInt(this.chargePaymentMode); } + + + public ChargeCalculationType getChargeCalculation() { + return ChargeCalculationType.fromInt(this.chargeCalculation); + } + + + public BigDecimal getPercentage() { + return this.percentage; + } + + + public void updateAmount(BigDecimal amount) { + this.amount = amount; + calculateOutstanding(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java index ff9856472..2ef229dd4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleProcessingWrapper.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.loanaccount.domain; +import java.math.BigDecimal; import java.util.List; import java.util.Set; @@ -25,14 +26,14 @@ public class LoanRepaymentScheduleProcessingWrapper { for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) { final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges, - currency); + currency,period,repaymentPeriods.size()); final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges, currency); final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(), loanCharges, currency); final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(), - loanCharges, currency); + loanCharges, currency,period,repaymentPeriods.size()); final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(), loanCharges, currency); final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate, @@ -47,13 +48,42 @@ public class LoanRepaymentScheduleProcessingWrapper { } private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges, - final MonetaryCurrency monetaryCurrency) { + final MonetaryCurrency monetaryCurrency,LoanRepaymentScheduleInstallment period,int numberOfRepayments) { Money cumulative = Money.zero(monetaryCurrency); for (final LoanCharge loanCharge : loanCharges) { - if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) { - cumulative = cumulative.plus(loanCharge.amount()); + if (loanCharge.isFeeCharge()) { + if (loanCharge.isInstalmentFee()) { + if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(period.getPrincipal(monetaryCurrency).getAmount()).add( + period.getInterestCharged(monetaryCurrency).getAmount()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(period.getInterestCharged(monetaryCurrency).getAmount()); + } else { + amount = amount.add(period.getPrincipal(monetaryCurrency).getAmount()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else { + cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments))); + } + } else if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(period.getLoan().getSummary().getTotalInterestCharged()); + } else { + amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) { + cumulative = cumulative.plus(loanCharge.amount()); + } } } @@ -89,13 +119,42 @@ public class LoanRepaymentScheduleProcessingWrapper { } private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, - final Set loanCharges, final MonetaryCurrency currency) { + final Set loanCharges, final MonetaryCurrency currency,LoanRepaymentScheduleInstallment period,int numberOfRepayments) { Money cumulative = Money.zero(currency); for (final LoanCharge loanCharge : loanCharges) { - if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { - cumulative = cumulative.plus(loanCharge.amount()); + if (loanCharge.isPenaltyCharge()) { + if (loanCharge.isInstalmentFee()) { + if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(period.getPrincipal(currency).getAmount()) + .add(period.getInterestCharged(currency).getAmount()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(period.getInterestCharged(currency).getAmount()); + } else { + amount = amount.add(period.getPrincipal(currency).getAmount()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else { + cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments))); + } + } else if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(period.getLoan().getSummary().getTotalInterestCharged()); + } else { + amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) { + cumulative = cumulative.plus(loanCharge.amount()); + } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java index 8fe9f03dd..81e7af512 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java @@ -274,4 +274,9 @@ public final class LoanSummary { .plus(this.totalFeeChargesOutstanding).plus(this.totalPenaltyChargesOutstanding); this.totalOutstanding = totalOutstanding.getAmount(); } + + + public BigDecimal getTotalPrincipalDisbursed() { + return this.totalPrincipalDisbursed; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 8baf86e32..788a66b88 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -257,14 +257,21 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(final Set charges) { LoanCharge earliestUnpaidCharge = null; - + LoanCharge installemntCharge = null; for (final LoanCharge loanCharge : charges) { if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) { - if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) { + if(loanCharge.isInstalmentFee()){ + if(installemntCharge == null){ + installemntCharge = loanCharge; + } + }else if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) { earliestUnpaidCharge = loanCharge; } } } + if(earliestUnpaidCharge == null || earliestUnpaidCharge.getDueLocalDate().isAfter(new LocalDate())){ + earliestUnpaidCharge = installemntCharge; + } return earliestUnpaidCharge; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index 6db6f9ef7..d582d2d81 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -108,9 +108,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener outstandingBalance = outstandingBalance.minus(principalInterestForThisPeriod.principal()); // 7. determine fees and penalties - final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency); + final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency, + principalInterestForThisPeriod, principalDisbursed, totalInterestChargedForFullLoanTerm, numberOfRepayments); final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, - currency); + currency, principalInterestForThisPeriod, principalDisbursed, totalInterestChargedForFullLoanTerm, numberOfRepayments); // 8. sum up real totalInstallmentDue from components final Money totalInstallmentDue = principalInterestForThisPeriod.principal() // @@ -174,13 +175,43 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener } private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set loanCharges, - final MonetaryCurrency monetaryCurrency) { + final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod, + final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments) { Money cumulative = Money.zero(monetaryCurrency); for (final LoanCharge loanCharge : loanCharges) { - if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) { - cumulative = cumulative.plus(loanCharge.amount()); + if (loanCharge.isFeeCharge()) { + if (loanCharge.isInstalmentFee()) { + if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(principalInterestForThisPeriod.principal().getAmount()).add( + principalInterestForThisPeriod.interest().getAmount()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(principalInterestForThisPeriod.interest().getAmount()); + } else { + amount = amount.add(principalInterestForThisPeriod.principal().getAmount()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else { + cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments))); + } + } else if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(totalInterestChargedForFullLoanTerm.getAmount()); + } else { + amount = amount.add(principalDisbursed.getAmount()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) { + cumulative = cumulative.plus(loanCharge.amount()); + } } } @@ -188,13 +219,44 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener } private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, - final Set loanCharges, final MonetaryCurrency monetaryCurrency) { + final Set loanCharges, final MonetaryCurrency monetaryCurrency, + final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed, + final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments) { Money cumulative = Money.zero(monetaryCurrency); for (final LoanCharge loanCharge : loanCharges) { - if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) { - cumulative = cumulative.plus(loanCharge.amount()); + if (loanCharge.isPenaltyCharge()) { + if (loanCharge.isInstalmentFee()) { + if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(principalInterestForThisPeriod.principal().getAmount()).add( + principalInterestForThisPeriod.interest().getAmount()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(principalInterestForThisPeriod.interest().getAmount()); + } else { + amount = amount.add(principalInterestForThisPeriod.principal().getAmount()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else { + cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments))); + } + } else if (loanCharge.getChargeCalculation().isPercentageBased()) { + BigDecimal amount = BigDecimal.ZERO; + if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) { + amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount()); + } else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) { + amount = amount.add(totalInterestChargedForFullLoanTerm.getAmount()); + } else { + amount = amount.add(principalDisbursed.getAmount()); + } + BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100)); + cumulative = cumulative.plus(loanChargeAmt); + } else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) { + cumulative = cumulative.plus(loanCharge.amount()); + } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 572277ce7..51f64bcec 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -325,12 +325,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa existingLoanApplication.updateTransactionProcessingStrategy(strategy); } - final String chargesParamName = "charges"; - if (changes.containsKey(chargesParamName)) { - final Set loanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson()); - existingLoanApplication.updateLoanCharges(loanCharges); - } - final String collateralParamName = "collateral"; if (changes.containsKey(collateralParamName)) { final Set loanCollateral = this.loanCollateralAssembler.fromParsedJson(command.parsedJson()); @@ -346,6 +340,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query); existingLoanApplication.updateLoanSchedule(loanSchedule); } + + final String chargesParamName = "charges"; + if (changes.containsKey(chargesParamName)) { + final Set loanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson()); + existingLoanApplication.updateLoanCharges(loanCharges); + } this.loanRepository.saveAndFlush(existingLoanApplication); 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 97d68767f..e598ac57b 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 @@ -47,6 +47,7 @@ public class LoanChargeAssembler { final Set loanCharges = new HashSet(); final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element); + final Integer numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element); if (element.isJsonObject()) { final JsonObject topLevelJsonElement = element.getAsJsonObject(); @@ -83,14 +84,14 @@ public class LoanChargeAssembler { chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode); } final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime, - chargeCalculation, dueDate, chargePaymentModeEnum); + chargeCalculation, dueDate, chargePaymentModeEnum,numberOfRepayments); loanCharges.add(loanCharge); } else { final Long loanChargeId = id; final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId); if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); } - loanCharge.update(amount, dueDate, principal); + loanCharge.update(amount, dueDate, principal, numberOfRepayments); loanCharges.add(loanCharge); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 2b3339408..ebe68b3e5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -925,7 +925,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loanCharge.isWaived()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_WAIVED, loanCharge.getId()); } else if (loanCharge.isPaid()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_PAID, - loanCharge.getId()); } + loanCharge.getId()); + } else if (loanCharge.isInstalmentFee()){ + throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.WAIVE_NOT_ALLOWED_FOR_CHARGE, + loanCharge.getId()); + } final Map changes = new LinkedHashMap(3); @@ -1001,7 +1005,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (loanCharge.isWaived()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId()); } else if (loanCharge.isPaid()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID, - loanCharge.getId()); } + loanCharge.getId()); + }else if (loanCharge.isInstalmentFee()){ + throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_PAYABLE, + loanCharge.getId()); + } if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException( LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); }