Merge pull request #1346 from fynmanoj/FINERACT-1151-fix

FINERACT-1151-fix
This commit is contained in:
Avik Ganguly
2020-09-25 13:58:09 +05:30
committed by GitHub
7 changed files with 151 additions and 1 deletions
@@ -359,4 +359,52 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
} }
@Test
public void testCreateLoanRescheduleRequestWithMultpleInterestAppropriation() {
// create all required entities
this.createRequiredEntities();
this.createAndApproveLoanRescheduleRequestForInterestAppropriation();
this.createAndApproveLoanRescheduleRequestForSecondInterestAppropriation();
}
/**
* create new loan reschedule request
**/
private void createAndApproveLoanRescheduleRequestForSecondInterestAppropriation() {
LOG.info(
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR INTEREST APPROPRIATTION-------------------------------------");
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnPrincipal(null).updateGraceOnInterest(null)
.updateExtraTerms(null).updateRescheduleFromDate("04 December 2015").updateAdjustedDueDate("04 June 2016")
.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(7);
Float totalDueForPeriod = (Float) period.get("totalDueForPeriod");
final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
assertEquals(12187, totalDueForPeriod.intValue(), "EXPECTED REPAYMENT in Second Reschedule is NOK");
assertEquals(130750, totalExpectedRepayment.intValue(), "TOTAL EXPECTED in Second Reschedule REPAYMENT is NOK");
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
}
} }
@@ -82,6 +82,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
@Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true) @Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestAccrued; private BigDecimal interestAccrued;
@Column(name = "reschedule_interest_portion", scale = 6, precision = 19, nullable = true)
private BigDecimal rescheduleInterestPortion;
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true) @Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal feeChargesCharged; private BigDecimal feeChargesCharged;
@@ -139,6 +142,24 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
this.obligationsMet = false; this.obligationsMet = false;
} }
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
final BigDecimal penaltyCharges, final boolean recalculatedInterestComponent,
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails, final BigDecimal rescheduleInterestPortion) {
this.loan = loan;
this.installmentNumber = installmentNumber;
this.fromDate = fromDate.toDateTimeAtStartOfDay().toDate();
this.dueDate = dueDate.toDateTimeAtStartOfDay().toDate();
this.principal = defaultToNullIfZero(principal);
this.interestCharged = defaultToNullIfZero(interest);
this.feeChargesCharged = defaultToNullIfZero(feeCharges);
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
this.obligationsMet = false;
this.recalculatedInterestComponent = recalculatedInterestComponent;
this.loanCompoundingDetails = compoundingDetails;
this.rescheduleInterestPortion = rescheduleInterestPortion;
}
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate, public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges, final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
final BigDecimal penaltyCharges, final boolean recalculatedInterestComponent, final BigDecimal penaltyCharges, final boolean recalculatedInterestComponent,
@@ -807,4 +828,13 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
return getPenaltyChargesPaid(currency).plus(getFeeChargesPaid(currency)).plus(getInterestPaid(currency)) return getPenaltyChargesPaid(currency).plus(getFeeChargesPaid(currency)).plus(getInterestPaid(currency))
.plus(getPrincipalCompleted(currency)); .plus(getPrincipalCompleted(currency));
} }
public BigDecimal getRescheduleInterestPortion() {
return rescheduleInterestPortion;
}
public void setRescheduleInterestPortion(BigDecimal rescheduleInterestPortion) {
this.rescheduleInterestPortion = rescheduleInterestPortion;
}
} }
@@ -397,6 +397,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
for (LoanScheduleModelPeriod installment : (List<LoanScheduleModelPeriod>) periods) { for (LoanScheduleModelPeriod installment : (List<LoanScheduleModelPeriod>) periods) {
if (!installment.isEMIFixedSpecificToInstallment()) { if (!installment.isEMIFixedSpecificToInstallment()) {
installment.addInterestAmount(interestFraction); installment.addInterestAmount(interestFraction);
installment.setRescheduleInterestPortion(interestFraction.getAmount());
interestTobeApproppriated = interestTobeApproppriated.minus(interestFraction); interestTobeApproppriated = interestTobeApproppriated.minus(interestFraction);
} }
} }
@@ -404,6 +405,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
if (interestTobeApproppriated.isGreaterThanZero()) { if (interestTobeApproppriated.isGreaterThanZero()) {
lastInstallment.addInterestAmount(interestTobeApproppriated); lastInstallment.addInterestAmount(interestTobeApproppriated);
BigDecimal rescheduleInterestPortion = BigDecimal.ZERO;
if (lastInstallment.rescheduleInterestPortion() != null) {
rescheduleInterestPortion = lastInstallment.rescheduleInterestPortion();
}
lastInstallment.setRescheduleInterestPortion(rescheduleInterestPortion.add(interestTobeApproppriated.getAmount()));
} }
scheduleParams.addTotalRepaymentExpected(loanApplicationTerms.getInterestTobeApproppriated()); scheduleParams.addTotalRepaymentExpected(loanApplicationTerms.getInterestTobeApproppriated());
scheduleParams.addTotalCumulativeInterest(loanApplicationTerms.getInterestTobeApproppriated()); scheduleParams.addTotalCumulativeInterest(loanApplicationTerms.getInterestTobeApproppriated());
@@ -2385,6 +2391,24 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation); loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation);
periods.clear(); periods.clear();
} }
BigDecimal rescheuleInterestPortionTobeRetained = BigDecimal.ZERO;
for (LoanRepaymentScheduleInstallment retainedInstallment : retainedInstallments) {
if (retainedInstallment.getRescheduleInterestPortion() != null) {
rescheuleInterestPortionTobeRetained = rescheuleInterestPortionTobeRetained
.add(retainedInstallment.getRescheduleInterestPortion());
}
}
BigDecimal rescheuleInterestPortionTotal = BigDecimal.ZERO;
for (LoanRepaymentScheduleInstallment inst : loan.getRepaymentScheduleInstallments()) {
if (inst.getRescheduleInterestPortion() != null) {
rescheuleInterestPortionTotal = rescheuleInterestPortionTotal.add(inst.getRescheduleInterestPortion());
}
}
BigDecimal rescheuleInterestPortionTobeAppropriated = rescheuleInterestPortionTotal.subtract(rescheuleInterestPortionTobeRetained);
loanApplicationTerms.setInterestTobeApproppriated(Money.of(loan.getCurrency(), rescheuleInterestPortionTobeAppropriated));
LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), holidayDetailDTO, loanScheduleParams); LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), holidayDetailDTO, loanScheduleParams);
for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) { for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
@@ -2589,7 +2613,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
scheduledLoanInstallment.periodFromDate(), scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.periodFromDate(), scheduledLoanInstallment.periodDueDate(),
scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.principalDue(), scheduledLoanInstallment.interestDue(),
scheduledLoanInstallment.feeChargesDue(), scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.feeChargesDue(), scheduledLoanInstallment.penaltyChargesDue(),
scheduledLoanInstallment.isRecalculatedInterestComponent(), scheduledLoanInstallment.getLoanCompoundingDetails()); scheduledLoanInstallment.isRecalculatedInterestComponent(), scheduledLoanInstallment.getLoanCompoundingDetails(),
scheduledLoanInstallment.rescheduleInterestPortion());
installments.add(installment); installments.add(installment);
} }
return installment; return installment;
@@ -138,4 +138,14 @@ public final class LoanScheduleModelDisbursementPeriod implements LoanScheduleMo
public boolean isEMIFixedSpecificToInstallment() { public boolean isEMIFixedSpecificToInstallment() {
return isEMIFixedSpecificToInstallment; return isEMIFixedSpecificToInstallment;
} }
@Override
public BigDecimal rescheduleInterestPortion() {
return null;
}
@Override
public void setRescheduleInterestPortion(BigDecimal rescheduleInterestPortion) {
return;
}
} }
@@ -58,4 +58,8 @@ public interface LoanScheduleModelPeriod {
void setEMIFixedSpecificToInstallmentTrue(); void setEMIFixedSpecificToInstallmentTrue();
boolean isEMIFixedSpecificToInstallment(); boolean isEMIFixedSpecificToInstallment();
BigDecimal rescheduleInterestPortion();
void setRescheduleInterestPortion(BigDecimal rescheduleInterestPortion);
} }
@@ -43,6 +43,7 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
private final boolean recalculatedInterestComponent; private final boolean recalculatedInterestComponent;
private final Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>(); private final Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>();
private boolean isEMIFixedSpecificToInstallment = false; private boolean isEMIFixedSpecificToInstallment = false;
BigDecimal rescheduleInterestPortion;
public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate, public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate,
final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue, final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
@@ -172,4 +173,14 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
public void setEMIFixedSpecificToInstallmentTrue() { public void setEMIFixedSpecificToInstallmentTrue() {
this.isEMIFixedSpecificToInstallment = true; this.isEMIFixedSpecificToInstallment = true;
} }
@Override
public void setRescheduleInterestPortion(BigDecimal rescheduleInterestPortion) {
this.rescheduleInterestPortion = rescheduleInterestPortion;
}
@Override
public BigDecimal rescheduleInterestPortion() {
return this.rescheduleInterestPortion;
}
} }
@@ -0,0 +1,22 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
--
alter table m_loan_repayment_schedule
add column `reschedule_interest_portion` decimal(19,6) DEFAULT NULL after `accrual_interest_derived`;