Fix loan repayment schedule with multidisburse

This commit is contained in:
Jose Alberto Hernandez
2022-09-22 10:58:23 +02:00
committed by Arnold Gálovics
parent c8a084cb97
commit 1521b9dcf6
17 changed files with 237 additions and 290 deletions
@@ -373,7 +373,6 @@ public final class CalendarUtils {
}
public static boolean isValidRedurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate date) {
final Recur recur = CalendarUtils.getICalRecur(recurringRule);
if (recur == null) {
return false;
@@ -59,6 +59,7 @@ import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -142,6 +143,7 @@ import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDat
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.stereotype.Component;
@Slf4j
@Entity
@Component
@Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"),
@@ -5112,15 +5114,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName,
command.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementPrincipalParameterName, locale));
Collection<LoanDisbursementDetails> loanDisburseDetails = this.getDisbursementDetails();
BigDecimal setPrincipalAmount = BigDecimal.ZERO;
for (LoanDisbursementDetails details : loanDisburseDetails) {
if (details.actualDisbursementDate() != null) {
setPrincipalAmount = setPrincipalAmount.add(details.principal());
}
}
this.loanRepaymentScheduleDetail.setPrincipal(getPrincipalAmountForRepaymentSchedule());
this.loanRepaymentScheduleDetail.setPrincipal(setPrincipalAmount);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
} else {
@@ -5141,6 +5136,25 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return changedTransactionDetail;
}
public BigDecimal getPrincipalAmountForRepaymentSchedule() {
BigDecimal principalAmount = BigDecimal.ZERO;
if (isMultiDisburmentLoan() && isDisbursed()) {
Collection<LoanDisbursementDetails> loanDisburseDetails = this.getDisbursementDetails();
for (LoanDisbursementDetails details : loanDisburseDetails) {
if (details.actualDisbursementDate() != null) {
principalAmount = principalAmount.add(details.principal());
}
}
} else if (isApproved()) {
principalAmount = getApprovedPrincipal();
} else {
principalAmount = getPrincipal().getAmount();
}
return principalAmount;
}
public BigDecimal retriveLastEmiAmount() {
BigDecimal emiAmount = this.fixedEmiAmount;
LocalDate startDate = this.getDisbursementDate();
@@ -1259,8 +1259,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
*/
private Money getPrincipalToBeScheduled(final LoanApplicationTerms loanApplicationTerms) {
Money principalToBeScheduled;
if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) {
principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal();
if (loanApplicationTerms.isMultiDisburseLoan()) {
if (loanApplicationTerms.getTotalDisbursedAmount().isGreaterThanZero()) {
principalToBeScheduled = loanApplicationTerms.getTotalMultiDisbursedAmount();
} else if (loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) {
principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal();
} else {
principalToBeScheduled = loanApplicationTerms.getPrincipal();
}
} else {
principalToBeScheduled = loanApplicationTerms.getPrincipal();
}
@@ -20,9 +20,11 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import net.fortuna.ical4j.model.Recur;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
@@ -131,10 +133,14 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
private AdjustedDateDetailsDTO recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(
final AdjustedDateDetailsDTO adjustedDateDetailsDTO, final LoanApplicationTerms loanApplicationTerms,
final HolidayDetailDTO holidayDetailDTO, final boolean isFirstRepayment) {
checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
isFirstRepayment);
final Recur recur = CalendarUtils.getICalRecur(holidayDetailDTO.getWorkingDays().getRecurrence());
final boolean isSevenDaysWeek = (recur.getDayList().size() == 7); // 7 Seven days in the week
// If Workings days are not seven day week
if (!isSevenDaysWeek) {
checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
isFirstRepayment);
}
// Check Holidays If applied
checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
isFirstRepayment);
@@ -148,6 +154,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(adjustedDateDetailsDTO,
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
}
return adjustedDateDetailsDTO;
}
@@ -192,10 +199,12 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
*/
private void checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
final HolidayDetailDTO holidayDetailDTO, final LoanApplicationTerms loanApplicationTerms, final boolean isFirstRepayment) {
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())) {
if (WorkingDaysUtil
.getRepaymentRescheduleType(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())
.isMoveToNextRepaymentDay()) {
final RepaymentRescheduleType repaymentRescheduleType = WorkingDaysUtil
.getRepaymentRescheduleType(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate());
if (repaymentRescheduleType.isMoveToNextRepaymentDay()) {
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(),
adjustedDateDetailsDTO.getNextRepaymentPeriodDueDate())
|| adjustedDateDetailsDTO.getChangedScheduleDate()
@@ -43,7 +43,6 @@ import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
@@ -252,32 +251,6 @@ public final class LoanApplicationTerms {
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, DayOfWeekType dayOfWeek,
final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance,
final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount,
final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance, final LocalDate interestChargedFromDate,
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
final RecalculationFrequencyType compoundingFrequencyType,
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate,
List<LoanTermVariationsData> loanTermVariations, final Integer numberOfDays, final boolean isSkipRepaymentOnFirstDayOfMonth,
final Calendar loanCalendar, final HolidayDetailDTO holidayDetailDTO, final boolean allowCompoundingOnEod) {
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
return assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDay, dayOfWeek,
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsTolerance,
loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, interestChargedFromDate,
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType, restCalendarInstance,
compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy,
rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations,
calendarHistoryDataWrapper, numberOfDays, isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, false,
false, null, false);
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, DayOfWeekType dayOfWeek,
final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
@@ -337,93 +310,6 @@ public final class LoanApplicationTerms {
isPrincipalCompoundingDisabledForOverdueLoans);
}
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, final LocalDate expectedDisbursementDate,
final LocalDate repaymentsStartingFromDate, final LocalDate calculatedRepaymentsStartingFromDate,
final Money inArrearsTolerance, final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan,
final BigDecimal emiAmount, final List<DisbursementData> disbursementDatas, final BigDecimal maxOutstandingBalance,
final LocalDate interestChargedFromDate, final LoanInterestRecalculationDetails interestRecalculationDetails,
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, final Calendar loanCalendar,
BigDecimal approvedAmount, final BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations,
Integer numberOfDays, boolean isSkipRepaymentOnFirstDayOfMonth, final HolidayDetailDTO holidayDetailDTO,
final boolean allowCompoundingOnEod) {
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
final PeriodFrequencyType repaymentPeriodFrequencyType = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
final AmortizationMethod amortizationMethod = loanProductRelatedDetail.getAmortizationMethod();
final InterestMethod interestMethod = loanProductRelatedDetail.getInterestMethod();
final BigDecimal interestRatePerPeriod = loanProductRelatedDetail.getNominalInterestRatePerPeriod();
final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail.getInterestPeriodFrequencyType();
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
.getInterestCalculationPeriodMethod();
final boolean allowPartialPeriodInterestCalcualtion = loanProductRelatedDetail.isAllowPartialPeriodInterestCalcualtion();
final Money principalMoney = loanProductRelatedDetail.getPrincipal();
//
final Integer graceOnPrincipalPayment = loanProductRelatedDetail.graceOnPrincipalPayment();
final Integer recurringMoratoriumOnPrincipalPeriods = loanProductRelatedDetail.recurringMoratoriumOnPrincipalPeriods();
final Integer graceOnInterestPayment = loanProductRelatedDetail.graceOnInterestPayment();
final Integer graceOnInterestCharged = loanProductRelatedDetail.graceOnInterestCharged();
// Interest recalculation settings
final DaysInMonthType daysInMonthType = loanProductRelatedDetail.fetchDaysInMonthType();
final DaysInYearType daysInYearType = loanProductRelatedDetail.fetchDaysInYearType();
final boolean isInterestRecalculationEnabled = loanProductRelatedDetail.isInterestRecalculationEnabled();
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
if (isInterestRecalculationEnabled) {
rescheduleStrategyMethod = interestRecalculationDetails.getRescheduleStrategyMethod();
interestRecalculationCompoundingMethod = interestRecalculationDetails.getInterestRecalculationCompoundingMethod();
}
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
final boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = false;
final boolean isEqualAmortization = loanProductRelatedDetail.isEqualAmortization();
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
repaymentEvery, repaymentPeriodFrequencyType, null, null, amortizationMethod, interestMethod, interestRatePerPeriod,
interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
allowPartialPeriodInterestCalcualtion, principalMoney, expectedDisbursementDate, repaymentsStartingFromDate,
calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, recurringMoratoriumOnPrincipalPeriods,
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
disbursementDatas, maxOutstandingBalance, loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations,
calendarHistoryDataWrapper, isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfDays,
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false, false, null, false);
}
public static LoanApplicationTerms assembleFrom(final LoanApplicationTerms applicationTerms,
final List<LoanTermVariationsData> loanTermVariations) {
return new LoanApplicationTerms(applicationTerms.currency, applicationTerms.loanTermFrequency,
applicationTerms.loanTermPeriodFrequencyType, applicationTerms.numberOfRepayments, applicationTerms.repaymentEvery,
applicationTerms.repaymentPeriodFrequencyType, applicationTerms.nthDay, applicationTerms.weekDayType,
applicationTerms.amortizationMethod, applicationTerms.interestMethod, applicationTerms.interestRatePerPeriod,
applicationTerms.interestRatePeriodFrequencyType, applicationTerms.annualNominalInterestRate,
applicationTerms.interestCalculationPeriodMethod, applicationTerms.allowPartialPeriodInterestCalcualtion,
applicationTerms.principal, applicationTerms.expectedDisbursementDate, applicationTerms.repaymentsStartingFromDate,
applicationTerms.calculatedRepaymentsStartingFromDate, applicationTerms.principalGrace,
applicationTerms.recurringMoratoriumOnPrincipalPeriods, applicationTerms.interestPaymentGrace,
applicationTerms.interestChargingGrace, applicationTerms.interestChargedFromDate, applicationTerms.inArrearsTolerance,
applicationTerms.multiDisburseLoan, applicationTerms.actualFixedEmiAmount, applicationTerms.disbursementDatas,
applicationTerms.maxOutstandingBalance, applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType,
applicationTerms.daysInYearType, applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
applicationTerms.recalculationFrequencyType, applicationTerms.compoundingCalendarInstance,
applicationTerms.compoundingFrequencyType, applicationTerms.principalThresholdForLastInstalment,
applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preClosureInterestCalculationStrategy,
applicationTerms.loanCalendar, applicationTerms.approvedPrincipal.getAmount(), loanTermVariations,
applicationTerms.calendarHistoryDataWrapper, applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled,
applicationTerms.numberOfDays, applicationTerms.isSkipRepaymentOnFirstDayOfMonth, applicationTerms.holidayDetailDTO,
applicationTerms.allowCompoundingOnEod, applicationTerms.isEqualAmortization,
applicationTerms.isFirstRepaymentDateAllowedOnHoliday, applicationTerms.isInterestToBeRecoveredFirstWhenGreaterThanEMI,
applicationTerms.fixedPrincipalPercentagePerInstallment, applicationTerms.isPrincipalCompoundingDisabledForOverdueLoans);
}
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer nthDay, final DayOfWeekType weekDayType,
@@ -1702,6 +1588,18 @@ public final class LoanApplicationTerms {
return disbursedAmount;
}
public Money getTotalMultiDisbursedAmount() {
Money disbursedAmount = Money.zero(getCurrency());
if (isMultiDisburseLoan()) {
for (DisbursementData disbursement : getDisbursementDatas()) {
disbursedAmount = disbursedAmount.plus(disbursement.getPrincipal());
}
} else {
disbursedAmount = getPrincipal();
}
return disbursedAmount;
}
public void updatePeriodNumberApplicableForPrincipalOrInterestGrace(final Integer periodsApplicationForGrace) {
int applicablePeriodNumber = periodsApplicationForGrace;
int graceOnPrincipal = defaultToZeroIfNull(this.principalGrace);
@@ -2996,20 +2996,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ChangedTransactionDetail changedTransactionDetail = null;
if (command.entityId() != null) {
changedTransactionDetail = loan.updateDisbursementDateAndAmountForTranche(loanDisbursementDetails, command, changes,
scheduleGeneratorDTO);
} else {
// BigDecimal setAmount = loan.getApprovedPrincipal();
Collection<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
BigDecimal setAmount = BigDecimal.ZERO;
for (LoanDisbursementDetails details : loanDisburseDetails) {
if (details.actualDisbursementDate() != null) {
setAmount = setAmount.add(details.principal());
}
}
loan.repaymentScheduleDetail().setPrincipal(setAmount);
loan.repaymentScheduleDetail().setPrincipal(loan.getPrincipalAmountForRepaymentSchedule());
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
@@ -1288,7 +1288,7 @@ public class ClientLoanIntegrationTest {
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
.withExpectedDisbursementDate("20 September 2011") //
.withSubmittedOnDate("20 September 2011") //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withCollaterals(collaterals).withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
@@ -1316,7 +1316,7 @@ public class ClientLoanIntegrationTest {
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
.withExpectedDisbursementDate(fourMonthsfromNow) //
.withSubmittedOnDate(fourMonthsfromNow) //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withCollaterals(collaterals).withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
@@ -4757,7 +4757,7 @@ public class ClientLoanIntegrationTest {
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
.withExpectedDisbursementDate(disbursementDate) //
.withSubmittedOnDate(disbursementDate) //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withPrincipalGrace(graceOnPrincipalPayment) //
.withInterestGrace(graceOnInterestPayment)//
.withCharges(charges)//
@@ -5816,7 +5816,7 @@ public class ClientLoanIntegrationTest {
.withRepaymentEveryAfter("2")
//
.withAmortizationTypeAsEqualPrincipalPayments().withRepaymentFrequencyTypeAsWeeks()
.withwithRepaymentStrategy(LoanProductTestBuilder.RBI_INDIA_STRATEGY).withInterestTypeAsFlatBalance()
.withRepaymentStrategy(LoanProductTestBuilder.RBI_INDIA_STRATEGY).withInterestTypeAsFlatBalance()
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withPrincipalGrace("1").withInterestGrace("1")
.withLoanTermFrequency("4") //
.withLoanTermFrequencyAsMonths() //
@@ -478,7 +478,7 @@ public class DelinquencyBucketsIntegrationTest {
// First Loan Delinquency Classification after Disbursement command
assertEquals(getLoansLoanIdResponse.getDelinquencyRange().getClassification(), classificationExpected);
printRepaymentSchedule(getLoansLoanIdResponse);
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
// Apply a partial repayment
operationDate = Utils.dateFormatter.format(todaysDate);
@@ -660,7 +660,7 @@ public class DelinquencyBucketsIntegrationTest {
// First Loan Delinquency Classification after Disbursement command
assertEquals(getLoansLoanIdResponse.getDelinquencyRange().getClassification(), classificationExpected01);
printRepaymentSchedule(getLoansLoanIdResponse);
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
// Apply a repayment to get a first full paid installment
operationDate = Utils.dateFormatter.format(todaysDate);
@@ -752,7 +752,7 @@ public class DelinquencyBucketsIntegrationTest {
// First Loan Delinquency Classification after Disbursement command
assertEquals(getLoansLoanIdResponse.getDelinquencyRange().getClassification(), classificationExpected);
printRepaymentSchedule(getLoansLoanIdResponse);
loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
// Apply a repayment to get a full paid installment
operationDate = Utils.dateFormatter.format(todaysDate);
@@ -176,7 +176,7 @@ public class LoanAuditingIntegrationTest {
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
.withExpectedDisbursementDate(submittedOnDate) //
.withSubmittedOnDate(submittedOnDate) //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
@@ -19,6 +19,8 @@
package org.apache.fineract.integrationtests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
@@ -32,6 +34,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
import org.apache.fineract.integrationtests.common.Utils;
@@ -43,17 +49,15 @@ import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Slf4j
@SuppressWarnings({ "rawtypes", "unchecked" })
public class LoanDisbursementDetailsIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(LoanDisbursementDetailsIntegrationTest.class);
private ResponseSpecification responseSpec;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;
private Integer loanID;
private Integer loanId;
private Integer disbursementId;
final String approveDate = "01 March 2014";
final String expectedDisbursementDate = "01 March 2014";
@@ -80,18 +84,18 @@ public class LoanDisbursementDetailsIntegrationTest {
createTranches.add(this.loanTransactionHelper.createTrancheDetail(id, "01 September 2015", "5000"));
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2014");
LOG.info("---------------------------------CLIENT CREATED WITH ID---------------------------------------------------{}", clientID);
log.info("---------------------------------CLIENT CREATED WITH ID---------------------------------------------------{}", clientID);
final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(new LoanProductTestBuilder()
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withAmortizationTypeAsEqualInstallments().withTranches(true)
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).build(null));
LOG.info("----------------------------------LOAN PRODUCT CREATED WITH ID------------------------------------------- {}",
log.info("----------------------------------LOAN PRODUCT CREATED WITH ID------------------------------------------- {}",
loanProductID);
final Integer loanIDWithEmi = applyForLoanApplicationWithEmiAmount(clientID, loanProductID, proposedAmount, createTranches,
installmentAmount);
LOG.info("-----------------------------------LOAN CREATED WITH EMI LOANID------------------------------------------------- {}",
log.info("-----------------------------------LOAN CREATED WITH EMI LOANID------------------------------------------------- {}",
loanIDWithEmi);
HashMap repaymentScheduleWithEmi = (HashMap) this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec,
@@ -105,24 +109,23 @@ public class LoanDisbursementDetailsIntegrationTest {
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanIDWithEmi);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
log.info("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount("01 June 2015", "01 June 2015", "10000", loanIDWithEmi,
createTranches);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
LOG.info(
log.info(
"-----------------------------------MULTI DISBURSAL LOAN WITH EMI APPROVED SUCCESSFULLY---------------------------------------");
final Integer loanIDWithoutEmi = applyForLoanApplicationWithEmiAmount(clientID, loanProductID, proposedAmount, createTranches,
withoutInstallmentAmount);
HashMap repaymentScheduleWithoutEmi = (HashMap) this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec,
loanIDWithoutEmi, "repaymentSchedule");
this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, loanIDWithoutEmi, "repaymentSchedule");
ArrayList<HashMap> periods1 = (ArrayList<HashMap>) repaymentScheduleWithEmi.get("periods");
assertEquals(15, periods1.size());
LOG.info("-----------------------------------LOAN CREATED WITHOUT EMI LOANID------------------------------------------------- {}",
log.info("-----------------------------------LOAN CREATED WITHOUT EMI LOANID------------------------------------------------- {}",
loanIDWithoutEmi);
/* To be uncommented once issue MIFOSX-2006 is closed. */
@@ -131,12 +134,12 @@ public class LoanDisbursementDetailsIntegrationTest {
HashMap loanStatusMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanIDWithoutEmi);
LoanStatusChecker.verifyLoanIsPending(loanStatusMap);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
log.info("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount("01 June 2015", "01 June 2015", "10000",
loanIDWithoutEmi, createTranches);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
LOG.info(
log.info(
"-----------------------------------MULTI DISBURSAL LOAN WITHOUT EMI APPROVED SUCCESSFULLY---------------------------------------");
}
@@ -205,6 +208,7 @@ public class LoanDisbursementDetailsIntegrationTest {
list.add(expectedRepaymentSchedule14);
for (int i = 0; i < list.size(); i++) {
log.info("values {} {} {}", i, periods.get(i), list.get(i));
this.assertRepaymentScheduleValuesWithEMI(periods.get(i), list.get(i), i);
}
}
@@ -213,7 +217,7 @@ public class LoanDisbursementDetailsIntegrationTest {
assertEquals(period.get("dueDate").toString(), expectedRepaymentSchedule.getDueDate());
assertEquals(period.get("principalLoanBalanceOutstanding"), expectedRepaymentSchedule.getPrincipalLoanBalanceOutstanding());
LOG.info("{}", period.get("totalOriginalDueForPeriod").toString());
log.info("{}", period.get("totalOriginalDueForPeriod").toString());
assertEquals(Float.parseFloat(period.get("totalOriginalDueForPeriod").toString()),
expectedRepaymentSchedule.getTotalOriginalDueForPeriod().floatValue(), 0.0f);
@@ -233,103 +237,25 @@ public class LoanDisbursementDetailsIntegrationTest {
}
}
/* Uncomment and modify test builder values once MIFOSX-2006 is closed. */
/*
* private void validateRepaymentScheduleWithoutEMI(ArrayList<HashMap> periods){ LoanDisbursementTestBuilder
* expectedRepaymentSchedule0 = new LoanDisbursementTestBuilder( "[2015, 6, 1]", 0.0f, 0.0f, null, null, 5000.0f,
* null, null, null);
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule1 = new LoanDisbursementTestBuilder( "[2015, 7, 1]", 800.0f,
* 800.0f, 50.0f, 750.0f, 4250.0f, 750.0f, 750.0f, "[2015, 6, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule2 = new LoanDisbursementTestBuilder( "[2015, 8, 1]", 800.0f,
* 800.0f, 42.5f, 757.5f, 3492.5f, 757.5f, 757.5f, "[2015, 7, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule3 = new LoanDisbursementTestBuilder( "[2015, 9, 1]", 0.0f,
* 0.0f, null, null, 5000.0f, null, null, null);
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule4 = new LoanDisbursementTestBuilder( "[2015, 9, 1]", 800.0f,
* 800.0f, 34.92f, 765.08f, 7727.42f, 765.08f, 765.08f, "[2015, 8, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule5 = new LoanDisbursementTestBuilder( "[2015, 10, 1]",
* 800.0f, 800.0f, 77.27f, 722.73f, 7004.69f, 722.73f, 722.73f, "[2015, 9, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule6 = new LoanDisbursementTestBuilder( "[2015, 11, 1]",
* 800.0f, 800.0f, 70.05f, 729.95f, 6274.74f, 729.95f, 729.95f, "[2015, 10, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule7 = new LoanDisbursementTestBuilder( "[2015, 12, 1]",
* 800.0f, 800.0f, 62.75f, 737.25f, 5537.49f, 737.25f, 737.25f, "[2015, 11, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule8 = new LoanDisbursementTestBuilder( "[2016, 1, 1]", 800.0f,
* 800.0f, 55.37f, 744.63f, 4792.86f, 744.63f, 744.63f, "[2015, 12, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule9 = new LoanDisbursementTestBuilder( "[2016, 2, 1]", 800.0f,
* 800.0f, 47.93f, 752.07f, 4040.79f, 752.07f, 752.07f, "[2016, 1, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule10 = new LoanDisbursementTestBuilder( "[2016, 3, 1]",
* 800.0f, 800.0f, 40.41f, 759.59f, 3281.2f, 759.59f, 759.59f, "[2016, 2, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule11 = new LoanDisbursementTestBuilder( "[2016, 4, 1]",
* 800.0f, 800.0f, 32.81f, 767.19f, 2514.01f, 767.19f, 767.19f, "[2016, 3, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule12 = new LoanDisbursementTestBuilder( "[2016, 5, 1]",
* 800.0f, 800.0f, 25.14f, 774.86f, 1739.15f, 774.86f, 774.86f, "[2016, 4, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule13 = new LoanDisbursementTestBuilder( "[2016, 6, 1]",
* 800.0f, 800.0f, 17.39f, 782.61f, 956.54f, 782.61f, 782.61f, "[2016, 5, 1]");
*
* LoanDisbursementTestBuilder expectedRepaymentSchedule14 = new LoanDisbursementTestBuilder( "[2016, 7, 1]",
* 966.11f, 966.11f, 9.57f, 956.54f, 0.0f, 956.54f, 956.54f, "[2016, 6, 1]");
*
* ArrayList<LoanDisbursementTestBuilder> list = new ArrayList<LoanDisbursementTestBuilder>();
* list.add(expectedRepaymentSchedule0); list.add(expectedRepaymentSchedule1); list.add(expectedRepaymentSchedule2);
* list.add(expectedRepaymentSchedule3); list.add(expectedRepaymentSchedule4); list.add(expectedRepaymentSchedule5);
* list.add(expectedRepaymentSchedule6); list.add(expectedRepaymentSchedule7); list.add(expectedRepaymentSchedule8);
* list.add(expectedRepaymentSchedule9); list.add(expectedRepaymentSchedule10);
* list.add(expectedRepaymentSchedule11); list.add(expectedRepaymentSchedule12);
* list.add(expectedRepaymentSchedule13); list.add(expectedRepaymentSchedule14);
*
* for (int i = 0; i < list.size(); i++) { this.assertRepaymentScheduleValuesWithoutEMI(periods.get(i), list.get(i),
* i); } }
*
* private void assertRepaymentScheduleValuesWithoutEMI(HashMap period, LoanDisbursementTestBuilder
* expectedRepaymentSchedule, int position) {
*
* assertEquals(period.get("dueDate").toString(), expectedRepaymentSchedule.getDueDate());
* assertEquals(period.get("principalLoanBalanceOutstanding"),
* expectedRepaymentSchedule.getPrincipalLoanBalanceOutstanding());
* assertEquals(period.get("totalOriginalDueForPeriod"), expectedRepaymentSchedule.getTotalOriginalDueForPeriod());
* assertEquals(period.get("totalOutstandingForPeriod"), expectedRepaymentSchedule.getTotalOutstandingForPeriod());
*
* if (position != 0 && position != 3) {
*
* assertEquals(period.get("interestOutstanding"), expectedRepaymentSchedule.getInterestOutstanding());
* assertEquals(period.get("principalOutstanding"), expectedRepaymentSchedule.getPrincipalOutstanding());
* assertEquals(period.get("principalDue"), expectedRepaymentSchedule.getPrincipalDue());
* assertEquals(period.get("principalOriginalDue"), expectedRepaymentSchedule.getPrincipalOriginalDue());
* assertEquals(period.get("fromDate").toString(), expectedRepaymentSchedule.getFromDate()); } }
*/
private Integer applyForLoanApplicationWithEmiAmount(final Integer clientID, final Integer loanProductID, final String proposedAmount,
private Integer applyForLoanApplicationWithEmiAmount(final Integer clientId, final Integer loanProductId, final String proposedAmount,
List<HashMap> tranches, final String installmentAmount) {
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
log.info("----------------APPLYING FOR LOAN APPLICATION");
List<HashMap> collaterals = new ArrayList<>();
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
Assertions.assertNotNull(collateralId);
final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
clientID.toString(), collateralId);
clientId.toString(), collateralId);
Assertions.assertNotNull(clientCollateralId);
addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
final String loanApplicationJSON = new LoanApplicationTestBuilder()
//
.withPrincipal(proposedAmount)
//
.withLoanTermFrequency("12")
//
.withLoanTermFrequencyAsMonths()
//
.withNumberOfRepayments("12").withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths() //
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
.withPrincipal(proposedAmount) //
.withLoanTermFrequency("12") //
.withLoanTermFrequencyAsMonths() //
.withNumberOfRepayments("12") //
.withRepaymentEveryAfter("1") //
.withRepaymentFrequencyTypeAsMonths() //
.withInterestRatePerPeriod("1") //
.withExpectedDisbursementDate("01 June 2015") //
.withTranches(tranches) //
@@ -337,12 +263,55 @@ public class LoanDisbursementDetailsIntegrationTest {
.withInterestTypeAsDecliningBalance() //
.withSubmittedOnDate("01 June 2015") //
.withAmortizationTypeAsEqualInstallments() //
.withCollaterals(collaterals).build(clientID.toString(), loanProductID.toString(), null);
.withCollaterals(collaterals).build(clientId.toString(), loanProductId.toString(), null);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
@Test
public void validateEqualInstallmentsForMultiTrancheLoan() {
List<HashMap> emptyTranches = new ArrayList<>();
final String operationDate = "01 January 2014";
final String principal = "1000";
final String disbursedPrincipal = "900";
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, operationDate);
log.info("-----------------CLIENT CREATED WITH ID------------------- {}", clientId);
final String loanProductJSON = new LoanProductTestBuilder().withAmortizationTypeAsEqualInstallments() //
.withInterestTypeAsDecliningBalance().withMoratorium("", "").withInterestCalculationPeriodTypeAsRepaymentPeriod(true)
.withInterestTypeAsDecliningBalance() //
.withMultiDisburse() //
.withDisallowExpectectedDisbursements(true) //
.build(null);
log.info("Product {}", loanProductJSON);
final Integer loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
log.info("------------------LOAN PRODUCT CREATED WITH ID----------- {}", loanProductId);
final Integer loanId = applyForMultiTrancheLoanApplication(clientId.toString(), loanProductId.toString(), principal, operationDate);
log.info("-------------------LOAN CREATED WITH loanId----------------- {}", loanId);
this.loanTransactionHelper.approveLoanWithApproveAmount(operationDate, expectedDisbursementDate, principal, loanId, emptyTranches);
log.info("-------------------MULTI DISBURSAL LOAN APPROVED SUCCESSFULLY-------");
GetLoansLoanIdResponse getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
assertNotNull(getLoansLoanIdResponse);
this.loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
loanTransactionHelper.disburseLoanWithTransactionAmount(operationDate, loanId, disbursedPrincipal);
log.info("-------------------MULTI DISBURSAL LOAN DISBURSED SUCCESSFULLY-------");
getLoansLoanIdResponse = this.loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
assertNotNull(getLoansLoanIdResponse);
this.loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
final Double limit = 2.0;
evaluateEqualInstallmentsForRepaymentSchedule(getLoansLoanIdResponse.getRepaymentSchedule(), limit);
log.info("-----------MULTI DISBURSAL LOAN EQUAL INSTALLMENTS SUCCESSFULLY-------");
}
@Test
public void createApproveAndValidateMultiDisburseLoan() throws ParseException {
@@ -351,28 +320,29 @@ public class LoanDisbursementDetailsIntegrationTest {
createTranches.add(this.loanTransactionHelper.createTrancheDetail(id, "01 March 2014", "1000"));
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2014");
LOG.info("---------------------------------CLIENT CREATED WITH ID--------------------------------------------------- {}", clientID);
log.info("---------------------------------CLIENT CREATED WITH ID--------------------------------------------------- {}", clientID);
final Integer loanProductID = this.loanTransactionHelper
.getLoanProductId(new LoanProductTestBuilder().withInterestTypeAsDecliningBalance().withTranches(true)
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).build(null));
LOG.info("----------------------------------LOAN PRODUCT CREATED WITH ID------------------------------------------- {}",
log.info("----------------------------------LOAN PRODUCT CREATED WITH ID------------------------------------------- {}",
loanProductID);
this.loanID = applyForLoanApplicationWithTranches(clientID, loanProductID, proposedAmount, createTranches);
LOG.info("-----------------------------------LOAN CREATED WITH LOANID------------------------------------------------- {}", loanID);
this.loanId = applyForLoanApplicationWithTranches(clientID, loanProductID, proposedAmount, createTranches);
log.info("-----------------------------------LOAN CREATED WITH LOANID------------------------------------------------- {}",
this.loanId);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, this.loanId);
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
log.info("-----------------------------------APPROVE LOAN-----------------------------------------------------------");
loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(approveDate, expectedDisbursementDate, approvalAmount,
loanID, createTranches);
this.loanId, createTranches);
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
LOG.info("-----------------------------------MULTI DISBURSAL LOAN APPROVED SUCCESSFULLY---------------------------------------");
log.info("-----------------------------------MULTI DISBURSAL LOAN APPROVED SUCCESSFULLY---------------------------------------");
ArrayList<HashMap> disbursementDetails = (ArrayList<HashMap>) this.loanTransactionHelper.getLoanDetail(this.requestSpec,
this.responseSpec, this.loanID, "disbursementDetails");
this.responseSpec, this.loanId, "disbursementDetails");
this.disbursementId = (Integer) disbursementDetails.get(0).get("id");
this.editLoanDisbursementDetails();
}
@@ -386,7 +356,7 @@ public class LoanDisbursementDetailsIntegrationTest {
private void addNewDisbursementDetails() throws ParseException {
List<HashMap> addTranches = new ArrayList<>();
ArrayList<HashMap> disbursementDetails = (ArrayList<HashMap>) this.loanTransactionHelper.getLoanDetail(this.requestSpec,
this.responseSpec, this.loanID, "disbursementDetails");
this.responseSpec, this.loanId, "disbursementDetails");
ArrayList expectedDisbursementDate = (ArrayList) disbursementDetails.get(0).get("expectedDisbursementDate");
String date = formatExpectedDisbursementDate(expectedDisbursementDate.toString());
@@ -397,14 +367,14 @@ public class LoanDisbursementDetailsIntegrationTest {
addTranches.add(this.loanTransactionHelper.createTrancheDetail(id, "04 March 2014", "500"));
/* Add disbursement detail */
this.loanTransactionHelper.addAndDeleteDisbursementDetail(this.loanID, this.approvalAmount, this.expectedDisbursementDate,
this.loanTransactionHelper.addAndDeleteDisbursementDetail(this.loanId, this.approvalAmount, this.expectedDisbursementDate,
addTranches, "");
}
private void deleteDisbursmentDetails() throws ParseException {
List<HashMap> deleteTranches = new ArrayList<>();
ArrayList<HashMap> disbursementDetails = (ArrayList<HashMap>) this.loanTransactionHelper.getLoanDetail(this.requestSpec,
this.responseSpec, this.loanID, "disbursementDetails");
this.responseSpec, this.loanId, "disbursementDetails");
/* Delete the last tranche */
for (int i = 0; i < disbursementDetails.size() - 1; i++) {
ArrayList expectedDisbursementDate = (ArrayList) disbursementDetails.get(i).get("expectedDisbursementDate");
@@ -414,7 +384,7 @@ public class LoanDisbursementDetailsIntegrationTest {
}
/* Add disbursement detail */
this.loanTransactionHelper.addAndDeleteDisbursementDetail(this.loanID, this.approvalAmount, this.expectedDisbursementDate,
this.loanTransactionHelper.addAndDeleteDisbursementDetail(this.loanId, this.approvalAmount, this.expectedDisbursementDate,
deleteTranches, "");
}
@@ -422,11 +392,11 @@ public class LoanDisbursementDetailsIntegrationTest {
String updatedExpectedDisbursementDate = "01 March 2014";
String updatedPrincipal = "900";
/* Update */
this.loanTransactionHelper.editDisbursementDetail(this.loanID, this.disbursementId, this.approvalAmount,
this.loanTransactionHelper.editDisbursementDetail(this.loanId, this.disbursementId, this.approvalAmount,
this.expectedDisbursementDate, updatedExpectedDisbursementDate, updatedPrincipal, "");
/* Validate Edit */
ArrayList<HashMap> disbursementDetails = (ArrayList<HashMap>) this.loanTransactionHelper.getLoanDetail(this.requestSpec,
this.responseSpec, this.loanID, "disbursementDetails");
this.responseSpec, this.loanId, "disbursementDetails");
assertEquals(Float.parseFloat(updatedPrincipal), disbursementDetails.get(0).get("principal"));
ArrayList expectedDisbursementDate = (ArrayList) disbursementDetails.get(0).get("expectedDisbursementDate");
String date = formatExpectedDisbursementDate(expectedDisbursementDate.toString());
@@ -435,7 +405,6 @@ public class LoanDisbursementDetailsIntegrationTest {
}
private String formatExpectedDisbursementDate(String expectedDisbursementDate) throws ParseException {
SimpleDateFormat source = new SimpleDateFormat("[yyyy, MM, dd]");
SimpleDateFormat target = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
String date = target.format(source.parse(expectedDisbursementDate));
@@ -443,35 +412,56 @@ public class LoanDisbursementDetailsIntegrationTest {
return date;
}
private Integer applyForLoanApplicationWithTranches(final Integer clientID, final Integer loanProductID, String principal,
private Integer applyForLoanApplicationWithTranches(final Integer clientId, final Integer loanProductId, String principal,
List<HashMap> tranches) {
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
log.info("----------------APPLYING FOR LOAN APPLICATION");
List<HashMap> collaterals = new ArrayList<>();
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
Assertions.assertNotNull(collateralId);
final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
clientID.toString(), collateralId);
clientId.toString(), collateralId);
Assertions.assertNotNull(clientCollateralId);
addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
final String loanApplicationJSON = new LoanApplicationTestBuilder()
//
.withPrincipal(principal)
//
.withLoanTermFrequency("5")
//
.withLoanTermFrequencyAsMonths()
//
.withNumberOfRepayments("5").withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths() //
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
.withPrincipal(principal) //
.withLoanTermFrequency("5") //
.withLoanTermFrequencyAsMonths() //
.withNumberOfRepayments("5") //
.withRepaymentEveryAfter("1") //
.withRepaymentFrequencyTypeAsMonths() //
.withInterestRatePerPeriod("2") //
.withExpectedDisbursementDate("01 March 2014") //
.withTranches(tranches) //
.withInterestTypeAsDecliningBalance() //
.withSubmittedOnDate("01 March 2014") //
.withCollaterals(collaterals).build(clientID.toString(), loanProductID.toString(), null);
.withCollaterals(collaterals).build(clientId.toString(), loanProductId.toString(), null);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
private Integer applyForMultiTrancheLoanApplication(final String clientId, final String loanProductId, String principal,
String operationDate) {
log.info("----------------APPLYING FOR MULTI TRANCHE LOAN APPLICATION");
List<HashMap> emptyData = new ArrayList<>();
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
.withPrincipal(principal) //
.withLoanTermFrequency("3") //
.withLoanTermFrequencyAsMonths() //
.withNumberOfRepayments("3") //
.withRepaymentEveryAfter("1") //
.withRepaymentFrequencyTypeAsMonths() //
.withInterestRatePerPeriod("0") //
.withExpectedDisbursementDate(operationDate) //
.withTranches(emptyData) //
.withInterestTypeAsDecliningBalance() //
.withSubmittedOnDate(operationDate) //
.withCollaterals(emptyData) //
.build(clientId, loanProductId, null);
log.info("Loan account {}", loanApplicationJSON);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {
collaterals.add(collaterals(collateralId, quantity));
}
@@ -483,4 +473,24 @@ public class LoanDisbursementDetailsIntegrationTest {
return collateral;
}
public void evaluateEqualInstallmentsForRepaymentSchedule(GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule, Double limit) {
Double totalOutstandingForPeriod = 0.0;
Double totalInstallmentAmountForPeriod = 0.0;
if (getLoanRepaymentSchedule != null) {
log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
if (period.getPeriod() != null) {
log.info("Period number {} for due date {} and outstanding {} {}", period.getPeriod(), period.getDueDate(),
period.getTotalOutstandingForPeriod(), period.getTotalInstallmentAmountForPeriod());
if (period.getPeriod() == 1) {
totalOutstandingForPeriod = period.getTotalOutstandingForPeriod();
totalInstallmentAmountForPeriod = period.getTotalInstallmentAmountForPeriod();
} else {
assertTrue(Math.abs(period.getTotalOutstandingForPeriod() - totalOutstandingForPeriod) <= limit);
assertTrue(Math.abs(period.getTotalInstallmentAmountForPeriod() - totalInstallmentAmountForPeriod) <= limit);
}
}
}
}
}
}
@@ -245,7 +245,7 @@ public class LoanRepaymentRescheduleAtDisbursementTest {
.withInterestCalculationPeriodTypeAsDays() //
.withExpectedDisbursementDate(disbursementDate) //
.withSubmittedOnDate(disbursementDate) //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withCollaterals(collaterals).withCharges(charges)//
.build(clientID.toString(), loanProductID.toString(), null);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
@@ -204,7 +204,7 @@ public class LoanRescheduleWithAdvancePaymentTest {
.withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestCalculationPeriodTypeAsDays()
.withInterestRatePerPeriod("12").withInterestTypeAsDecliningBalance().withSubmittedOnDate(submittedDate)
.withExpectedDisbursementDate(submittedDate).withFirstRepaymentDate(firstRepaymentDate)
.withwithRepaymentStrategy(LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY)
.withRepaymentStrategy(LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY)
.withinterestChargedFromDate(submittedDate).build(this.clientId.toString(), this.loanProductId.toString(), null);
this.loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
@@ -318,7 +318,7 @@ public class LoanRescheduleWithAdvancePaymentTest {
.withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestCalculationPeriodTypeAsDays()
.withInterestRatePerPeriod("12").withInterestTypeAsDecliningBalance().withSubmittedOnDate(submittedDate)
.withExpectedDisbursementDate(submittedDate).withFirstRepaymentDate(firstRepaymentDate)
.withwithRepaymentStrategy(LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY)
.withRepaymentStrategy(LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY)
.withinterestChargedFromDate(submittedDate).build(this.clientId.toString(), this.loanProductId.toString(), null);
this.loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
@@ -409,7 +409,7 @@ public class LoanReschedulingWithinCenterTest {
.withInterestCalculationPeriodTypeAsDays() //
.withExpectedDisbursementDate(disbursementDate) //
.withSubmittedOnDate(disbursementDate) //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withCollaterals(collaterals).withCharges(charges)//
.build(clientID.toString(), groupId.toString(), loanProductID.toString(), null);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
@@ -187,7 +187,7 @@ public class LoanTransactionAuditingIntegrationTest {
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod() //
.withExpectedDisbursementDate(submittedOnDate) //
.withSubmittedOnDate(submittedOnDate) //
.withwithRepaymentStrategy(repaymentStrategy) //
.withRepaymentStrategy(repaymentStrategy) //
.withCharges(charges).build(clientID.toString(), loanProductID.toString(), savingsId);
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
}
@@ -335,7 +335,7 @@ public class LoanApplicationTestBuilder {
return this;
}
public LoanApplicationTestBuilder withwithRepaymentStrategy(final String transactionProcessingStrategy) {
public LoanApplicationTestBuilder withRepaymentStrategy(final String transactionProcessingStrategy) {
this.transactionProcessingID = transactionProcessingStrategy;
return this;
}
@@ -98,6 +98,7 @@ public class LoanProductTestBuilder {
private Boolean allowApprovedDisbursedAmountsOverApplied = false;
private String overAppliedCalculationType = null;
private Integer overAppliedNumber = null;
private Boolean isEqualAmortization = false;
private Boolean isInterestRecalculationEnabled = false;
private String daysInYearType = "1";
@@ -168,6 +169,7 @@ public class LoanProductTestBuilder {
map.put("accountingRule", this.accountingRule);
map.put("minPrincipal", this.minPrincipal);
map.put("maxPrincipal", this.maxPrincipal);
map.put("isEqualAmortization", this.isEqualAmortization);
map.put("overdueDaysForNPA", this.overdueDaysForNPA);
if (this.minimumDaysBetweenDisbursalAndFirstRepayment != null) {
map.put("minimumDaysBetweenDisbursalAndFirstRepayment", this.minimumDaysBetweenDisbursalAndFirstRepayment);
@@ -382,6 +384,11 @@ public class LoanProductTestBuilder {
return this;
}
public LoanProductTestBuilder withEqualAmortization(boolean isEqualAmortization) {
this.isEqualAmortization = isEqualAmortization;
return this;
}
public LoanProductTestBuilder withMultiDisburse() {
this.multiDisburseLoan = true;
return this;
@@ -42,6 +42,8 @@ import javax.ws.rs.core.MediaType;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.GetDelinquencyTagHistoryResponse;
import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
import org.apache.fineract.client.util.JSON;
@@ -1062,4 +1064,16 @@ public class LoanTransactionHelper {
"---------------------------------GET A LOAN TRANSACTION ENTITY AUDIT FIELDS---------------------------------------------");
return Utils.performServerGet(requestSpec, responseSpec, GET_LOAN_TRANSACTION_URL, jsonReturn);
}
public void printRepaymentSchedule(GetLoansLoanIdResponse getLoansLoanIdResponse) {
GetLoansLoanIdRepaymentSchedule getLoanRepaymentSchedule = getLoansLoanIdResponse.getRepaymentSchedule();
if (getLoanRepaymentSchedule != null) {
log.info("Loan with {} periods", getLoanRepaymentSchedule.getPeriods().size());
for (GetLoansLoanIdRepaymentPeriod period : getLoanRepaymentSchedule.getPeriods()) {
log.info("Period number {} for due date {} and outstanding {}", period.getPeriod(), period.getDueDate(),
period.getTotalOutstandingForPeriod());
}
}
}
}