From 72bd70243ada218326e5195047094c3a6625fcd7 Mon Sep 17 00:00:00 2001 From: sachinkulkarni12 Date: Thu, 14 Jan 2016 14:10:55 +0530 Subject: [PATCH] Loan Repayment Rescheduling while disbursement --- ...RepaymentRescheduleAtDisbursementTest.java | 228 ++++++++++++++++++ .../common/loans/LoanTransactionHelper.java | 19 ++ .../loanaccount/data/LoanTransactionData.java | 67 ++++- .../portfolio/loanaccount/domain/Loan.java | 38 ++- .../domain/LoanTermVariations.java | 11 + .../LoanEventApiJsonValidator.java | 2 +- .../service/LoanReadPlatformServiceImpl.java | 7 +- ...WritePlatformServiceJpaRepositoryImpl.java | 16 +- 8 files changed, 372 insertions(+), 16 deletions(-) create mode 100644 mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java new file mode 100644 index 000000000..a7057f8e4 --- /dev/null +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/LoanRepaymentRescheduleAtDisbursementTest.java @@ -0,0 +1,228 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.integrationtests; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.mifosplatform.integrationtests.common.ClientHelper; +import org.mifosplatform.integrationtests.common.Utils; +import org.mifosplatform.integrationtests.common.accounting.Account; +import org.mifosplatform.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanProductTestBuilder; +import org.mifosplatform.integrationtests.common.loans.LoanStatusChecker; +import org.mifosplatform.integrationtests.common.loans.LoanTransactionHelper; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +@SuppressWarnings("rawtypes") +public class LoanRepaymentRescheduleAtDisbursementTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private LoanTransactionHelper loanTransactionHelper; + private LoanApplicationApprovalTest loanApplicationApprovalTest; + private ResponseSpecification generalResponseSpec; + + @Before + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + this.loanApplicationApprovalTest = new LoanApplicationApprovalTest(); + this.generalResponseSpec = new ResponseSpecBuilder().build(); + } + + @SuppressWarnings("unchecked") + @Test + public void testLoanRepaymentRescheduleAtDisbursement(){ + + final String approvalAmount = "10000"; + final String approveDate = "01 March 2015"; + final String expectedDisbursementDate = "01 March 2015"; + final String disbursementDate = "01 March 2015"; + final String adjustRepaymentDate = "16 March 2015"; + final String recalculationRestFrequencyDate = "01 January 2012"; + + // CREATE CLIENT + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2014"); + System.out.println("---------------------------------CLIENT CREATED WITH ID---------------------------------------------------" + + clientID); + + // CREATE LOAN MULTIDISBURSAL PRODUCT WITH INTEREST RECALCULATION + final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY, + LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE, + LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS, + LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "0", recalculationRestFrequencyDate, + LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null); + + // CREATE TRANCHES + List createTranches = new ArrayList<>(); + createTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 March 2015", "5000")); + createTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 May 2015", "5000")); + + // APPROVE TRANCHES + List approveTranches = new ArrayList<>(); + approveTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 March 2015", "5000")); + approveTranches.add(this.loanApplicationApprovalTest.createTrancheDetail("01 May 2015", "5000")); + + // APPLY FOR TRANCHE LOAN WITH INTEREST RECALCULATION + final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, disbursementDate, + recalculationRestFrequencyDate, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList(0), createTranches); + + HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + + // VALIDATE THE LOAN STATUS + LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); + + System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------------------------"); + loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate, expectedDisbursementDate, approvalAmount, + loanID, approveTranches); + + // VALIDATE THE LOAN IS APPROVED + LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap); + LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); + + // DISBURSE A FIRST TRANCHE + this.loanTransactionHelper.disburseLoanWithRepaymentReschedule(disbursementDate, loanID, adjustRepaymentDate); + loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + + ArrayList loanRepaymnetSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, generalResponseSpec, loanID); + HashMap firstInstallement = loanRepaymnetSchedule.get(1); + Map expectedvalues = new HashMap<>(3); + Calendar date = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + date.set(2015, Calendar.MARCH, 16); + expectedvalues.put("dueDate", getDateAsArray(date, 0)); + expectedvalues.put("principalDue", "834.71"); + expectedvalues.put("interestDue", "49.32"); + expectedvalues.put("feeChargesDue", "0"); + expectedvalues.put("penaltyChargesDue", "0"); + expectedvalues.put("totalDueForPeriod", "884.03"); + + // VALIDATE REPAYMENT SCHEDULE + verifyLoanRepaymentSchedule(firstInstallement, expectedvalues); + + } + + private void verifyLoanRepaymentSchedule(final HashMap firstInstallement, final Map expectedvalues) { + + assertEquals(expectedvalues.get("dueDate"), firstInstallement.get("dueDate")); + assertEquals(String.valueOf(expectedvalues.get("principalDue")), String.valueOf(firstInstallement.get("principalDue"))); + assertEquals(String.valueOf(expectedvalues.get("interestDue")), String.valueOf(firstInstallement.get("interestDue"))); + assertEquals(String.valueOf(expectedvalues.get("feeChargesDue")), String.valueOf(firstInstallement.get("feeChargesDue"))); + assertEquals(String.valueOf(expectedvalues.get("penaltyChargesDue")), String.valueOf(firstInstallement.get("penaltyChargesDue"))); + assertEquals(String.valueOf(expectedvalues.get("totalDueForPeriod")), String.valueOf(firstInstallement.get("totalDueForPeriod"))); + + } + + private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy, + final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod, + final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval, + final String recalculationRestFrequencyDate, final String preCloseInterestCalculationStrategy, final Account[] accounts) { + final String recalculationCompoundingFrequencyType = null; + final String recalculationCompoundingFrequencyInterval = null; + final String recalculationCompoundingFrequencyDate = null; + return createLoanProductWithInterestRecalculation(repaymentStrategy, interestRecalculationCompoundingMethod, + rescheduleStrategyMethod, recalculationRestFrequencyType, recalculationRestFrequencyInterval, + recalculationRestFrequencyDate, recalculationCompoundingFrequencyType, recalculationCompoundingFrequencyInterval, + recalculationCompoundingFrequencyDate, preCloseInterestCalculationStrategy, accounts, null, false); + } + + private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy, + final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod, + final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval, + final String recalculationRestFrequencyDate, final String recalculationCompoundingFrequencyType, + final String recalculationCompoundingFrequencyInterval, final String recalculationCompoundingFrequencyDate, + final String preCloseInterestCalculationStrategy, final Account[] accounts, final String chargeId, + boolean isArrearsBasedOnOriginalSchedule) { + System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); + LoanProductTestBuilder builder = new LoanProductTestBuilder() + .withPrincipal("10000.00") + .withNumberOfRepayments("12") + .withRepaymentAfterEvery("2") + .withRepaymentTypeAsWeek() + .withinterestRatePerPeriod("2") + .withInterestRateFrequencyTypeAsMonths() + .withTranches(true) + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true) + .withRepaymentStrategy(repaymentStrategy) + .withInterestTypeAsDecliningBalance() + .withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod, + preCloseInterestCalculationStrategy) + .withInterestRecalculationRestFrequencyDetails(recalculationRestFrequencyType, recalculationRestFrequencyInterval, + recalculationRestFrequencyDate) + .withInterestRecalculationCompoundingFrequencyDetails(recalculationCompoundingFrequencyType, + recalculationCompoundingFrequencyInterval, recalculationCompoundingFrequencyDate); + if (accounts != null) { + builder = builder.withAccountingRulePeriodicAccrual(accounts); + } + + if (isArrearsBasedOnOriginalSchedule) builder = builder.withArrearsConfiguration(); + + final String loanProductJSON = builder.build(chargeId); + return this.loanTransactionHelper.getLoanProductId(loanProductJSON); + } + + private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, final Integer loanProductID, + final String disbursementDate, final String restStartDate, final String repaymentStrategy, final List charges, List tranches) { + final String graceOnInterestPayment = null; + final String compoundingStartDate = null; + final String graceOnPrincipalPayment = null; + return applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, disbursementDate, restStartDate, + compoundingStartDate, repaymentStrategy, charges, graceOnInterestPayment, graceOnPrincipalPayment,tranches); + } + + private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, final Integer loanProductID, + final String disbursementDate, final String restStartDate, final String compoundingStartDate, final String repaymentStrategy, + final List charges, final String graceOnInterestPayment, final String graceOnPrincipalPayment, List tranches) { + System.out.println("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); + final String loanApplicationJSON = new LoanApplicationTestBuilder() // + .withPrincipal("10000.00") // + .withLoanTermFrequency("24") // + .withLoanTermFrequencyAsWeeks() // + .withNumberOfRepayments("12") // + .withRepaymentEveryAfter("2") // + .withRepaymentFrequencyTypeAsWeeks() // + .withInterestRatePerPeriod("2") // + .withAmortizationTypeAsEqualInstallments() // + .withTranches(tranches) + .withFixedEmiAmount("") // + .withInterestTypeAsDecliningBalance() // + .withInterestCalculationPeriodTypeAsDays() // + .withInterestCalculationPeriodTypeAsDays() // + .withExpectedDisbursementDate(disbursementDate) // + .withSubmittedOnDate(disbursementDate) // + .withRestFrequencyDate(restStartDate)// + .withwithRepaymentStrategy(repaymentStrategy) // + .withCharges(charges)// + .build(clientID.toString(), loanProductID.toString(), null); + return this.loanTransactionHelper.getLoanId(loanApplicationJSON); + } + + private List getDateAsArray(Calendar date, int addPeriod) { + return getDateAsArray(date, addPeriod, Calendar.DAY_OF_MONTH); + } + + private List getDateAsArray(Calendar date, int addvalue, int type) { + date.add(type, addvalue); + return new ArrayList<>(Arrays.asList(date.get(Calendar.YEAR), date.get(Calendar.MONTH) + 1, + date.get(Calendar.DAY_OF_MONTH))); + } +} diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java index 7fb9c9952..9440b5b1a 100755 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/loans/LoanTransactionHelper.java @@ -152,6 +152,11 @@ public class LoanTransactionHelper { public HashMap disburseLoan(final String date, final Integer loanID) { return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date, null)); } + + public HashMap disburseLoanWithRepaymentReschedule(final String date, final Integer loanID, String adjustRepaymentDate) { + return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanWithRepaymentRescheduleAsJSON(date, + null, adjustRepaymentDate)); + } public HashMap disburseLoan(final String date, final Integer loanID, final String disburseAmt) { return performLoanTransaction(createLoanOperationURL(DISBURSE_LOAN_COMMAND, loanID), getDisburseLoanAsJSON(date, disburseAmt)); @@ -260,6 +265,20 @@ public class LoanTransactionHelper { System.out.println("Loan Application disburse request : " + map); return new Gson().toJson(map); } + + private String getDisburseLoanWithRepaymentRescheduleAsJSON(final String actualDisbursementDate, final String transactionAmount, final String adjustRepaymentDate) { + final HashMap map = new HashMap<>(); + map.put("locale", "en"); + map.put("dateFormat", "dd MMMM yyyy"); + map.put("actualDisbursementDate", actualDisbursementDate); + map.put("adjustRepaymentDate", adjustRepaymentDate); + map.put("note", "DISBURSE NOTE"); + if (transactionAmount != null) { + map.put("transactionAmount", transactionAmount); + } + System.out.println("Loan Application disburse request : " + map); + return new Gson().toJson(map); + } private String getApproveLoanAsJSON(final String approvalDate) { return getApproveLoanAsJSON(approvalDate, null, null, null); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java index 3503865d1..48686bee0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java @@ -43,8 +43,9 @@ public class LoanTransactionData { private final BigDecimal outstandingLoanBalance; @SuppressWarnings("unused") private final LocalDate submittedOnDate; - private final boolean manuallyReversed; + @SuppressWarnings("unused") + private final LocalDate possibleNextRepaymentDate; // templates final Collection paymentTypeOptions; @@ -71,7 +72,7 @@ public class LoanTransactionData { feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, null, externalId, transfer, fixedEmiAmount, outstandingLoanBalance,manuallyReversed); } - + public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, @@ -121,16 +122,74 @@ public class LoanTransactionData { this.outstandingLoanBalance = outstandingLoanBalance; this.submittedOnDate = submittedOnDate; this.manuallyReversed = manuallyReversed; + this.possibleNextRepaymentDate = null; } public LoanTransactionData(Long id, LoanTransactionEnumData transactionType, LocalDate date, BigDecimal totalAmount, BigDecimal principalPortion, BigDecimal interestPortion, BigDecimal feeChargesPortion, BigDecimal penaltyChargesPortion, BigDecimal overPaymentPortion, BigDecimal unrecognizedIncomePortion, BigDecimal outstandingLoanBalance,final boolean manuallyReversed) { this(id, null, null, transactionType, null, null, date, totalAmount, principalPortion, interestPortion, feeChargesPortion, - penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null,manuallyReversed); + penaltyChargesPortion, overPaymentPortion, unrecognizedIncomePortion, null, null, null, null, outstandingLoanBalance, null, + manuallyReversed); } + + public static LoanTransactionData LoanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData transactionType, final LocalDate expectedDisbursedOnLocalDateForTemplate, + final BigDecimal disburseAmountForTemplate, final Collection paymentOptions, + final BigDecimal retriveLastEmiAmount, final LocalDate possibleNextRepaymentDate) { + final Long id = null; + final Long officeId = null; + final String officeName = null; + final PaymentDetailData paymentDetailData = null; + final CurrencyData currency = null; + final BigDecimal unrecognizedIncomePortion = null; + final BigDecimal principalPortion = null;; + final BigDecimal interestPortion = null; + final BigDecimal feeChargesPortion = null; + final BigDecimal penaltyChargesPortion = null; + final BigDecimal overpaymentPortion = null; + final String externalId = null; + final BigDecimal outstandingLoanBalance = null; + final AccountTransferData transfer = null; + final LocalDate submittedOnDate = null; + final boolean manuallyReversed = false; + return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currency, expectedDisbursedOnLocalDateForTemplate, + disburseAmountForTemplate, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overpaymentPortion, unrecognizedIncomePortion, + paymentOptions, transfer, externalId, retriveLastEmiAmount, outstandingLoanBalance, submittedOnDate, manuallyReversed, possibleNextRepaymentDate); + + } - public LocalDate dateOf() { + private LoanTransactionData(Long id , final Long officeId, final String officeName, LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, + final CurrencyData currency, final LocalDate date, BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, + final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, BigDecimal unrecognizedIncomePortion, Collection paymentOptions, + final AccountTransferData transfer, final String externalId, final BigDecimal fixedEmiAmount, BigDecimal outstandingLoanBalance, + final LocalDate submittedOnDate, final boolean manuallyReversed, final LocalDate possibleNextRepaymentDate) { + this.id = id; + this.officeId = officeId; + this.officeName = officeName; + this.type = transactionType; + this.paymentDetailData = paymentDetailData; + this.currency = currency; + this.date = date; + this.amount = amount; + this.principalPortion = principalPortion; + this.interestPortion = interestPortion; + this.feeChargesPortion = feeChargesPortion; + this.penaltyChargesPortion = penaltyChargesPortion; + this.unrecognizedIncomePortion = unrecognizedIncomePortion; + this.paymentTypeOptions = paymentOptions; + this.externalId = externalId; + this.transfer = transfer; + this.overpaymentPortion = overpaymentPortion; + this.fixedEmiAmount = fixedEmiAmount; + this.outstandingLoanBalance = outstandingLoanBalance; + this.submittedOnDate = submittedOnDate; + this.manuallyReversed = manuallyReversed; + this.possibleNextRepaymentDate = possibleNextRepaymentDate; + } + + + + public LocalDate dateOf() { return this.date; } 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 aa96d46b4..906c854ec 100755 --- 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 @@ -2324,7 +2324,7 @@ public class Loan extends AbstractPersistable { } public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule, - final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser) { + final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser, LocalDate nextPossibleRepaymentDate, Date rescheduledRepaymentDate) { boolean isEmiAmountChanged = false; if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstallmentAmount()) && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) { @@ -2332,13 +2332,19 @@ public class Loan extends AbstractPersistable { final Date dateValue = null; final boolean isSpecificToInstallment = false; LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(), - actualDisbursementDate.toDate(), emiAmount, dateValue, isSpecificToInstallment, this); + actualDisbursementDate.toDate(), emiAmount, dateValue, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue()); this.loanTermVariations.add(loanVariationTerms); } else { this.fixedEmiAmount = emiAmount; } isEmiAmountChanged = true; } + if(rescheduledRepaymentDate != null && this.loanProduct.isMultiDisburseLoan()){ + final boolean isSpecificToInstallment = false; + LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.DUE_DATE.getValue(), + nextPossibleRepaymentDate.toDate(), emiAmount, rescheduledRepaymentDate, isSpecificToInstallment, this, LoanStatus.ACTIVE.getValue()); + this.loanTermVariations.add(loanVariationTerms); + } if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) { regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser); @@ -5533,6 +5539,12 @@ public class Loan extends AbstractPersistable { } actualDisbursementDate = lastTransactionDate; updateLoanToLastDisbursalState(actualDisbursementDate); + for (Iterator iterator = this.loanTermVariations.iterator(); iterator.hasNext();) { + LoanTermVariations loanTermVariations = iterator.next(); + if (loanTermVariations.fetchDateValue().isAfter(actualDisbursementDate)) { + iterator.remove(); + } + } reverseExistingTransactionsTillLastDisbursal(actualDisbursementDate); loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds, currentUser); @@ -5621,5 +5633,27 @@ public class Loan extends AbstractPersistable { public void setExpectedFirstRepaymentOnDate(Date expectedFirstRepaymentOnDate) { this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate; } + + /* + * get the next repayment date for rescheduling at the time of disbursement + */ + public LocalDate getNextPossibleRepaymentDateForRescheduling(){ + Set loanDisbursementDetails = this.disbursementDetails; + LocalDate nextRepaymentDate = new LocalDate(); + for(LoanDisbursementDetails loanDisbursementDetail : loanDisbursementDetails){ + if(loanDisbursementDetail.actualDisbursementDate() == null){ + for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { + if (installment.getDueDate().isEqual(loanDisbursementDetail.expectedDisbursementDateAsLocalDate()) || + installment.getDueDate().isAfter(loanDisbursementDetail.expectedDisbursementDateAsLocalDate()) + && installment.isNotFullyPaidOff()) { + nextRepaymentDate = installment.getDueDate(); + break; + } + } + break; + } + } + return nextRepaymentDate; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTermVariations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTermVariations.java index 75eaed745..73aaaf042 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTermVariations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTermVariations.java @@ -60,6 +60,17 @@ public class LoanTermVariations extends AbstractPersistable { this.isSpecificToInstallment = isSpecificToInstallment; this.onLoanStatus = loan.status().getValue(); } + + public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue, + final boolean isSpecificToInstallment, final Loan loan, final Integer loanStatus) { + this.loan = loan; + this.termApplicableFrom = termApplicableFrom; + this.termType = termType; + this.decimalValue = decimalValue; + this.dateValue = dateValue; + this.isSpecificToInstallment = isSpecificToInstallment; + this.onLoanStatus = loanStatus; + } protected LoanTermVariations() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index a06c5870e..2fbda5987 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -64,7 +64,7 @@ public final class LoanEventApiJsonValidator { "dateFormat", LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName)); } else { disbursementParameters = new HashSet<>(Arrays.asList("actualDisbursementDate", "externalId", "note", "locale", - "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", + "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber", "adjustRepaymentDate", LoanApiConstants.principalDisbursedParameterName, LoanApiConstants.emiAmountParameterName)); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index d6a720f76..1a9c6c37d 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -480,11 +480,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { if (paymentDetailsRequired) { paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); } - final BigDecimal outstandingLoanBalance = null; - final BigDecimal unrecognizedIncomePortion = null; - return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDateForTemplate(), - loan.getDisburseAmountForTemplate(), null, null, null, null, null, unrecognizedIncomePortion, paymentOptions, null, null, - loan.retriveLastEmiAmount(), outstandingLoanBalance, false); + return LoanTransactionData.LoanTransactionDataForDisbursalTemplate(transactionType, loan.getExpectedDisbursedOnLocalDateForTemplate(), loan.getDisburseAmountForTemplate(), + paymentOptions, loan.retriveLastEmiAmount(), loan.getNextPossibleRepaymentDateForRescheduling()); } 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 da44b4cd7..9a9e62ddf 100755 --- 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 @@ -9,6 +9,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -283,6 +284,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); + + final LocalDate nextPossibleRepaymentDate = loan.getNextPossibleRepaymentDateForRescheduling(); + final Date rescheduledRepaymentDate = command.DateValueOfParameterNamed("adjustRepaymentDate"); // check for product mix validations checkForProductMixRestrictions(loan); @@ -333,7 +337,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); - regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO); + regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(), loan, null); @@ -518,6 +522,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions(); final Map changes = new LinkedHashMap<>(); if (disbursalCommand == null) { return changes; } + + final LocalDate nextPossibleRepaymentDate = null; + final Date rescheduledRepaymentDate = null; for (int i = 0; i < disbursalCommand.length; i++) { final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i]; @@ -563,7 +570,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } LocalDate recalculateFrom = null; final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); - regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO); + regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive( loan.fetchRepaymentScheduleInstallments(), loan, null); @@ -2669,11 +2676,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final boolean recalculateSchedule, - final ScheduleGeneratorDTO scheduleGeneratorDTO) { + final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate nextPossibleRepaymentDate, final Date rescheduledRepaymentDate) { AppUser currentUser = getAppUserIfPresent(); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName); - loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser); + loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser, + nextPossibleRepaymentDate, rescheduledRepaymentDate); } private List retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {