MIFOSX-1421 : fixed interest recalculation with RBI strategy
This commit is contained in:
+47
-13
@@ -1759,8 +1759,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now())) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||
updateLoanSummaryDerivedFields();
|
||||
@@ -2295,6 +2296,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
LoanStatus statusEnum = null;
|
||||
|
||||
LocalDate recalculateFrom = loanTransaction.getTransactionDate();
|
||||
if (adjustedTransaction != null && adjustedTransaction.getTransactionDate().isBefore(recalculateFrom)) {
|
||||
recalculateFrom = adjustedTransaction.getTransactionDate();
|
||||
}
|
||||
|
||||
if (loanTransaction.isRecoveryRepayment()) {
|
||||
statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, LoanStatus.fromInt(this.loanStatus));
|
||||
} else {
|
||||
@@ -2382,7 +2388,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
if (reprocess) {
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
@@ -2395,7 +2401,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
* of the transactions. for this need to save the reversed
|
||||
* transactions first and then new transactions.
|
||||
*/
|
||||
// this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
@@ -2408,6 +2414,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
if (changedTransactionDetail != null) {
|
||||
this.loanTransactions.removeAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
}
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
@@ -2434,6 +2443,18 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return repaymentsOrWaivers;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retreiveListOfTransactionsPostDisbursementExcludeAccruals() {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (!transaction.isDisbursement() && transaction.isNotReversed() && !transaction.isAccrual()) {
|
||||
repaymentsOrWaivers.add(transaction);
|
||||
}
|
||||
}
|
||||
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
|
||||
Collections.sort(repaymentsOrWaivers, transactionComparator);
|
||||
return repaymentsOrWaivers;
|
||||
}
|
||||
|
||||
private void doPostLoanTransactionChecks(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
if (isOverPaid()) {
|
||||
@@ -2651,8 +2672,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||
@@ -2797,8 +2819,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
@@ -3897,8 +3920,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
workingDays);
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
@@ -3976,17 +4000,24 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return maturityDate;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail recalculateSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
LocalDate recalculateFrom = null;
|
||||
List<LoanTransaction> loanTransactions = this.retreiveListOfTransactionsPostDisbursementExcludeAccruals();
|
||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||
if (recalculateFrom == null || loanTransaction.getTransactionDate().isAfter(recalculateFrom)) {
|
||||
recalculateFrom = loanTransaction.getTransactionDate();
|
||||
}
|
||||
}
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays,
|
||||
calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
@@ -4002,10 +4033,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
* the transactions. for this need to save the reversed transactions
|
||||
* first and then new transactions.
|
||||
*/
|
||||
// this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
this.loanTransactions.addAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
this.loanTransactions.removeAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
return changedTransactionDetail;
|
||||
|
||||
}
|
||||
@@ -4013,7 +4045,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
private void regenerateRepaymentScheduleWithInterestRecalculation(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, LocalDate recalculateFrom) {
|
||||
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa) { return; }
|
||||
|
||||
@@ -4056,8 +4088,10 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mc, applicationCurrency,
|
||||
loanApplicationTerms, charges(), isHolidayEnabled, holidays, workingDays, retreiveListOfTransactionsPostDisbursement(),
|
||||
loanRepaymentScheduleTransactionProcessor);
|
||||
loanApplicationTerms, charges(), isHolidayEnabled, holidays, workingDays,
|
||||
retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
|
||||
this.repaymentScheduleInstallments, recalculateFrom);
|
||||
if (loanSchedule == null) { return; }
|
||||
|
||||
updateLoanSchedule(loanSchedule);
|
||||
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate();
|
||||
|
||||
+6
-17
@@ -104,11 +104,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
final List<LoanRepaymentScheduleInstallment> processInstallments = new ArrayList<>(1);
|
||||
processInstallments.add(chargePaidDetail.getInstallment());
|
||||
Money processAmt = chargePaidDetail.getAmount();
|
||||
if(processAmt.isGreaterThan(unprocessed)){
|
||||
if (processAmt.isGreaterThan(unprocessed)) {
|
||||
processAmt = unprocessed;
|
||||
}
|
||||
unprocessed = handleTransactionAndCharges(loanTransaction, currency, processInstallments,
|
||||
transferCharges, processAmt, chargePaidDetail.isFeeCharge());
|
||||
unprocessed = handleTransactionAndCharges(loanTransaction, currency, processInstallments, transferCharges, processAmt,
|
||||
chargePaidDetail.isFeeCharge());
|
||||
if (!unprocessed.isGreaterThanZero()) {
|
||||
break;
|
||||
}
|
||||
@@ -437,19 +437,6 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
// empty implementation by default.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<LocalDate, Money> handleRecalculation(final LocalDate disbursementDate,
|
||||
final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentinstallment,
|
||||
final Map<LocalDate, LocalDate> recalculationDates) {
|
||||
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
currentInstallment.resetDerivedComponents();
|
||||
currentInstallment.updateDerivedFields(currency, disbursementDate);
|
||||
}
|
||||
return handleRepaymentSchedule(transactionsPostDisbursement, currency, installments, currentinstallment, recalculationDates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<LocalDate, Money> handleRepaymentSchedule(final List<LoanTransaction> transactionsPostDisbursement,
|
||||
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
@@ -458,7 +445,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
Money currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
||||
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||
Money amountToProcess = null;
|
||||
Money unProcessed = processTransaction(loanTransaction, currency, installments, amountToProcess);
|
||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||
newLoanTransaction.resetDerivedComponents();
|
||||
Money unProcessed = processTransaction(newLoanTransaction, currency, installments, amountToProcess);
|
||||
if (loanTransaction.getTransactionDate().isAfter(installment.getFromDate())
|
||||
&& recalculationDates.get(loanTransaction.getTransactionDate()).isBefore(installment.getDueDate())) {
|
||||
Money earlyPayment = currentInstallmentOutstanding.minus(installment.getTotalOutstanding(currency));
|
||||
|
||||
+2
-4
@@ -28,10 +28,8 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
||||
void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency,
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||
|
||||
Map<LocalDate, Money> handleRecalculation(LocalDate disbursementDate, List<LoanTransaction> transactionsPostDisbursement,
|
||||
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentinstallment, Map<LocalDate, LocalDate> recalculationDates);
|
||||
|
||||
Map<LocalDate, Money> handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment, Map<LocalDate, LocalDate> recalculationDates);
|
||||
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment,
|
||||
Map<LocalDate, LocalDate> recalculationDates);
|
||||
|
||||
}
|
||||
+132
-45
@@ -212,8 +212,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
if (diffAmt != null && !diffAmt.isEmpty()) {
|
||||
Money interestDueToLatePayment = totalCumulativeInterest.zero();
|
||||
Money interestReducedDueToEarlyPayment = totalCumulativeInterest.zero();
|
||||
BigDecimal interestDueToLatePayment = BigDecimal.ZERO;
|
||||
BigDecimal interestReducedDueToEarlyPayment = BigDecimal.ZERO;
|
||||
for (RecalculationDetail detail : diffAmt) {
|
||||
if (!detail.isLatePayment() && detail.getStartDate().isAfter(periodStartDate)
|
||||
&& !detail.getStartDate().isAfter(scheduledDueDate)) {
|
||||
@@ -231,7 +231,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
|
||||
}
|
||||
}
|
||||
interestReducedDueToEarlyPayment = interestReducedDueToEarlyPayment.plus(loanApplicationTerms.interestRateFor(
|
||||
interestReducedDueToEarlyPayment = interestReducedDueToEarlyPayment.add(loanApplicationTerms.interestRateFor(
|
||||
this.paymentPeriodsInOneYearCalculator, mc, detail.getAmount(), detail.getStartDate(), scheduledDueDate));
|
||||
|
||||
} else if (detail.isLatePayment() && detail.isOverlapping(periodStartDate, scheduledDueDate)
|
||||
@@ -247,7 +247,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
if (toDate.isAfter(LocalDate.now())) {
|
||||
toDate = LocalDate.now();
|
||||
}
|
||||
interestDueToLatePayment = interestDueToLatePayment.plus(loanApplicationTerms.interestRateFor(
|
||||
interestDueToLatePayment = interestDueToLatePayment.add(loanApplicationTerms.interestRateFor(
|
||||
this.paymentPeriodsInOneYearCalculator, mc, detail.getAmount(), fromDate, toDate));
|
||||
}
|
||||
}
|
||||
@@ -332,17 +332,19 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
LocalDate fromDate = detail.getStartDate();
|
||||
LocalDate toDate = detail.getToDate();
|
||||
if (!toDate.isAfter(LocalDate.now())) {
|
||||
Money interestDueToLatePayment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc,
|
||||
BigDecimal interestForLatePayment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc,
|
||||
detail.getAmount(), fromDate, toDate);
|
||||
Money interestDueToLatePayment = Money.of(detail.getAmount().getCurrency(), interestForLatePayment);
|
||||
if (interestDueToLatePayment.isGreaterThanZero()) {
|
||||
totalInterestCharged = totalInterestCharged.add(interestDueToLatePayment.getAmount());
|
||||
totalRepaymentExpected = totalRepaymentExpected.add(interestDueToLatePayment.getAmount());
|
||||
|
||||
totalInterestCharged = totalInterestCharged.add(interestDueToLatePayment.getAmount());
|
||||
totalRepaymentExpected = totalRepaymentExpected.add(interestDueToLatePayment.getAmount());
|
||||
|
||||
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, toDate,
|
||||
interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment,
|
||||
interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment);
|
||||
periods.add(installment);
|
||||
periodNumber++;
|
||||
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate,
|
||||
toDate, interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment,
|
||||
interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment);
|
||||
periods.add(installment);
|
||||
periodNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,47 +522,95 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom) {
|
||||
|
||||
int installmentNumber = 0;
|
||||
final List<RecalculationDetail> recalculationDetails = new ArrayList<>();
|
||||
LoanScheduleModel loanScheduleModel = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
LoanScheduleModel loanScheduleModel = null;
|
||||
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
||||
List<LoanRepaymentScheduleInstallment> removeInstallments = new ArrayList<>();
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (!loanApplicationTerms.isPrincipalGraceApplicableForThisPeriod(installment.getInstallmentNumber())
|
||||
&& installment.getPrincipal(loanApplicationTerms.getCurrency()).isZero()) {
|
||||
removeInstallments.add(installment);
|
||||
}
|
||||
}
|
||||
installments.removeAll(removeInstallments);
|
||||
if (previousSchedule == null) {
|
||||
loanScheduleModel = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||
}
|
||||
if (recalculateFrom == null) {
|
||||
recalculateFrom = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
}
|
||||
while (!recalculateFrom.isAfter(LocalDate.now())) {
|
||||
int installmentNumber = findLastProcessedInstallmentNumber(installments, recalculateFrom);
|
||||
List<LoanTransaction> processTransactions = processTransactions(transactions, recalculateFrom);
|
||||
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled,
|
||||
holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
||||
processTransactions, installmentNumber);
|
||||
|
||||
while (installmentNumber < loanScheduleModel.getPeriods().size() - 1) {
|
||||
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, transactions, loanRepaymentScheduleTransactionProcessor, installmentNumber,
|
||||
loanScheduleModel, recalculationDetails);
|
||||
installmentNumber = recalculatedSchedule.getInstallmentNumber();
|
||||
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
||||
recalculateFrom = getNextRecalculateFromDate(transactions, recalculateFrom);
|
||||
}
|
||||
return loanScheduleModel;
|
||||
|
||||
}
|
||||
|
||||
private LocalDate getNextRecalculateFromDate(List<LoanTransaction> processTransactions, LocalDate preCalculationDate) {
|
||||
LocalDate recalculateFrom = null;
|
||||
for (LoanTransaction loanTransaction : processTransactions) {
|
||||
if (preCalculationDate.isBefore(loanTransaction.getTransactionDate())
|
||||
&& (recalculateFrom == null || recalculateFrom.isAfter(loanTransaction.getTransactionDate()))) {
|
||||
recalculateFrom = loanTransaction.getTransactionDate();
|
||||
}
|
||||
}
|
||||
if (recalculateFrom == null) {
|
||||
recalculateFrom = LocalDate.now().plusDays(1);
|
||||
}
|
||||
return recalculateFrom;
|
||||
}
|
||||
|
||||
private LoanScheduleModel recalculateInstallment(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||
List<LoanRepaymentScheduleInstallment> installments, List<LoanTransaction> processTransactions, final int skipRecalculation) {
|
||||
int processInstallmentsFrom = 0;
|
||||
Integer numberOfInstallments = previousSchedule.size() - 1;
|
||||
final List<RecalculationDetail> recalculationDetails = new ArrayList<>();
|
||||
while (processInstallmentsFrom < numberOfInstallments) {
|
||||
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, processTransactions, loanRepaymentScheduleTransactionProcessor,
|
||||
processInstallmentsFrom, installments, recalculationDetails, skipRecalculation);
|
||||
processInstallmentsFrom = recalculatedSchedule.getInstallmentNumber();
|
||||
if (recalculatedSchedule.getLoanScheduleModel() != null) {
|
||||
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||
numberOfInstallments = installments.size() - 1;
|
||||
}
|
||||
}
|
||||
return loanScheduleModel;
|
||||
}
|
||||
|
||||
private RecalculatedSchedule recalculateInterest(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
||||
LoanScheduleModel model, final List<RecalculationDetail> recalculationDetails) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
||||
final int skipRecalculation) {
|
||||
boolean processRecalculate = false;
|
||||
int processedInstallmentNumber = installmentNumber;
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
|
||||
final List<RecalculationDetail> diffAmt = new ArrayList<>();
|
||||
Money unpaidPricipal = loanApplicationTerms.getPrincipal();
|
||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(null,
|
||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||
scheduledLoanInstallment.penaltyChargesDue());
|
||||
installments.add(installment);
|
||||
}
|
||||
}
|
||||
|
||||
MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
|
||||
final List<LoanRepaymentScheduleInstallment> processinstallmets = new ArrayList<>();
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
installment.resetDerivedComponents();
|
||||
installment.updateDerivedFields(currency, loanApplicationTerms.getExpectedDisbursementDate());
|
||||
}
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
processinstallmets.add(installment);
|
||||
unpaidPricipal = unpaidPricipal.minus(installment.getPrincipal(currency));
|
||||
@@ -577,7 +627,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
for (LoanTransaction loanTransaction : transactions) {
|
||||
LocalDate loantransactionDate = loanTransaction.getTransactionDate();
|
||||
if (!loantransactionDate.isAfter(transactionsDate) && !loanTransaction.isAccrual()) {
|
||||
if (!loantransactionDate.isAfter(transactionsDate)) {
|
||||
transactionsForInstallment.add(loanTransaction);
|
||||
recalculationDates.put(
|
||||
loantransactionDate,
|
||||
@@ -585,9 +635,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
workingDays));
|
||||
}
|
||||
}
|
||||
Map<LocalDate, Money> earlyPaymentMap = loanRepaymentScheduleTransactionProcessor.handleRecalculation(
|
||||
loanApplicationTerms.getExpectedDisbursementDate(), transactionsForInstallment, currency, processinstallmets,
|
||||
installment, recalculationDates);
|
||||
Map<LocalDate, Money> earlyPaymentMap = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(
|
||||
transactionsForInstallment, currency, processinstallmets, installment, recalculationDates);
|
||||
|
||||
for (Map.Entry<LocalDate, Money> entry : earlyPaymentMap.entrySet()) {
|
||||
LocalDate startDate = entry.getKey();
|
||||
@@ -645,20 +694,37 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
}
|
||||
|
||||
if (!diffAmt.isEmpty()) {
|
||||
if ((!diffAmt.isEmpty() && skipRecalculation <= installment.getInstallmentNumber())
|
||||
|| installment.getInstallmentNumber() == installments.size() - 1) {
|
||||
processRecalculate = true;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
LoanScheduleModel model = null;
|
||||
recalculationDetails.addAll(diffAmt);
|
||||
if (processRecalculate) {
|
||||
recalculationDetails.addAll(diffAmt);
|
||||
model = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays,
|
||||
recalculationDetails);
|
||||
}
|
||||
return new RecalculatedSchedule(model, processedInstallmentNumber);
|
||||
}
|
||||
|
||||
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentSchedule(LoanScheduleModel model) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(null,
|
||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||
scheduledLoanInstallment.penaltyChargesDue());
|
||||
installments.add(installment);
|
||||
}
|
||||
}
|
||||
return installments;
|
||||
}
|
||||
|
||||
private void applyRest(List<LoanTransaction> loanTransactions, LocalDate from, LocalDate to,
|
||||
LoanRepaymentScheduleInstallment installment,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
||||
@@ -668,7 +734,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||
for (LoanTransaction transaction : loanTransactions) {
|
||||
LocalDate loantransactionDate = transaction.getTransactionDate();
|
||||
if (loantransactionDate.isAfter(from) && !transaction.getTransactionDate().isAfter(to) && !transaction.isAccrual()) {
|
||||
if (loantransactionDate.isAfter(from) && !transaction.getTransactionDate().isAfter(to)) {
|
||||
transactions.add(transaction);
|
||||
recalculationDates.put(
|
||||
loantransactionDate,
|
||||
@@ -714,10 +780,31 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
}
|
||||
|
||||
Money interest = applicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc, prepaymentAmount,
|
||||
BigDecimal interest = applicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc, prepaymentAmount,
|
||||
calculateInterestFrom, LocalDate.now());
|
||||
prepaymentAmount = prepaymentAmount.plus(interest).plus(amount);
|
||||
return prepaymentAmount;
|
||||
}
|
||||
|
||||
private Integer findLastProcessedInstallmentNumber(final List<LoanRepaymentScheduleInstallment> installments, LocalDate date) {
|
||||
int installmentNumber = 0;
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (!installment.getDueDate().isAfter(date) && installmentNumber < installment.getInstallmentNumber()) {
|
||||
installmentNumber = installment.getInstallmentNumber();
|
||||
}
|
||||
}
|
||||
|
||||
return installmentNumber;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> processTransactions(final List<LoanTransaction> transactions, final LocalDate tillDate) {
|
||||
List<LoanTransaction> toProcess = new ArrayList<>();
|
||||
for (LoanTransaction loanTransaction : transactions) {
|
||||
if (!loanTransaction.getTransactionDate().isAfter(tillDate)) {
|
||||
toProcess.add(loanTransaction);
|
||||
}
|
||||
}
|
||||
return toProcess;
|
||||
}
|
||||
|
||||
}
|
||||
+11
-6
@@ -663,8 +663,8 @@ public final class LoanApplicationTerms {
|
||||
return periodicInterestRate;
|
||||
}
|
||||
|
||||
public Money interestRateFor(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc, final Money outstandingBalance,
|
||||
final LocalDate fromDate, final LocalDate toDate) {
|
||||
public BigDecimal interestRateFor(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc,
|
||||
final Money outstandingBalance, final LocalDate fromDate, final LocalDate toDate) {
|
||||
|
||||
long loanTermPeriodsInOneYear = calculator.calculate(PeriodFrequencyType.DAYS).longValue();
|
||||
int repaymentEvery = Days.daysBetween(fromDate, toDate).getDays();
|
||||
@@ -678,7 +678,7 @@ public final class LoanApplicationTerms {
|
||||
final BigDecimal oneDayOfYearInterestRate = this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(
|
||||
divisor, mc);
|
||||
BigDecimal interestRate = oneDayOfYearInterestRate.multiply(BigDecimal.valueOf(repaymentEvery), mc);
|
||||
return outstandingBalance.multiplyRetainScale(interestRate, mc.getRoundingMode());
|
||||
return outstandingBalance.getAmount().multiply(interestRate, mc);
|
||||
}
|
||||
|
||||
private long calculatePeriodsInOneYear(final PaymentPeriodsInOneYearCalculator calculator) {
|
||||
@@ -709,7 +709,7 @@ public final class LoanApplicationTerms {
|
||||
return totalNumberOfRepaymentPeriods - getPrincipalGrace();
|
||||
}
|
||||
|
||||
private boolean isPrincipalGraceApplicableForThisPeriod(final int periodNumber) {
|
||||
public boolean isPrincipalGraceApplicableForThisPeriod(final int periodNumber) {
|
||||
return periodNumber > 0 && periodNumber <= getPrincipalGrace();
|
||||
}
|
||||
|
||||
@@ -981,7 +981,7 @@ public final class LoanApplicationTerms {
|
||||
if (this.interestCalculationPeriodMethod.getValue().equals(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getValue())) {
|
||||
switch (this.repaymentPeriodFrequencyType) {
|
||||
case WEEKS:
|
||||
int days = getPeriodsBetween(fromDate, toDate);
|
||||
int days = Days.daysBetween(fromDate, toDate).getDays();
|
||||
isSameAsRepaymentPeriod = (days % 7) == 0;
|
||||
break;
|
||||
case MONTHS:
|
||||
@@ -1016,9 +1016,14 @@ public final class LoanApplicationTerms {
|
||||
PeriodType periodType = PeriodType.yearMonthDay();
|
||||
Period difference = new Period(fromDate, toDate, periodType);
|
||||
switch (this.repaymentPeriodFrequencyType) {
|
||||
case WEEKS:
|
||||
case DAYS:
|
||||
numberOfPeriods = difference.getDays();
|
||||
break;
|
||||
case WEEKS:
|
||||
periodType = PeriodType.weeks();
|
||||
difference = new Period(fromDate, toDate, periodType);
|
||||
numberOfPeriods = difference.getWeeks();
|
||||
break;
|
||||
case MONTHS:
|
||||
numberOfPeriods = difference.getMonths();
|
||||
break;
|
||||
|
||||
+3
-1
@@ -9,6 +9,7 @@ import java.math.MathContext;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
@@ -27,7 +28,8 @@ public interface LoanScheduleGenerator {
|
||||
LoanScheduleModel rescheduleNextInstallments(MathContext mc, ApplicationCurrency applicationCurrency,
|
||||
LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges, boolean isHolidayEnabled, List<Holiday> holidays,
|
||||
WorkingDays workingDays, List<LoanTransaction> transactions,
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor);
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
||||
|
||||
Money fetchPrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||
LoanApplicationTerms applicationTerms, MathContext mc);
|
||||
|
||||
+1
-1
@@ -2205,7 +2205,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
workingDays = this.workingDaysRepository.findOne();
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateSchedule(loanScheduleFactory, applicationCurrency,
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user