From 09df00c078e23524c77fd0a77b2d0f21983130d7 Mon Sep 17 00:00:00 2001 From: pramod Date: Thu, 24 Dec 2015 12:41:26 +0530 Subject: [PATCH 1/2] MIFOSX-2358:fix for last installment overdue charge --- .../portfolio/loanaccount/domain/Loan.java | 3 ++ .../service/LoanReadPlatformServiceImpl.java | 1 + ...WritePlatformServiceJpaRepositoryImpl.java | 44 ++++++++++++++++--- .../loanproduct/data/LoanOverdueDTO.java | 11 ++++- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 7e3060e1b..529ad40c7 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -836,6 +836,9 @@ public class Loan extends AbstractPersistable { */ private BigDecimal calculateAmountPercentageAppliedTo(final LoanCharge loanCharge) { BigDecimal amount = BigDecimal.ZERO; + if(loanCharge.isOverdueInstallmentCharge()){ + return loanCharge.getAmountPercentageAppliedTo(); + } switch (loanCharge.getChargeCalculation()) { case PERCENT_OF_AMOUNT: amount = getPrincpal().getAmount(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 2515994a0..02f8df98e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -1482,6 +1482,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder.append("select ").append(rm.schema()).append(" where DATE_SUB(CURDATE(),INTERVAL ? DAY) > ls.duedate ") .append(" and ls.completed_derived <> 1 and mc.charge_applies_to_enum =1 ") + .append(" and ls.recalculated_interest_component <> 1 ") .append(" and mc.charge_time_enum = 9 and ml.loan_status_id = 300 "); if (backdatePenalties) { return this.jdbcTemplate.query(sqlBuilder.toString(), rm, new Object[] { penaltyWaitPeriod }); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 1ce5db5d2..193c11720 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -2474,6 +2474,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingReversedTransactionIds = new ArrayList<>(); boolean runInterestRecalculation = false; LocalDate recalculateFrom = DateUtils.getLocalDateOfTenant(); + LocalDate lastChargeDate = null; for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) { final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString()); @@ -2486,6 +2487,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (recalculateFrom.isAfter(overdueDTO.getRecalculateFrom())) { recalculateFrom = overdueDTO.getRecalculateFrom(); } + if (lastChargeDate == null || overdueDTO.getLastChargeAppliedDate().isAfter(lastChargeDate)) { + lastChargeDate = overdueDTO.getLastChargeAppliedDate(); + } } if (loan != null) { boolean reprocessRequired = true; @@ -2503,6 +2507,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } if (reprocessRequired) { + addInstallmentIfPenaltyAppliedAfterLastDueDate(loan, lastChargeDate); ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions(); if (changedTransactionDetail != null) { for (final Map.Entry mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) { @@ -2528,6 +2533,28 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } + private void addInstallmentIfPenaltyAppliedAfterLastDueDate(Loan loan, LocalDate lastChargeDate) { + if (lastChargeDate != null) { + List installments = loan.fetchRepaymentScheduleInstallments(); + LoanRepaymentScheduleInstallment lastInstallment = loan.fetchRepaymentScheduleInstallment(installments.size()); + if (lastChargeDate.isAfter(lastInstallment.getDueDate())) { + if (lastInstallment.isRecalculatedInterestComponent()) { + installments.remove(lastInstallment); + lastInstallment = loan.fetchRepaymentScheduleInstallment(installments.size()); + } + boolean recalculatedInterestComponent = true; + BigDecimal principal = BigDecimal.ZERO; + BigDecimal interest = BigDecimal.ZERO; + BigDecimal feeCharges = BigDecimal.ZERO; + BigDecimal penaltyCharges = BigDecimal.ONE; + LoanRepaymentScheduleInstallment newEntry = new LoanRepaymentScheduleInstallment(loan, installments.size() + 1, + lastInstallment.getDueDate(), lastChargeDate, principal, interest, feeCharges, penaltyCharges, + recalculatedInterestComponent); + installments.add(newEntry); + } + } + } + public LoanOverdueDTO applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final Integer periodNumber, final JsonCommand command, Loan loan, final List existingTransactionIds, final List existingReversedTransactionIds) { boolean runInterestRecalculation = false; @@ -2565,6 +2592,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } LoanRepaymentScheduleInstallment installment = null; + LocalDate lastChargeAppliedDate = dueDate; if (!scheduleDates.isEmpty()) { if (loan == null) { loan = this.loanAssembler.assembleFrom(loanId); @@ -2573,8 +2601,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds()); } installment = loan.fetchRepaymentScheduleInstallment(periodNumber); + lastChargeAppliedDate = installment.getDueDate(); } LocalDate recalculateFrom = DateUtils.getLocalDateOfTenant(); + if (loan != null) { this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_APPLY_OVERDUE_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN, loan)); @@ -2591,10 +2621,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (entry.getValue().isBefore(recalculateFrom)) { recalculateFrom = entry.getValue(); } + if (entry.getValue().isAfter(lastChargeAppliedDate)) { + lastChargeAppliedDate = entry.getValue(); + } } } - return new LoanOverdueDTO(loan, runInterestRecalculation, recalculateFrom); + return new LoanOverdueDTO(loan, runInterestRecalculation, recalculateFrom, lastChargeAppliedDate); } @Override @@ -3079,11 +3112,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf boolean isFloatingInterestRate = loan.getIsFloatingInterestRate(); BigDecimal interestRateDiff = loan.getInterestRateDifferential(); List baseLendingRatePeriods = null; - try{ - baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate() - .getRatePeriods(); - }catch(final FloatingRateNotFoundException ex){ - // Do not do anything + try { + baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods(); + } catch (final FloatingRateNotFoundException ex) { + // Do not do anything } floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff, baseLendingRatePeriods); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanOverdueDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanOverdueDTO.java index 50915a319..18c24f2ed 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanOverdueDTO.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanOverdueDTO.java @@ -13,11 +13,14 @@ public class LoanOverdueDTO { private final Loan loan; private final boolean runInterestRecalculation; private final LocalDate recalculateFrom; + private final LocalDate lastChargeAppliedDate; - public LoanOverdueDTO(final Loan loan, final boolean runInterestRecalculation, final LocalDate recalculateFrom) { + public LoanOverdueDTO(final Loan loan, final boolean runInterestRecalculation, final LocalDate recalculateFrom, + final LocalDate lastChargeAppliedDate) { this.loan = loan; this.runInterestRecalculation = runInterestRecalculation; this.recalculateFrom = recalculateFrom; + this.lastChargeAppliedDate = lastChargeAppliedDate; } public boolean isRunInterestRecalculation() { @@ -28,8 +31,12 @@ public class LoanOverdueDTO { return this.loan; } - public LocalDate getRecalculateFrom() { return this.recalculateFrom; } + + + public LocalDate getLastChargeAppliedDate() { + return this.lastChargeAppliedDate; + } } From 9901fc0d3f4f8457b9d0d209f0afb26286e056b4 Mon Sep 17 00:00:00 2001 From: pramod Date: Tue, 29 Dec 2015 10:21:32 +0530 Subject: [PATCH 2/2] added support to accept interest charged from before disburse date --- .../domain/AbstractLoanScheduleGenerator.java | 24 ++++++++++--------- ...ateLoanScheduleQueryFromApiJsonHelper.java | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index 6644dc716..93c259f98 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -172,7 +172,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // calculated interest start date for the period LocalDate periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, - scheduleParams.getPeriodStartDate(), idealDisbursementDate); + scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate); // Loan Schedule Exceptions that need to be applied for Loan Account LoanTermVariationParams termVariationParams = applyLoanTermVariations(loanApplicationTerms, scheduleParams, @@ -229,14 +229,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // for a loan repayment which falls between the // two periods for interest first repayment strategies handleRecalculationForNonDueDateTransactions(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, scheduleParams, periods, - totalInterestChargedForFullLoanTerm, idealDisbursementDate, lastRestDate, scheduledDueDate, + totalInterestChargedForFullLoanTerm, idealDisbursementDate, firstRepaymentdate, lastRestDate, scheduledDueDate, periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams); if (currentPeriodParams.isSkipCurrentLoop()) { continue; } periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, - scheduleParams.getPeriodStartDate(), idealDisbursementDate); + scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate); // backup for pre-close transaction updateCompoundingDetails(scheduleParams, periodStartDateApplicableForInterest); @@ -581,9 +581,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener private void handleRecalculationForNonDueDateTransactions(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, final Set loanCharges, final HolidayDetailDTO holidayDetailDTO, LoanScheduleParams scheduleParams, final Collection periods, final Money totalInterestChargedForFullLoanTerm, - final LocalDate idealDisbursementDate, final LocalDate lastRestDate, final LocalDate scheduledDueDate, - final LocalDate periodStartDateForInterest, final Collection applicableTransactions, - final ScheduleCurrentPeriodParams currentPeriodParams) { + final LocalDate idealDisbursementDate, LocalDate firstRepaymentdate, final LocalDate lastRestDate, + final LocalDate scheduledDueDate, final LocalDate periodStartDateForInterest, + final Collection applicableTransactions, final ScheduleCurrentPeriodParams currentPeriodParams) { if (scheduleParams.applyInterestRecalculation()) { final MonetaryCurrency currency = scheduleParams.getCurrency(); final Collection interestRates = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges(); @@ -608,7 +608,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // calculates period start date for interest // calculation as per the configuration periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms, - scheduleParams.getPeriodStartDate(), idealDisbursementDate); + scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate); int daysInPeriodApplicable = Days.daysBetween(periodStartDateApplicableForInterest, transactionDate).getDays(); Money interestForThisinstallment = Money.zero(currency); @@ -1507,15 +1507,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener /** * calculates Interest stating date as per the settings + * + * @param firstRepaymentdate + * TODO */ private LocalDate calculateInterestStartDateForPeriod(final LoanApplicationTerms loanApplicationTerms, LocalDate periodStartDate, - final LocalDate idealDisbursementDate) { + final LocalDate idealDisbursementDate, final LocalDate firstRepaymentdate) { LocalDate periodStartDateApplicableForInterest = periodStartDate; - if (periodStartDate.isBefore(idealDisbursementDate)) { + if (periodStartDate.isBefore(idealDisbursementDate) || firstRepaymentdate.isAfter(periodStartDate)) { if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) { if (periodStartDate.isEqual(loanApplicationTerms.getExpectedDisbursementDate()) - || loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate().isBefore( - loanApplicationTerms.getInterestChargedFromLocalDate())) { + || loanApplicationTerms.getInterestChargedFromLocalDate().isAfter(periodStartDate)) { periodStartDateApplicableForInterest = loanApplicationTerms.getInterestChargedFromLocalDate(); } } else { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java index a579768f7..657c7de7c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/CalculateLoanScheduleQueryFromApiJsonHelper.java @@ -146,13 +146,13 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper { dataValidationErrors.add(error); } else if (repaymentsStartingFromDate == null && interestChargedFromDate != null) { - if (expectedDisbursementDate != null && expectedDisbursementDate.isAfter(interestChargedFromDate)) { + /* if (expectedDisbursementDate != null && expectedDisbursementDate.isAfter(interestChargedFromDate)) { final ApiParameterError error = ApiParameterError.parameterError( "validation.msg.loan.interestChargedFromDate.cannot.be.before.disbursement.date", "The parameter interestChargedFromDate cannot be before the date given for expectedDisbursementDate.", "interestChargedFromDate", interestChargedFromDate, expectedDisbursementDate); dataValidationErrors.add(error); - } + }*/ } }