Merge pull request #1346 from fynmanoj/FINERACT-1151-fix
FINERACT-1151-fix
This commit is contained in:
+48
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-1
@@ -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;
|
||||||
|
|||||||
+10
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -58,4 +58,8 @@ public interface LoanScheduleModelPeriod {
|
|||||||
void setEMIFixedSpecificToInstallmentTrue();
|
void setEMIFixedSpecificToInstallmentTrue();
|
||||||
|
|
||||||
boolean isEMIFixedSpecificToInstallment();
|
boolean isEMIFixedSpecificToInstallment();
|
||||||
|
|
||||||
|
BigDecimal rescheduleInterestPortion();
|
||||||
|
|
||||||
|
void setRescheduleInterestPortion(BigDecimal rescheduleInterestPortion);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
@@ -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`;
|
||||||
Reference in New Issue
Block a user