Skip to content

Commit

Permalink
Merge pull request #1327 from fynmanoj/FINERACT-1109-rework
Browse files Browse the repository at this point in the history
FINERACT-1109-rework-with-fixed-emi
  • Loading branch information
avikganguly01 authored Sep 14, 2020
2 parents cdf3c76 + 10dd1fa commit 44bc6e6
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ public void initialize() {

this.generalResponseSpec = new ResponseSpecBuilder().build();

// create all required entities
this.createRequiredEntities();
}

@AfterEach
Expand All @@ -90,6 +88,16 @@ private void createRequiredEntities() {
this.enableConfig();
}

/**
* Creates the client, loan product, and loan entities
**/
private void createRequiredEntitiesWithRecalculationEnabled() {
this.createClientEntity();
this.createLoanProductWithInterestRecalculation();
this.createLoanEntity();
this.enableConfig();
}

/**
* create a new client
**/
Expand All @@ -114,6 +122,38 @@ private void createLoanProductEntity() {
LOG.info("Successfully created loan product (ID:{}) ", this.loanProductId);
}

private void createLoanProductWithInterestRecalculation() {
LOG.info(
"---------------------------------CREATING LOAN PRODUCT WITH RECALULATION ENABLED ------------------------------------------");

final String interestRecalculationCompoundingMethod = LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE;
final String rescheduleStrategyMethod = LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS;
final String recalculationRestFrequencyType = LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY;
final String recalculationRestFrequencyInterval = "0";
final String preCloseInterestCalculationStrategy = LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE;
final String recalculationCompoundingFrequencyType = null;
final String recalculationCompoundingFrequencyInterval = null;
final Integer recalculationCompoundingFrequencyOnDayType = null;
final Integer recalculationCompoundingFrequencyDayOfWeekType = null;
final Integer recalculationRestFrequencyOnDayType = null;
final Integer recalculationRestFrequencyDayOfWeekType = null;

final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(loanPrincipalAmount)
.withNumberOfRepayments(numberOfRepayments).withinterestRatePerPeriod(interestRatePerPeriod)
.withInterestRateFrequencyTypeAsYear().withInterestTypeAsDecliningBalance().withInterestCalculationPeriodTypeAsDays()
.withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod,
preCloseInterestCalculationStrategy)
.withInterestRecalculationRestFrequencyDetails(recalculationRestFrequencyType, recalculationRestFrequencyInterval,
recalculationRestFrequencyOnDayType, recalculationRestFrequencyDayOfWeekType)
.withInterestRecalculationCompoundingFrequencyDetails(recalculationCompoundingFrequencyType,
recalculationCompoundingFrequencyInterval, recalculationCompoundingFrequencyOnDayType,
recalculationCompoundingFrequencyDayOfWeekType)
.build(null);

this.loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
LOG.info("Successfully created loan product (ID:{}) ", this.loanProductId);
}

