support fees on specified dates for FLAT interest type loans and loan schedules.
This commit is contained in:
+1
-1
@@ -225,7 +225,7 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
|
||||
createCreditJournalEntryForLoanProduct(office, loanPortfolioAccount, loanId, transactionId, entryDate, principalAmount);
|
||||
}
|
||||
}
|
||||
if (principalAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
|
||||
totalDebitAmount = totalDebitAmount.add(interestAmount);
|
||||
|
||||
GLAccount interestAccount = getLinkedFinAccountForLoanProduct(loanProductId, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS);
|
||||
|
||||
+2
-1
@@ -118,7 +118,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
if (loanCharge.isFeeCharge() && loanCharge.isNotFullyPaid() && amountRemaining.isGreaterThanZero()) {
|
||||
amountRemaining = loanCharge.updatePaidAmountBy(amountRemaining);
|
||||
final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges);
|
||||
amountRemaining = unpaidCharge.updatePaidAmountBy(amountRemaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -91,14 +91,14 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
@@ -130,18 +130,18 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
interestPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
} else {
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
|
||||
// 1. pay of principal before interest.
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
feeChargesPortion = currentInstallment.payFeeChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion);
|
||||
|
||||
penaltyChargesPortion = currentInstallment.payPenaltyChargesComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion);
|
||||
|
||||
+6
-4
@@ -742,6 +742,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
final LocalDate submittedOn, final String externalId) {
|
||||
|
||||
final LoanScheduleData loanScheduleData = loanSchedule.generate();
|
||||
|
||||
// Have to set expectedDisbursementDate to avoid nullPointer so should be passed down to updateLoanSchedule method
|
||||
if (loanSchedule.getDisbursementDate() != null) {
|
||||
this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate();
|
||||
}
|
||||
|
||||
updateLoanSchedule(loanScheduleData);
|
||||
|
||||
LoanStatus from = null;
|
||||
@@ -760,10 +766,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.submittedOnDate = submittedOn.toDate();
|
||||
this.expectedMaturityDate = determineExpectedMaturityDate().toDate();
|
||||
|
||||
if (loanSchedule.getDisbursementDate() != null) {
|
||||
this.expectedDisbursedOnDate = loanSchedule.getDisbursementDate().toDate();
|
||||
}
|
||||
|
||||
if (loanSchedule.getRepaymentStartFromDate() != null) {
|
||||
this.expectedFirstRepaymentOnDate = loanSchedule.getRepaymentStartFromDate().toDate();
|
||||
}
|
||||
|
||||
+3
-9
@@ -12,13 +12,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
|
||||
public interface AmortizationLoanScheduleGenerator {
|
||||
|
||||
LoanScheduleData generate(
|
||||
ApplicationCurrency applicationCurrency,
|
||||
LoanProductRelatedDetail loanScheduleInfo,
|
||||
LocalDate disbursementDate,
|
||||
LocalDate interestCalculatedFrom,
|
||||
BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
List<LocalDate> scheduledDates,
|
||||
Set<LoanCharge> loanCharges);
|
||||
LoanScheduleData generate(ApplicationCurrency applicationCurrency, LoanProductRelatedDetail loanScheduleInfo,
|
||||
LocalDate disbursementDate, LocalDate interestCalculatedFrom, BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate, List<LocalDate> scheduledDates, Set<LoanCharge> loanCharges);
|
||||
}
|
||||
+153
-167
@@ -17,178 +17,164 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDat
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
|
||||
/**
|
||||
* Irregular payments for declining balance not supported
|
||||
*/
|
||||
public class EqualInstallmentsAmortizationLoanScheduleGenerator implements AmortizationLoanScheduleGenerator {
|
||||
|
||||
private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
|
||||
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
|
||||
private final PmtCalculator pmtCalculator = new PmtCalculator();
|
||||
|
||||
@Override
|
||||
public LoanScheduleData generate(
|
||||
final ApplicationCurrency currency,
|
||||
final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate,
|
||||
final LocalDate interestCalculatedFrom,
|
||||
final BigDecimal periodInterestRateForRepaymentPeriod,
|
||||
final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
final List<LocalDate> scheduledDates,
|
||||
final Set<LoanCharge> loanCharges) {
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
// determine 'total payment' for each repayment based on pmt function (and hence the total due overall)
|
||||
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
|
||||
final Money totalDuePerInstallment = this.pmtCalculator.calculatePaymentForOnePeriodFrom(loanScheduleInfo, periodInterestRateForRepaymentPeriod, monetaryCurrency);
|
||||
final Money totalRepaymentDueForLoanTerm = this.pmtCalculator.calculateTotalRepaymentFrom(loanScheduleInfo, periodInterestRateForRepaymentPeriod, monetaryCurrency);
|
||||
|
||||
Money totalInterestDue = totalRepaymentDueForLoanTerm.minus(loanScheduleInfo.getPrincipal());
|
||||
Money outstandingBalance = loanScheduleInfo.getPrincipal();
|
||||
Money principalDisbursed = loanScheduleInfo.getPrincipal();
|
||||
Money totalPrincipal = Money.zero(monetaryCurrency);
|
||||
Money totalInterest = Money.zero(monetaryCurrency);
|
||||
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator.calculateRepaymentPeriodAsAFractionOfDays(
|
||||
loanScheduleInfo.getRepaymentPeriodFrequencyType(),
|
||||
loanScheduleInfo.getRepayEvery(),
|
||||
interestCalculatedFrom,
|
||||
scheduledDates,
|
||||
idealDisbursementDateBasedOnFirstRepaymentDate);
|
||||
|
||||
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueAtDisbursement()) {
|
||||
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
|
||||
|
||||
// create entries of disbursement period on loan schedule
|
||||
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
|
||||
periods.add(disbursementPeriod);
|
||||
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
BigDecimal cumulativePrincipalDisbursed = loanScheduleInfo.getPrincipal().getAmount();
|
||||
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
|
||||
BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
|
||||
|
||||
LocalDate startDate = disbursementDate;
|
||||
int periodNumber = 1;
|
||||
for (LocalDate scheduledDueDate : scheduledDates) {
|
||||
private final PeriodicInterestRateCalculator periodicInterestRateCalculator = new PeriodicInterestRateCalculator();
|
||||
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
|
||||
private final PmtCalculator pmtCalculator = new PmtCalculator();
|
||||
|
||||
// number of days from startDate to this scheduledDate
|
||||
int daysInPeriod = Days.daysBetween(startDate, scheduledDueDate).getDays();
|
||||
|
||||
Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn(outstandingBalance, periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo);
|
||||
Money principalForInstallment = this.periodicInterestRateCalculator.calculatePrincipalOn(totalDuePerInstallment, interestForInstallment, loanScheduleInfo);
|
||||
|
||||
if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
|
||||
Money graceOnInterestForRepaymentPeriod = interestForInstallment;
|
||||
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
|
||||
totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod);
|
||||
interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction - Integer.valueOf(1).doubleValue();
|
||||
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25") && interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
|
||||
Money graceOnInterestForRepaymentPeriod = interestForInstallment.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
|
||||
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
|
||||
totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod);
|
||||
interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0");
|
||||
}
|
||||
|
||||
totalPrincipal = totalPrincipal.plus(principalForInstallment);
|
||||
totalInterest = totalInterest.plus(interestForInstallment);
|
||||
|
||||
if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
|
||||
Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
|
||||
if (principalDifference.isLessThanZero()) {
|
||||
principalForInstallment = principalForInstallment.plus(principalDifference.abs());
|
||||
} else if (principalDifference.isGreaterThanZero()) {
|
||||
principalForInstallment = principalForInstallment.minus(principalDifference.abs());
|
||||
}
|
||||
|
||||
final Money interestDifference = totalInterest.minus(totalInterestDue);
|
||||
if (interestDifference.isLessThanZero()) {
|
||||
interestForInstallment = interestForInstallment.plus(interestDifference.abs());
|
||||
} else if (interestDifference.isGreaterThanZero()) {
|
||||
interestForInstallment = interestForInstallment.minus(interestDifference.abs());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public LoanScheduleData generate(final ApplicationCurrency currency, final LoanProductRelatedDetail loanScheduleInfo,
|
||||
final LocalDate disbursementDate, final LocalDate interestCalculatedFrom,
|
||||
final BigDecimal periodInterestRateForRepaymentPeriod, final LocalDate idealDisbursementDateBasedOnFirstRepaymentDate,
|
||||
final List<LocalDate> scheduledDates, final Set<LoanCharge> loanCharges) {
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(principalForInstallment);
|
||||
|
||||
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
|
||||
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate,
|
||||
scheduledDueDate,
|
||||
principalForInstallment.getAmount(),
|
||||
outstandingBalance.getAmount(),
|
||||
interestForInstallment.getAmount(),
|
||||
feeChargesForInstallment.getAmount(),
|
||||
penaltyChargesForInstallment.getAmount(),
|
||||
totalInstallmentDue.getAmount());
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<LoanSchedulePeriodData>();
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += daysInPeriod;
|
||||
cumulativePrincipalDue = cumulativePrincipalDue.add(principalForInstallment.getAmount());
|
||||
cumulativeInterestExpected = cumulativeInterestExpected.add(interestForInstallment.getAmount());
|
||||
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
|
||||
startDate = scheduledDueDate;
|
||||
// determine 'total payment' for each repayment based on pmt function
|
||||
// (and hence the total due overall)
|
||||
final MonetaryCurrency monetaryCurrency = loanScheduleInfo.getPrincipal().getCurrency();
|
||||
final Money totalDuePerInstallment = this.pmtCalculator.calculatePaymentForOnePeriodFrom(loanScheduleInfo,
|
||||
periodInterestRateForRepaymentPeriod, monetaryCurrency);
|
||||
final Money totalRepaymentDueForLoanTerm = this.pmtCalculator.calculateTotalRepaymentFrom(loanScheduleInfo,
|
||||
periodInterestRateForRepaymentPeriod, monetaryCurrency);
|
||||
|
||||
periodNumber++;
|
||||
}
|
||||
|
||||
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
|
||||
|
||||
CurrencyData currencyData = new CurrencyData(
|
||||
currency.getCode(),
|
||||
currency.getName(),
|
||||
monetaryCurrency.getDigitsAfterDecimal(),
|
||||
currency.getDisplaySymbol(),
|
||||
currency.getNameCode());
|
||||
|
||||
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
Money totalInterestDue = totalRepaymentDueForLoanTerm.minus(loanScheduleInfo.getPrincipal());
|
||||
Money outstandingBalance = loanScheduleInfo.getPrincipal();
|
||||
Money principalDisbursed = loanScheduleInfo.getPrincipal();
|
||||
Money totalPrincipal = Money.zero(monetaryCurrency);
|
||||
Money totalInterest = Money.zero(monetaryCurrency);
|
||||
|
||||
private Money cumulativeFeeChargesDueWithin(
|
||||
final LocalDate periodStart,
|
||||
final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(
|
||||
final LocalDate periodStart,
|
||||
final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
||||
.calculateRepaymentPeriodAsAFractionOfDays(loanScheduleInfo.getRepaymentPeriodFrequencyType(),
|
||||
loanScheduleInfo.getRepayEvery(), interestCalculatedFrom, scheduledDates,
|
||||
idealDisbursementDateBasedOnFirstRepaymentDate);
|
||||
|
||||
BigDecimal chargesDueAtTimeOfDisbursement = BigDecimal.ZERO;
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueAtDisbursement()) {
|
||||
chargesDueAtTimeOfDisbursement = chargesDueAtTimeOfDisbursement.add(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
|
||||
|
||||
// create entries of disbursement period on loan schedule
|
||||
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate,
|
||||
principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
|
||||
periods.add(disbursementPeriod);
|
||||
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
BigDecimal cumulativePrincipalDisbursed = loanScheduleInfo.getPrincipal().getAmount();
|
||||
BigDecimal cumulativePrincipalDue = BigDecimal.ZERO;
|
||||
BigDecimal cumulativeInterestExpected = BigDecimal.ZERO;
|
||||
BigDecimal totalExpectedRepayment = chargesDueAtTimeOfDisbursement;
|
||||
|
||||
LocalDate startDate = disbursementDate;
|
||||
int periodNumber = 1;
|
||||
for (LocalDate scheduledDueDate : scheduledDates) {
|
||||
|
||||
// number of days from startDate to this scheduledDate
|
||||
int daysInPeriod = Days.daysBetween(startDate, scheduledDueDate).getDays();
|
||||
|
||||
Money interestForInstallment = this.periodicInterestRateCalculator.calculateInterestOn(outstandingBalance,
|
||||
periodInterestRateForRepaymentPeriod, daysInPeriod, loanScheduleInfo);
|
||||
Money principalForInstallment = this.periodicInterestRateCalculator.calculatePrincipalOn(totalDuePerInstallment,
|
||||
interestForInstallment, loanScheduleInfo);
|
||||
|
||||
if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
|
||||
Money graceOnInterestForRepaymentPeriod = interestForInstallment;
|
||||
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
|
||||
totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod);
|
||||
interestCalculationGraceOnRepaymentPeriodFraction = interestCalculationGraceOnRepaymentPeriodFraction
|
||||
- Integer.valueOf(1).doubleValue();
|
||||
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.valueOf("0.25")
|
||||
&& interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
|
||||
Money graceOnInterestForRepaymentPeriod = interestForInstallment
|
||||
.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
|
||||
interestForInstallment = interestForInstallment.minus(graceOnInterestForRepaymentPeriod);
|
||||
totalInterestDue = totalInterestDue.minus(graceOnInterestForRepaymentPeriod);
|
||||
interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf("0");
|
||||
}
|
||||
|
||||
totalPrincipal = totalPrincipal.plus(principalForInstallment);
|
||||
totalInterest = totalInterest.plus(interestForInstallment);
|
||||
|
||||
if (periodNumber == loanScheduleInfo.getNumberOfRepayments()) {
|
||||
Money principalDifference = totalPrincipal.minus(loanScheduleInfo.getPrincipal());
|
||||
if (principalDifference.isLessThanZero()) {
|
||||
principalForInstallment = principalForInstallment.plus(principalDifference.abs());
|
||||
} else if (principalDifference.isGreaterThanZero()) {
|
||||
principalForInstallment = principalForInstallment.minus(principalDifference.abs());
|
||||
}
|
||||
|
||||
final Money interestDifference = totalInterest.minus(totalInterestDue);
|
||||
if (interestDifference.isLessThanZero()) {
|
||||
interestForInstallment = interestForInstallment.plus(interestDifference.abs());
|
||||
} else if (interestDifference.isGreaterThanZero()) {
|
||||
interestForInstallment = interestForInstallment.minus(interestDifference.abs());
|
||||
}
|
||||
}
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(principalForInstallment);
|
||||
|
||||
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges,
|
||||
monetaryCurrency);
|
||||
final Money totalInstallmentDue = principalForInstallment.plus(interestForInstallment).plus(feeChargesForInstallment)
|
||||
.plus(penaltyChargesForInstallment);
|
||||
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount());
|
||||
|
||||
LoanSchedulePeriodData installment = LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, startDate, scheduledDueDate,
|
||||
principalForInstallment.getAmount(), outstandingBalance.getAmount(), interestForInstallment.getAmount(),
|
||||
feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount(), totalInstallmentDue.getAmount());
|
||||
|
||||
periods.add(installment);
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += daysInPeriod;
|
||||
cumulativePrincipalDue = cumulativePrincipalDue.add(principalForInstallment.getAmount());
|
||||
cumulativeInterestExpected = cumulativeInterestExpected.add(interestForInstallment.getAmount());
|
||||
totalExpectedRepayment = totalExpectedRepayment.add(totalInstallmentDue.getAmount());
|
||||
startDate = scheduledDueDate;
|
||||
|
||||
periodNumber++;
|
||||
}
|
||||
|
||||
final BigDecimal cumulativePrincipalOutstanding = cumulativePrincipalDisbursed.subtract(cumulativePrincipalDue);
|
||||
|
||||
CurrencyData currencyData = new CurrencyData(currency.getCode(), currency.getName(), monetaryCurrency.getDigitsAfterDecimal(),
|
||||
currency.getDisplaySymbol(), currency.getNameCode());
|
||||
|
||||
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
|
||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
}
|
||||
+35
-3
@@ -103,11 +103,15 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(principalPerInstallment);
|
||||
|
||||
Money feeChargesForInstallment = Money.zero(monetaryCurrency);
|
||||
Money penaltyChargesForInstallment = Money.zero(monetaryCurrency);
|
||||
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(startDate, scheduledDueDate, loanCharges, monetaryCurrency);
|
||||
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(startDate, scheduledDueDate, loanCharges,
|
||||
monetaryCurrency);
|
||||
|
||||
Money totalInstallmentDue = principalPerInstallment.plus(interestPerInstallment).plus(feeChargesForInstallment)
|
||||
final Money totalInstallmentDue = principalPerInstallment //
|
||||
.plus(interestPerInstallment) //
|
||||
.plus(feeChargesForInstallment) //
|
||||
.plus(penaltyChargesForInstallment);
|
||||
|
||||
cumulativeChargesToDate = cumulativeChargesToDate.add(feeChargesForInstallment.getAmount()).add(
|
||||
penaltyChargesForInstallment.getAmount());
|
||||
|
||||
@@ -135,4 +139,32 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
return new LoanScheduleData(currencyData, periods, loanTermInDays, cumulativePrincipalDisbursed, cumulativePrincipalDue,
|
||||
cumulativePrincipalOutstanding, cumulativeInterestExpected, cumulativeChargesToDate, totalExpectedRepayment);
|
||||
}
|
||||
|
||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionBetween(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return cumulative;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user