Merge pull request #1580 from pramodn02/variableinstalmentchanges
MIFOSX-2358:fix for last installment overdue charge
This commit is contained in:
+3
@@ -836,6 +836,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
*/
|
||||
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();
|
||||
|
||||
+13
-11
@@ -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<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, LoanScheduleParams scheduleParams,
|
||||
final Collection<LoanScheduleModelPeriod> periods, final Money totalInterestChargedForFullLoanTerm,
|
||||
final LocalDate idealDisbursementDate, final LocalDate lastRestDate, final LocalDate scheduledDueDate,
|
||||
final LocalDate periodStartDateForInterest, final Collection<RecalculationDetail> applicableTransactions,
|
||||
final ScheduleCurrentPeriodParams currentPeriodParams) {
|
||||
final LocalDate idealDisbursementDate, LocalDate firstRepaymentdate, final LocalDate lastRestDate,
|
||||
final LocalDate scheduledDueDate, final LocalDate periodStartDateForInterest,
|
||||
final Collection<RecalculationDetail> applicableTransactions, final ScheduleCurrentPeriodParams currentPeriodParams) {
|
||||
if (scheduleParams.applyInterestRecalculation()) {
|
||||
final MonetaryCurrency currency = scheduleParams.getCurrency();
|
||||
final Collection<LoanTermVariationsData> 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 {
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -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 }); }
|
||||
|
||||
+38
-6
@@ -2474,6 +2474,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final List<Long> 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<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
@@ -2528,6 +2533,28 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
private void addInstallmentIfPenaltyAppliedAfterLastDueDate(Loan loan, LocalDate lastChargeDate) {
|
||||
if (lastChargeDate != null) {
|
||||
List<LoanRepaymentScheduleInstallment> 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<Long> existingTransactionIds, final List<Long> 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<FloatingRatePeriodData> 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);
|
||||
|
||||
+9
-2
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user