FINERACT-1981: Generate loan schedule as dto
This commit is contained in:
+6
-6
@@ -828,7 +828,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
Money amortizableAmount = disbursementTransaction.getAmount(transactionCtx.getCurrency()).minus(downPaymentAmount);
|
||||
|
||||
if (amortizableAmount.isGreaterThanZero()) {
|
||||
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, mc.getRoundingMode());
|
||||
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, MoneyHelper.getMathContext());
|
||||
MoneyHolder moneyHolder = new MoneyHolder(amortizableAmount);
|
||||
|
||||
candidateRepaymentInstallments.forEach(i -> {
|
||||
@@ -1194,7 +1194,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
case IN_ADVANCE -> {
|
||||
int numberOfInstallments = inAdvanceInstallments.size();
|
||||
if (numberOfInstallments > 0) {
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getRoundingMode());
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getMathContext());
|
||||
Money balanceAdjustment = transactionAmountUnprocessed.minus(evenPortion.multipliedBy(numberOfInstallments));
|
||||
for (LoanRepaymentScheduleInstallment inAdvanceInstallment : inAdvanceInstallments) {
|
||||
Set<LoanCharge> inAdvanceInstallmentCharges = getLoanChargesOfInstallment(charges, inAdvanceInstallment,
|
||||
@@ -1262,7 +1262,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
int numberOfInstallments = currentInstallments.size();
|
||||
refundedPortion = zero;
|
||||
if (numberOfInstallments > 0) {
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getRoundingMode());
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getMathContext());
|
||||
Money balanceAdjustment = transactionAmountUnprocessed.minus(evenPortion.multipliedBy(numberOfInstallments));
|
||||
for (LoanRepaymentScheduleInstallment internalCurrentInstallment : currentInstallments) {
|
||||
currentInstallment = internalCurrentInstallment;
|
||||
@@ -1467,7 +1467,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (numberOfInstallments > 0) {
|
||||
// This will be the same amount as transactionAmountUnprocessed in case of the future
|
||||
// installment allocation is NEXT_INSTALLMENT or LAST_INSTALLMENT
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getRoundingMode());
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getMathContext());
|
||||
// Adjustment might be needed due to the divide operation and the rounding mode
|
||||
Money balanceAdjustment = transactionAmountUnprocessed.minus(evenPortion.multipliedBy(numberOfInstallments));
|
||||
for (LoanRepaymentScheduleInstallment inAdvanceInstallment : inAdvanceInstallments) {
|
||||
@@ -1645,7 +1645,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (numberOfInstallments > 0) {
|
||||
// This will be the same amount as transactionAmountUnprocessed in case of the future
|
||||
// installment allocation is NEXT_INSTALLMENT or LAST_INSTALLMENT
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getRoundingMode());
|
||||
Money evenPortion = transactionAmountUnprocessed.dividedBy(numberOfInstallments, MoneyHelper.getMathContext());
|
||||
// Adjustment might be needed due to the divide operation and the rounding mode
|
||||
Money balanceAdjustment = transactionAmountUnprocessed.minus(evenPortion.multipliedBy(numberOfInstallments));
|
||||
for (LoanRepaymentScheduleInstallment internalCurrentInstallment : currentInstallments) {
|
||||
@@ -1723,7 +1723,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
Money adjustCalculatedPrincipal = Money.zero(currency);
|
||||
if (outstandingPrincipalBalance.get().isGreaterThanZero()) {
|
||||
calculatedPrincipal = outstandingPrincipalBalance.get()
|
||||
.dividedBy(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments(), MoneyHelper.getRoundingMode());
|
||||
.dividedBy(loanTransaction.getLoanReAgeParameter().getNumberOfInstallments(), MoneyHelper.getMathContext());
|
||||
Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
|
||||
if (installmentAmountInMultiplesOf != null) {
|
||||
calculatedPrincipal = Money.roundToMultiplesOf(calculatedPrincipal, installmentAmountInMultiplesOf);
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelDisbursementPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;
|
||||
|
||||
@Data
|
||||
public class LoanSchedulePlan {
|
||||
|
||||
private final List<LoanSchedulePlanPeriod> periods;
|
||||
private final CurrencyData currency;
|
||||
private final int loanTermInDays;
|
||||
private final BigDecimal totalDisbursedAmount;
|
||||
private final BigDecimal totalPrincipalAmount;
|
||||
private final BigDecimal totalInterestAmount;
|
||||
private final BigDecimal totalFeeAmount;
|
||||
private final BigDecimal totalPenaltyAmount;
|
||||
private final BigDecimal totalRepaymentAmount;
|
||||
private final BigDecimal totalOutstandingAmount;
|
||||
|
||||
public static LoanSchedulePlan from(LoanScheduleModel model) {
|
||||
List<LoanSchedulePlanPeriod> periods = new ArrayList<>();
|
||||
|
||||
model.getPeriods().forEach(periodModel -> {
|
||||
LoanSchedulePlanPeriod periodPlan = null;
|
||||
if (periodModel instanceof LoanScheduleModelDisbursementPeriod disbursementPeriod) {
|
||||
periodPlan = new LoanSchedulePlanDisbursementPeriod(disbursementPeriod.getDisbursementDate(), //
|
||||
disbursementPeriod.getDisbursementDate(), //
|
||||
disbursementPeriod.getPrincipalDisbursed().getAmount(), //
|
||||
disbursementPeriod.getPrincipalDisbursed().getAmount());//
|
||||
} else if (periodModel instanceof LoanScheduleModelDownPaymentPeriod downPaymentPeriod) {
|
||||
periodPlan = new LoanSchedulePlanDownPaymentPeriod(downPaymentPeriod.getPeriodNumber(), //
|
||||
downPaymentPeriod.getPeriodDate(), //
|
||||
downPaymentPeriod.getPeriodDate(), //
|
||||
downPaymentPeriod.getPrincipalDue().getAmount(), //
|
||||
downPaymentPeriod.getPrincipalDue().getAmount(), //
|
||||
downPaymentPeriod.getOutstandingLoanBalance().getAmount());//
|
||||
} else if (periodModel instanceof LoanScheduleModelRepaymentPeriod repaymentPeriod) {
|
||||
periodPlan = new LoanSchedulePlanRepaymentPeriod(repaymentPeriod.getPeriodNumber(), //
|
||||
repaymentPeriod.getFromDate(), //
|
||||
repaymentPeriod.getDueDate(), //
|
||||
repaymentPeriod.getPrincipalDue().getAmount(), //
|
||||
repaymentPeriod.getInterestDue().getAmount(), //
|
||||
repaymentPeriod.getFeeChargesDue().getAmount(), //
|
||||
repaymentPeriod.getPenaltyChargesDue().getAmount(), //
|
||||
repaymentPeriod.getTotalDue().getAmount(), //
|
||||
repaymentPeriod.getOutstandingLoanBalance().getAmount());//
|
||||
}
|
||||
if (periodPlan != null) {
|
||||
periods.add(periodPlan);
|
||||
}
|
||||
});
|
||||
|
||||
return new LoanSchedulePlan(periods, //
|
||||
model.getApplicationCurrency().toData(), //
|
||||
model.getLoanTermInDays(), //
|
||||
model.getTotalPrincipalDisbursed().getAmount(), //
|
||||
model.getTotalPrincipalExpected(), //
|
||||
model.getTotalInterestCharged(), //
|
||||
model.getTotalFeeChargesCharged(), //
|
||||
model.getTotalPenaltyChargesCharged(), //
|
||||
model.getTotalRepaymentExpected(), //
|
||||
model.getTotalOutstanding()//
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class LoanSchedulePlanDisbursementPeriod implements LoanSchedulePlanPeriod {
|
||||
|
||||
private final LocalDate periodFromDate;
|
||||
private final LocalDate periodDueDate;
|
||||
private final BigDecimal principalAmount;
|
||||
private final BigDecimal outstandingLoanBalance;
|
||||
|
||||
@Override
|
||||
public Integer periodNumber() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodFromDate() {
|
||||
return periodFromDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodDueDate() {
|
||||
return periodDueDate;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class LoanSchedulePlanDownPaymentPeriod implements LoanSchedulePlanPeriod {
|
||||
|
||||
private final int periodNumber;
|
||||
private final LocalDate periodFromDate;
|
||||
private final LocalDate periodDueDate;
|
||||
private final BigDecimal principalAmount;
|
||||
private final BigDecimal totalDueAmount;
|
||||
private final BigDecimal outstandingLoanBalance;
|
||||
|
||||
@Override
|
||||
public Integer periodNumber() {
|
||||
return periodNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodFromDate() {
|
||||
return periodFromDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodDueDate() {
|
||||
return periodDueDate;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface LoanSchedulePlanPeriod {
|
||||
|
||||
Integer periodNumber();
|
||||
|
||||
LocalDate periodFromDate();
|
||||
|
||||
LocalDate periodDueDate();
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public final class LoanSchedulePlanRepaymentPeriod implements LoanSchedulePlanPeriod {
|
||||
|
||||
private final int periodNumber;
|
||||
private final LocalDate periodFromDate;
|
||||
private final LocalDate periodDueDate;
|
||||
private final BigDecimal principalAmount;
|
||||
private final BigDecimal interestAmount;
|
||||
private final BigDecimal feeAmount;
|
||||
private final BigDecimal penaltyAmount;
|
||||
private final BigDecimal totalDueAmount;
|
||||
private final BigDecimal outstandingLoanBalance;
|
||||
|
||||
@Override
|
||||
public Integer periodNumber() {
|
||||
return periodNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodFromDate() {
|
||||
return periodFromDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate periodDueDate() {
|
||||
return periodDueDate;
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -45,6 +45,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleModelDownPaymentPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlan;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.PayableDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
|
||||
@@ -165,11 +166,10 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
|
||||
}
|
||||
|
||||
public LoanScheduleModel generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) {
|
||||
public LoanSchedulePlan generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) {
|
||||
|
||||
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(modelData, mc);
|
||||
|
||||
return generate(mc, loanApplicationTerms, null, null);
|
||||
return LoanSchedulePlan.from(generate(mc, loanApplicationTerms, null, null));
|
||||
}
|
||||
|
||||
private void prepareDisbursementsOnLoanApplicationTerms(final LoanApplicationTerms loanApplicationTerms) {
|
||||
@@ -221,22 +221,22 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
Money downPaymentAmount = Money.zero(loanApplicationTerms.getCurrency(), mc);
|
||||
if (loanApplicationTerms.isDownPaymentEnabled()) {
|
||||
downPaymentAmount = Money.of(loanApplicationTerms.getCurrency(), MathUtil.percentageOf(disbursedAmount.getAmount(),
|
||||
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
|
||||
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), mc), mc);
|
||||
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
|
||||
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount,
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
}
|
||||
|
||||
LoanScheduleModelDownPaymentPeriod downPaymentPeriod = LoanScheduleModelDownPaymentPeriod.downPayment(
|
||||
scheduleParams.getInstalmentNumber(), disbursementDate, downPaymentAmount,
|
||||
outstandingBalance.plus(disbursedAmount).minus(downPaymentAmount));
|
||||
outstandingBalance.plus(disbursedAmount, mc).minus(downPaymentAmount, mc));
|
||||
periods.add(downPaymentPeriod);
|
||||
|
||||
scheduleParams.addTotalRepaymentExpected(downPaymentAmount);
|
||||
scheduleParams.incrementInstalmentNumber();
|
||||
}
|
||||
|
||||
final Money disbursementRemainingBalance = disbursedAmount.minus(downPaymentAmount);
|
||||
final Money disbursementRemainingBalance = disbursedAmount.minus(downPaymentAmount, mc);
|
||||
scheduleParams.addPrincipalToBeScheduled(disbursementRemainingBalance);
|
||||
emiCalculator.addDisbursement(interestScheduleModel, disbursementDate, disbursementRemainingBalance);
|
||||
}
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
.isGreaterThan(Money.of(originalEmi.getCurrency(), BigDecimal.valueOf(lowerHalfOfRelatedPeriods), mc));
|
||||
|
||||
if (shouldBeAdjusted) {
|
||||
Money adjustment = emiDifference.dividedBy(numberOfRelatedPeriods, mc.getRoundingMode(), mc);
|
||||
Money adjustment = emiDifference.dividedBy(numberOfRelatedPeriods, mc);
|
||||
Money adjustedEqualMonthlyInstallmentValue = applyInstallmentAmountInMultiplesOf(scheduleModel,
|
||||
originalEmi.plus(adjustment, mc));
|
||||
if (adjustedEqualMonthlyInstallmentValue.isEqualTo(originalEmi)) {
|
||||
|
||||
Reference in New Issue
Block a user