/**
* submit a new loan application, approve and disburse the loan
**/
Expand Down Expand Up @@ -173,6 +213,8 @@ private void disableConfig() {

@Test
public void testCreateLoanRescheduleRequestWithInterestAppropriation() {
// create all required entities
this.createRequiredEntities();
this.createAndApproveLoanRescheduleRequestForInterestAppropriation();

}
Expand Down Expand Up @@ -210,8 +252,108 @@ private void createAndApproveLoanRescheduleRequestForInterestAppropriation() {
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");

assertEquals(12186, totalDueForPeriod.intValue(), "TOTAL EXPECTED LAST REPAYMENT is NOK");
assertEquals(123682, totalExpectedRepayment.intValue(), "TOTAL EXPECTED LAST REPAYMENT is NOK");
assertEquals(12186, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
assertEquals(123682, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");

LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);

}

@Test
public void testCreateLoanRescheduleRequestWithRecalculationEnabled() {
// create all required entities
this.createRequiredEntitiesWithRecalculationEnabled();
this.createAndApproveLoanRescheduleRequestWithRecalculationEnabled();
}

/**
* create new loan reschedule request with recalculation enabled in Loan product
**/

private void createAndApproveLoanRescheduleRequestWithRecalculationEnabled() {
LOG.info(
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR LOAN WITH RECALCULATION------------------------------------");

final String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnPrincipal(null).updateGraceOnInterest(null)
.updateExtraTerms(null).updateRescheduleFromDate("04 January 2015").updateAdjustedDueDate("04 October 2015")
.updateRecalculateInterest(true).build(this.loanId.toString());

this.loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
this.loanRescheduleRequestHelper.verifyCreationOfLoanRescheduleRequest(this.loanRescheduleRequestId);

LOG.info("Successfully created loan reschedule request (ID: {} )", this.loanRescheduleRequestId);

final String aproveRequestJSON = new LoanRescheduleRequestTestBuilder().getApproveLoanRescheduleRequestJSON();
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(this.loanRescheduleRequestId, aproveRequestJSON);
final HashMap response = (HashMap) this.loanRescheduleRequestHelper.getLoanRescheduleRequest(loanRescheduleRequestId, "statusEnum");
assertTrue((Boolean) response.get("approved"));

LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);

final Map repaymentSchedule = (Map) this.loanTransactionHelper.getLoanDetail(requestSpec, generalResponseSpec, loanId,
"repaymentSchedule");
final ArrayList periods = (ArrayList) repaymentSchedule.get("periods");

HashMap period = (HashMap) periods.get(5);
Float totalDueForPeriod = (Float) period.get("totalDueForPeriod");

final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");

assertEquals(12326, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
assertEquals(131512, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");

LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);

}

@Test
public void testCreateLoanRescheduleRequestForInterestAppropriationAndFixedEMI() {
// create all required entities
this.createRequiredEntities();
this.createAndApproveLoanRescheduleRequestForInterestAppropriationAndFixedEMI();
}

/**
* create new loan reschedule request with combination of date change, interest appropriation and fixed emi
**/
private void createAndApproveLoanRescheduleRequestForInterestAppropriationAndFixedEMI() {
LOG.info(
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR INTEREST APPROPRIATTION-------------------------------------");

final String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnPrincipal(null).updateGraceOnInterest(null)
.updateExtraTerms(null).updateRescheduleFromDate("04 January 2015").updateAdjustedDueDate("04 July 2015").updateEMI("5000")
.updateEmiChangeEndDate("4 September 2015").updateRecalculateInterest(true).build(this.loanId.toString());

this.loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
this.loanRescheduleRequestHelper.verifyCreationOfLoanRescheduleRequest(this.loanRescheduleRequestId);

LOG.info("Successfully created loan reschedule request (ID: {} )", this.loanRescheduleRequestId);

final String aproveRequestJSON = new LoanRescheduleRequestTestBuilder().getApproveLoanRescheduleRequestJSON();
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(this.loanRescheduleRequestId, aproveRequestJSON);
final HashMap response = (HashMap) this.loanRescheduleRequestHelper.getLoanRescheduleRequest(loanRescheduleRequestId, "statusEnum");
assertTrue((Boolean) response.get("approved"));

LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);

final Map repaymentSchedule = (Map) this.loanTransactionHelper.getLoanDetail(requestSpec, generalResponseSpec, loanId,
"repaymentSchedule");
final ArrayList periods = (ArrayList) repaymentSchedule.get("periods");

HashMap period = (HashMap) periods.get(5);
Float totalFixedDueForPeriod = (Float) period.get("totalDueForPeriod");

HashMap period2 = (HashMap) periods.get(8);
Float totalDueForPeriod = (Float) period2.get("totalDueForPeriod");

final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");

assertEquals(5000, totalFixedDueForPeriod.intValue(), "EXPECTED FIXED REPAYMENT is NOK");

assertEquals(15316, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT is NOK");
assertEquals(120806, totalExpectedRepayment.intValue(), "TOTAL EXPECTED REPAYMENT is NOK");

LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTe
loanRepaymentScheduleTransactionProcessor, loanApplicationTerms.getTotalInterestDue(), lastRestDate, scheduledDueDate,
periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams,
lastTotalOutstandingInterestPaymentDueToGrace, installment, loanCharges);

if (loanApplicationTerms.getCurrentPeriodFixedEmiAmount() != null) {
installment.setEMIFixedSpecificToInstallmentTrue();
}

periods.add(installment);

// Updates principal paid map with efective date for reducing
Expand Down Expand Up @@ -373,15 +378,30 @@ private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTe
if (loanApplicationTerms.isInterestToBeAppropriatedEquallyWhenGreaterThanEMIEnabled()
&& loanApplicationTerms.getInterestTobeApproppriated() != null
&& loanApplicationTerms.getInterestTobeApproppriated().isGreaterThanZero()) {
Money interestFraction = loanApplicationTerms.getInterestTobeApproppriated().dividedBy(periods.size(), mc.getRoundingMode());
int emisTobeChanged = 1;
for (LoanScheduleModelPeriod installment : (List<LoanScheduleModelPeriod>) periods) {
if (!installment.isEMIFixedSpecificToInstallment()) {
emisTobeChanged++;
}
}
if (emisTobeChanged > 1) {
emisTobeChanged--;
}
Money interestTobeApproppriated = loanApplicationTerms.getInterestTobeApproppriated();
Money interestFraction = interestTobeApproppriated.dividedBy(emisTobeChanged, mc.getRoundingMode());
BigDecimal roundFraction = interestFraction.getAmount().remainder(BigDecimal.ONE);
interestFraction = interestFraction.minus(roundFraction);
roundFraction = roundFraction.multiply(new BigDecimal(periods.size()));
for (LoanScheduleModelPeriod installment : (List<LoanScheduleModelPeriod>) periods) {
installment.addInterestAmount(interestFraction);
if (!installment.isEMIFixedSpecificToInstallment()) {
installment.addInterestAmount(interestFraction);
interestTobeApproppriated = interestTobeApproppriated.minus(interestFraction);
}
}
LoanScheduleModelPeriod lastInstallment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);

if (interestTobeApproppriated.isGreaterThanZero()) {
lastInstallment.addInterestAmount(interestTobeApproppriated);
}
LoanScheduleModelPeriod installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);
installment.addInterestAmount(Money.of(currency, roundFraction));
scheduleParams.addTotalRepaymentExpected(loanApplicationTerms.getInterestTobeApproppriated());
scheduleParams.addTotalCumulativeInterest(loanApplicationTerms.getInterestTobeApproppriated());
loanApplicationTerms.setInterestTobeApproppriated(Money.zero(currency));
Expand Down Expand Up @@ -1079,6 +1099,7 @@ private LoanTermVariationParams applyLoanTermVariations(final LoanApplicationTer
if (loanTermVariationsData.isSpecificToInstallment()) {
loanApplicationTerms.setCurrentPeriodFixedEmiAmount(loanTermVariationsData.getDecimalValue());
recalculateAmounts = true;

} else {
loanApplicationTerms.setFixedEmiAmount(loanTermVariationsData.getDecimalValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public final class LoanScheduleModelDisbursementPeriod implements LoanScheduleMo
private final LocalDate disbursementDate;
private final Money principalDisbursed;
private final BigDecimal chargesDueAtTimeOfDisbursement;
private boolean isEMIFixedSpecificToInstallment = false;

public static LoanScheduleModelDisbursementPeriod disbursement(final LoanApplicationTerms loanApplicationTerms,
final BigDecimal chargesDueAtTimeOfDisbursement) {
Expand Down Expand Up @@ -127,4 +128,14 @@ public void addInterestAmount(@SuppressWarnings("unused") Money principalDue) {
public Set<LoanInterestRecalcualtionAdditionalDetails> getLoanCompoundingDetails() {
return null;
}

@Override
public void setEMIFixedSpecificToInstallmentTrue() {
this.isEMIFixedSpecificToInstallment = true;
}

@Override
public boolean isEMIFixedSpecificToInstallment() {
return isEMIFixedSpecificToInstallment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ public interface LoanScheduleModelPeriod {
void addInterestAmount(Money interestDue);

Set<LoanInterestRecalcualtionAdditionalDetails> getLoanCompoundingDetails();

void setEMIFixedSpecificToInstallmentTrue();

boolean isEMIFixedSpecificToInstallment();
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
private Money totalDue;
private final boolean recalculatedInterestComponent;
private final Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>();
private boolean isEMIFixedSpecificToInstallment = false;

public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate,
final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
Expand Down Expand Up @@ -161,4 +162,14 @@ public void addInterestAmount(Money interestDue) {
public Set<LoanInterestRecalcualtionAdditionalDetails> getLoanCompoundingDetails() {
return this.loanCompoundingDetails;
}

@Override
public boolean isEMIFixedSpecificToInstallment() {
return this.isEMIFixedSpecificToInstallment;
}

@Override
public void setEMIFixedSpecificToInstallmentTrue() {
this.isEMIFixedSpecificToInstallment = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,6 @@ public void validateForCreateAction(final JsonCommand jsonCommand, final Loan lo
"Loan rescheduling is not supported for multidisbursement loans");
}

if (loan.isInterestRecalculationEnabledForProduct()) {
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
RescheduleLoansApiConstants.resheduleWithInterestRecalculationNotSupportedErrorCode,
"Loan rescheduling is not supported for the loan product with interest recalculation enabled");
}
validateForOverdueCharges(dataValidatorBuilder, loan, installment);
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
Expand Down

0 comments on commit 44bc6e6

Please sign in to comment.