blob: d006cd52aead9aff2979024022e782cd3b01a120 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY;
import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS;
import org.apache.fineract.portfolio.common.service.BusinessEventListner;
import org.apache.fineract.portfolio.common.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.apache.fineract.scheduledjobs.service.ScheduledJobRunnerServiceImpl;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LoanArrearsAgingServiceImpl implements LoanArrearsAgingService, BusinessEventListner {
private final static Logger logger = LoggerFactory.getLogger(ScheduledJobRunnerServiceImpl.class);
private final BusinessEventNotifierService businessEventNotifierService;
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
private final JdbcTemplate jdbcTemplate;
@Autowired
public LoanArrearsAgingServiceImpl(final RoutingDataSource dataSource, final BusinessEventNotifierService businessEventNotifierService) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.businessEventNotifierService = businessEventNotifierService;
}
@PostConstruct
public void registerForNotification() {
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_REFUND, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_ADJUST_TRANSACTION, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_UNDO_WRITTEN_OFF, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_WAIVE_INTEREST, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_ADD_CHARGE, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_WAIVE_CHARGE, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_CHARGE_PAYMENT, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_APPLY_OVERDUE_CHARGE, this);
this.businessEventNotifierService.addBusinessEventPostListners(BUSINESS_EVENTS.LOAN_DISBURSAL, new DisbursementEventListner());
}
@Transactional
@Override
@CronTarget(jobName = JobName.UPDATE_LOAN_ARREARS_AGEING)
public void updateLoanArrearsAgeingDetails() {
this.jdbcTemplate.execute("truncate table m_loan_arrears_aging");
final StringBuilder updateSqlBuilder = new StringBuilder(900);
updateSqlBuilder
.append("INSERT INTO m_loan_arrears_aging(`loan_id`,`principal_overdue_derived`,`interest_overdue_derived`,`fee_charges_overdue_derived`,`penalty_charges_overdue_derived`,`total_overdue_derived`,`overdue_since_date_derived`)");
updateSqlBuilder.append("select ml.id as loanId,");
updateSqlBuilder
.append("SUM((ifnull(mr.principal_amount,0) - ifnull(mr.principal_completed_derived, 0))) as principal_overdue_derived,");
updateSqlBuilder
.append("SUM((ifnull(mr.interest_amount,0) - ifnull(mr.interest_completed_derived, 0))) as interest_overdue_derived,");
updateSqlBuilder
.append("SUM((ifnull(mr.fee_charges_amount,0) - ifnull(mr.fee_charges_completed_derived, 0))) as fee_charges_overdue_derived,");
updateSqlBuilder
.append("SUM((ifnull(mr.penalty_charges_amount,0) - ifnull(mr.penalty_charges_completed_derived, 0))) as penalty_charges_overdue_derived,");
updateSqlBuilder.append("SUM((ifnull(mr.principal_amount,0) - ifnull(mr.principal_completed_derived, 0))) +");
updateSqlBuilder.append("SUM((ifnull(mr.interest_amount,0) - ifnull(mr.interest_completed_derived, 0))) +");
updateSqlBuilder.append("SUM((ifnull(mr.fee_charges_amount,0) - ifnull(mr.fee_charges_completed_derived, 0))) +");
updateSqlBuilder
.append("SUM((ifnull(mr.penalty_charges_amount,0) - ifnull(mr.penalty_charges_completed_derived, 0))) as total_overdue_derived,");
updateSqlBuilder.append("MIN(mr.duedate) as overdue_since_date_derived ");
updateSqlBuilder.append(" FROM m_loan ml ");
updateSqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id ");
updateSqlBuilder.append(" left join m_product_loan_recalculation_details prd on prd.product_id = ml.product_id ");
updateSqlBuilder.append(" WHERE ml.loan_status_id = 300 "); // active
updateSqlBuilder.append(" and mr.completed_derived is false ");
updateSqlBuilder.append(" and mr.duedate < SUBDATE(CURDATE(),INTERVAL ifnull(ml.grace_on_arrears_ageing,0) day) ");
updateSqlBuilder.append(" and (prd.arrears_based_on_original_schedule = 0 or prd.arrears_based_on_original_schedule is null) ");
updateSqlBuilder.append(" GROUP BY ml.id");
List<String> insertStatements = updateLoanArrearsAgeingDetailsWithOriginalSchedule();
insertStatements.add(0, updateSqlBuilder.toString());
final int[] results = this.jdbcTemplate.batchUpdate(insertStatements.toArray(new String[0]));
int result = 0;
for (int i : results) {
result += i;
}
logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by update: " + result);
}
@Override
public void updateLoanArrearsAgeingDetailsWithOriginalSchedule(final Loan loan) {
int count = this.jdbcTemplate.queryForObject("select count(mla.loan_id) from m_loan_arrears_aging mla where mla.loan_id =?",
Integer.class, loan.getId());
List<String> updateStatement = new ArrayList<>();
OriginalScheduleExtractor originalScheduleExtractor = new OriginalScheduleExtractor(loan.getId().toString());
Map<Long, List<LoanSchedulePeriodData>> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema,
originalScheduleExtractor);
if (scheduleDate.size() > 0) {
List<Map<String, Object>> transactions = getLoanSummary(loan.getId(), loan.getLoanSummary());
updateSchheduleWithPaidDetail(scheduleDate, transactions);
createInsertStatements(updateStatement, scheduleDate, count == 0);
if (updateStatement.size() == 1) {
this.jdbcTemplate.update(updateStatement.get(0));
} else {
String deletestatement = "DELETE FROM `m_loan_arrears_aging` WHERE `loan_id`=" + loan.getId();
this.jdbcTemplate.update(deletestatement);
}
}
}
@Override
public void updateLoanArrearsAgeingDetails(final Loan loan) {
int count = this.jdbcTemplate.queryForObject("select count(mla.loan_id) from m_loan_arrears_aging mla where mla.loan_id =?",
Integer.class, loan.getId());
String updateStatement = constructUpdateStatement(loan, count == 0);
if (updateStatement == null) {
String deletestatement = "DELETE FROM `m_loan_arrears_aging` WHERE `loan_id`=" + loan.getId();
this.jdbcTemplate.update(deletestatement);
} else {
this.jdbcTemplate.update(updateStatement);
}
}
private String constructUpdateStatement(final Loan loan, boolean isInsertStatement) {
String updateSql = null;
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
BigDecimal principalOverdue = BigDecimal.ZERO;
BigDecimal interestOverdue = BigDecimal.ZERO;
BigDecimal feeOverdue = BigDecimal.ZERO;
BigDecimal penaltyOverdue = BigDecimal.ZERO;
LocalDate overDueSince = LocalDate.now();
for (LoanRepaymentScheduleInstallment installment : installments) {
if (installment.getDueDate().isBefore(LocalDate.now())) {
principalOverdue = principalOverdue.add(installment.getPrincipalOutstanding(loan.getCurrency()).getAmount());
interestOverdue = interestOverdue.add(installment.getInterestOutstanding(loan.getCurrency()).getAmount());
feeOverdue = feeOverdue.add(installment.getFeeChargesOutstanding(loan.getCurrency()).getAmount());
penaltyOverdue = penaltyOverdue.add(installment.getPenaltyChargesOutstanding(loan.getCurrency()).getAmount());
if (installment.isNotFullyPaidOff() && overDueSince.isAfter(installment.getDueDate())) {
overDueSince = installment.getDueDate();
}
}
}
BigDecimal totalOverDue = principalOverdue.add(interestOverdue).add(feeOverdue).add(penaltyOverdue);
if (totalOverDue.compareTo(BigDecimal.ZERO) == 1) {
if (isInsertStatement) {
updateSql = constructInsertStatement(loan.getId(), principalOverdue, interestOverdue, feeOverdue, penaltyOverdue,
overDueSince);
} else {
updateSql = constructUpdateStatement(loan.getId(), principalOverdue, interestOverdue, feeOverdue, penaltyOverdue,
overDueSince);
}
}
return updateSql;
}
private List<String> updateLoanArrearsAgeingDetailsWithOriginalSchedule() {
List<String> insertStatement = new ArrayList<>();
final StringBuilder loanIdentifier = new StringBuilder();
loanIdentifier.append("select ml.id as loanId FROM m_loan ml ");
loanIdentifier.append("INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id ");
loanIdentifier
.append("inner join m_product_loan_recalculation_details prd on prd.product_id = ml.product_id and prd.arrears_based_on_original_schedule = 1 ");
loanIdentifier
.append("WHERE ml.loan_status_id = 300 and mr.completed_derived is false and mr.duedate < SUBDATE(CURDATE(),INTERVAL ifnull(ml.grace_on_arrears_ageing,0) day) group by ml.id");
List<Long> loanIds = this.jdbcTemplate.queryForList(loanIdentifier.toString(), Long.class);
if (!loanIds.isEmpty()) {
String loanIdsAsString = loanIds.toString();
loanIdsAsString = loanIdsAsString.substring(1, loanIdsAsString.length() - 1);
OriginalScheduleExtractor originalScheduleExtractor = new OriginalScheduleExtractor(loanIdsAsString);
Map<Long, List<LoanSchedulePeriodData>> scheduleDate = this.jdbcTemplate.query(originalScheduleExtractor.schema,
originalScheduleExtractor);
List<Map<String, Object>> loanSummary = getLoanSummary(loanIdsAsString);
updateSchheduleWithPaidDetail(scheduleDate, loanSummary);
createInsertStatements(insertStatement, scheduleDate, true);
}
return insertStatement;
}
private List<Map<String, Object>> getLoanSummary(final String loanIdsAsString) {
final StringBuilder transactionsSql = new StringBuilder();
transactionsSql.append("select ml.id as loanId, ");
transactionsSql
.append("ml.principal_repaid_derived as principalAmtPaid, ml.principal_writtenoff_derived as principalAmtWrittenoff, ");
transactionsSql.append(" ml.interest_repaid_derived as interestAmtPaid, ml.interest_waived_derived as interestAmtWaived, ");
transactionsSql.append("ml.fee_charges_repaid_derived as feeAmtPaid, ml.fee_charges_waived_derived as feeAmtWaived, ");
transactionsSql
.append("ml.penalty_charges_repaid_derived as penaltyAmtPaid, ml.penalty_charges_waived_derived as penaltyAmtWaived ");
transactionsSql.append("from m_loan ml ");
transactionsSql.append("where ml.id IN (").append(loanIdsAsString).append(") order by ml.id");
List<Map<String, Object>> loanSummary = this.jdbcTemplate.queryForList(transactionsSql.toString());
return loanSummary;
}
private List<Map<String, Object>> getLoanSummary(final Long loanId, final LoanSummary loanSummary) {
List<Map<String, Object>> transactionDetail = new ArrayList<>();
Map<String, Object> transactionMap = new HashMap<>();
transactionMap.put("loanId", loanId);
transactionMap.put("principalAmtPaid", loanSummary.getTotalPrincipalRepaid());
transactionMap.put("principalAmtWrittenoff", loanSummary.getTotalPrincipalWrittenOff());
transactionMap.put("interestAmtPaid", loanSummary.getTotalInterestRepaid());
transactionMap.put("interestAmtWaived", loanSummary.getTotalInterestWaived());
transactionMap.put("feeAmtPaid", loanSummary.getTotalFeeChargesRepaid());
transactionMap.put("feeAmtWaived", loanSummary.getTotalFeeChargesWaived());
transactionMap.put("penaltyAmtPaid", loanSummary.getTotalPenaltyChargesRepaid());
transactionMap.put("penaltyAmtWaived", loanSummary.getTotalPenaltyChargesWaived());
transactionDetail.add(transactionMap);
return transactionDetail;
}
private void createInsertStatements(List<String> insertStatement, Map<Long, List<LoanSchedulePeriodData>> scheduleDate,
boolean isInsertStatement) {
for (Map.Entry<Long, List<LoanSchedulePeriodData>> entry : scheduleDate.entrySet()) {
final Long loanId = entry.getKey();
BigDecimal principalOverdue = BigDecimal.ZERO;
BigDecimal interestOverdue = BigDecimal.ZERO;
BigDecimal feeOverdue = BigDecimal.ZERO;
BigDecimal penaltyOverdue = BigDecimal.ZERO;
LocalDate overDueSince = LocalDate.now();
for (LoanSchedulePeriodData loanSchedulePeriodData : entry.getValue()) {
if (!loanSchedulePeriodData.getComplete()) {
principalOverdue = principalOverdue.add(loanSchedulePeriodData.principalDue().subtract(
loanSchedulePeriodData.principalPaid()));
interestOverdue = interestOverdue.add(loanSchedulePeriodData.interestDue().subtract(
loanSchedulePeriodData.interestPaid()));
feeOverdue = feeOverdue.add(loanSchedulePeriodData.feeChargesDue().subtract(loanSchedulePeriodData.feeChargesPaid()));
penaltyOverdue = penaltyOverdue.add(loanSchedulePeriodData.penaltyChargesDue().subtract(
loanSchedulePeriodData.penaltyChargesPaid()));
if (overDueSince.isAfter(loanSchedulePeriodData.periodDueDate())
&& loanSchedulePeriodData.principalDue().subtract(loanSchedulePeriodData.principalPaid())
.compareTo(BigDecimal.ZERO) == 1) {
overDueSince = loanSchedulePeriodData.periodDueDate();
}
}
}
if (principalOverdue.compareTo(BigDecimal.ZERO) == 1) {
String sqlStatement = null;
if (isInsertStatement) {
sqlStatement = constructInsertStatement(loanId, principalOverdue, interestOverdue, feeOverdue, penaltyOverdue,
overDueSince);
} else {
sqlStatement = constructUpdateStatement(loanId, principalOverdue, interestOverdue, feeOverdue, penaltyOverdue,
overDueSince);
}
insertStatement.add(sqlStatement);
}
}
}
private String constructInsertStatement(final Long loanId, BigDecimal principalOverdue, BigDecimal interestOverdue,
BigDecimal feeOverdue, BigDecimal penaltyOverdue, LocalDate overDueSince) {
final StringBuilder insertStatementBuilder = new StringBuilder(900);
insertStatementBuilder
.append("INSERT INTO m_loan_arrears_aging(`loan_id`,`principal_overdue_derived`,`interest_overdue_derived`,")
.append("`fee_charges_overdue_derived`,`penalty_charges_overdue_derived`,`total_overdue_derived`,`overdue_since_date_derived`) VALUES(");
insertStatementBuilder.append(loanId).append(",");
insertStatementBuilder.append(principalOverdue).append(",");
insertStatementBuilder.append(interestOverdue).append(",");
insertStatementBuilder.append(feeOverdue).append(",");
insertStatementBuilder.append(penaltyOverdue).append(",");
BigDecimal totalOverDue = principalOverdue.add(interestOverdue).add(feeOverdue).add(penaltyOverdue);
insertStatementBuilder.append(totalOverDue).append(",'");
insertStatementBuilder.append(this.formatter.print(overDueSince)).append("')");
return insertStatementBuilder.toString();
}
private String constructUpdateStatement(final Long loanId, BigDecimal principalOverdue, BigDecimal interestOverdue,
BigDecimal feeOverdue, BigDecimal penaltyOverdue, LocalDate overDueSince) {
final StringBuilder insertStatementBuilder = new StringBuilder(900);
insertStatementBuilder.append("UPDATE m_loan_arrears_aging mla SET mla.principal_overdue_derived=");
insertStatementBuilder.append(principalOverdue).append(", mla.interest_overdue_derived=");
insertStatementBuilder.append(interestOverdue).append(", mla.fee_charges_overdue_derived=");
insertStatementBuilder.append(feeOverdue).append(", mla.penalty_charges_overdue_derived=");
insertStatementBuilder.append(penaltyOverdue).append(", mla.total_overdue_derived=");
BigDecimal totalOverDue = principalOverdue.add(interestOverdue).add(feeOverdue).add(penaltyOverdue);
insertStatementBuilder.append(totalOverDue).append(",mla.overdue_since_date_derived= '");
insertStatementBuilder.append(this.formatter.print(overDueSince)).append("' ");
insertStatementBuilder.append("WHERE mla.loan_id=").append(loanId);
return insertStatementBuilder.toString();
}
private void updateSchheduleWithPaidDetail(Map<Long, List<LoanSchedulePeriodData>> scheduleDate, List<Map<String, Object>> loanSummary) {
for (Map<String, Object> transactionMap : loanSummary) {
Long loanId = (Long) transactionMap.get("loanId");
BigDecimal principalAmtPaid = (BigDecimal) transactionMap.get("principalAmtPaid");
BigDecimal principalAmtWrittenoff = (BigDecimal) transactionMap.get("principalAmtWrittenoff");
BigDecimal interestAmtPaid = (BigDecimal) transactionMap.get("interestAmtPaid");
BigDecimal interestAmtWaived = (BigDecimal) transactionMap.get("interestAmtWaived");
BigDecimal feeAmtPaid = (BigDecimal) transactionMap.get("feeAmtPaid");
BigDecimal feeAmtWaived = (BigDecimal) transactionMap.get("feeAmtWaived");
BigDecimal penaltyAmtPaid = (BigDecimal) transactionMap.get("penaltyAmtPaid");
BigDecimal penaltyAmtWaived = (BigDecimal) transactionMap.get("penaltyAmtWaived");
BigDecimal principalAmt = principalAmtPaid.add(principalAmtWrittenoff);
BigDecimal interestAmt = interestAmtPaid.add(interestAmtWaived);
BigDecimal feeAmt = feeAmtPaid.add(feeAmtWaived);
BigDecimal penaltyAmt = penaltyAmtPaid.add(penaltyAmtWaived);
List<LoanSchedulePeriodData> loanSchedulePeriodDatas = scheduleDate.get(loanId);
if (loanSchedulePeriodDatas != null) {
List<LoanSchedulePeriodData> updatedPeriodData = new ArrayList<>(loanSchedulePeriodDatas.size());
for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDatas) {
BigDecimal principalPaid = null;
BigDecimal interestPaid = null;
BigDecimal feeChargesPaid = null;
BigDecimal penaltyChargesPaid = null;
Boolean isComplete = true;
if (loanSchedulePeriodData.principalDue().compareTo(principalAmt) == 1) {
principalPaid = principalAmt;
principalAmt = BigDecimal.ZERO;
isComplete = false;
} else {
principalPaid = loanSchedulePeriodData.principalDue();
principalAmt = principalAmt.subtract(loanSchedulePeriodData.principalDue());
}
if (loanSchedulePeriodData.interestDue().compareTo(interestAmt) == 1) {
interestPaid = interestAmt;
interestAmt = BigDecimal.ZERO;
isComplete = false;
} else {
interestPaid = loanSchedulePeriodData.interestDue();
interestAmt = interestAmt.subtract(loanSchedulePeriodData.interestDue());
}
if (loanSchedulePeriodData.feeChargesDue().compareTo(feeAmt) == 1) {
feeChargesPaid = feeAmt;
feeAmt = BigDecimal.ZERO;
isComplete = false;
} else {
feeChargesPaid = loanSchedulePeriodData.feeChargesDue();
feeAmt = feeAmt.subtract(loanSchedulePeriodData.feeChargesDue());
}
if (loanSchedulePeriodData.penaltyChargesDue().compareTo(penaltyAmt) == 1) {
penaltyChargesPaid = penaltyAmt;
penaltyAmt = BigDecimal.ZERO;
isComplete = false;
} else {
penaltyChargesPaid = loanSchedulePeriodData.penaltyChargesDue();
penaltyAmt = penaltyAmt.subtract(loanSchedulePeriodData.penaltyChargesDue());
}
LoanSchedulePeriodData periodData = LoanSchedulePeriodData.WithPaidDetail(loanSchedulePeriodData, isComplete,
principalPaid, interestPaid, feeChargesPaid, penaltyChargesPaid);
updatedPeriodData.add(periodData);
}
loanSchedulePeriodDatas.clear();
loanSchedulePeriodDatas.addAll(updatedPeriodData);
}
}
}
private static final class OriginalScheduleExtractor implements ResultSetExtractor<Map<Long, List<LoanSchedulePeriodData>>> {
private final String schema;
public OriginalScheduleExtractor(final String loanIdsAsString) {
final StringBuilder scheduleDetail = new StringBuilder();
scheduleDetail.append("select ml.id as loanId, mr.duedate as dueDate, mr.principal_amount as principalAmount, ");
scheduleDetail
.append("mr.interest_amount as interestAmount, mr.fee_charges_amount as feeAmount, mr.penalty_charges_amount as penaltyAmount ");
scheduleDetail.append("from m_loan ml INNER JOIN m_loan_repayment_schedule_history mr on mr.loan_id = ml.id ");
scheduleDetail.append("where mr.duedate < SUBDATE(CURDATE(),INTERVAL ifnull(ml.grace_on_arrears_ageing,0) day) and ");
scheduleDetail.append("ml.id IN(").append(loanIdsAsString).append(") and mr.version = (");
scheduleDetail.append("select max(lrs.version) from m_loan_repayment_schedule_history lrs where mr.loan_id = lrs.loan_id");
scheduleDetail.append(") order by ml.id,mr.duedate");
this.schema = scheduleDetail.toString();
}
@Override
public Map<Long, List<LoanSchedulePeriodData>> extractData(ResultSet rs) throws SQLException, DataAccessException {
Map<Long, List<LoanSchedulePeriodData>> scheduleDate = new HashMap<>();
while (rs.next()) {
Long loanId = rs.getLong("loanId");
List<LoanSchedulePeriodData> periodDatas = new ArrayList<>();
LoanSchedulePeriodData loanSchedulePeriodData = fetchLoanSchedulePeriodData(rs);
periodDatas.add(loanSchedulePeriodData);
while (rs.next()) {
Long tempLoanId = rs.getLong("loanId");
if (loanId.equals(tempLoanId)) {
periodDatas.add(fetchLoanSchedulePeriodData(rs));
} else {
rs.previous();
break;
}
}
scheduleDate.put(loanId, periodDatas);
}
return scheduleDate;
}
private LoanSchedulePeriodData fetchLoanSchedulePeriodData(ResultSet rs) throws SQLException {
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalAmount");
final BigDecimal interestDueOnPrincipalOutstanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestAmount");
final BigDecimal totalInstallmentAmount = principalDue.add(interestDueOnPrincipalOutstanding);
final BigDecimal feeChargesDueForPeriod = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeAmount");
final BigDecimal penaltyChargesDueForPeriod = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyAmount");
final Integer periodNumber = null;
final LocalDate fromDate = null;
final BigDecimal principalOutstanding = null;
final BigDecimal totalDueForPeriod = null;
return LoanSchedulePeriodData.repaymentOnlyPeriod(periodNumber, fromDate, dueDate, principalDue, principalOutstanding,
interestDueOnPrincipalOutstanding, feeChargesDueForPeriod, penaltyChargesDueForPeriod, totalDueForPeriod,
totalInstallmentAmount);
}
}
@SuppressWarnings("unused")
@Override
public void businessEventToBeExecuted(Map<BUSINESS_ENTITY, Object> businessEventEntity) {
// TODO Auto-generated method stub
}
@Override
public void businessEventWasExecuted(Map<BUSINESS_ENTITY, Object> businessEventEntity) {
Loan loan = null;
Object loanEntity = businessEventEntity.get(BUSINESS_ENTITY.LOAN);
Object loanTransactionEntity = businessEventEntity.get(BUSINESS_ENTITY.LOAN_TRANSACTION);
Object loanAdjustTransactionEntity = businessEventEntity.get(BUSINESS_ENTITY.LOAN_ADJUSTED_TRANSACTION);
Object loanChargeEntity = businessEventEntity.get(BUSINESS_ENTITY.LOAN_CHARGE);
if (loanEntity != null) {
loan = (Loan) loanEntity;
} else if (loanTransactionEntity != null) {
LoanTransaction loanTransaction = (LoanTransaction) loanTransactionEntity;
loan = loanTransaction.getLoan();
} else if (loanAdjustTransactionEntity != null) {
LoanTransaction loanTransaction = (LoanTransaction) loanAdjustTransactionEntity;
loan = loanTransaction.getLoan();
} else if (loanChargeEntity != null) {
LoanCharge loanCharge = (LoanCharge) loanChargeEntity;
loan = loanCharge.getLoan();
}
if (loan != null && loan.isOpen() && loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
&& loan.loanProduct().isArrearsBasedOnOriginalSchedule()) {
updateLoanArrearsAgeingDetailsWithOriginalSchedule(loan);
} else {
updateLoanArrearsAgeingDetails(loan);
}
}
private class DisbursementEventListner implements BusinessEventListner {
@SuppressWarnings("unused")
@Override
public void businessEventToBeExecuted(Map<BUSINESS_ENTITY, Object> businessEventEntity) {
// TODO Auto-generated method stub
}
@Override
public void businessEventWasExecuted(Map<BUSINESS_ENTITY, Object> businessEventEntity) {
Object loanEntity = businessEventEntity.get(BUSINESS_ENTITY.LOAN);
if (loanEntity != null) {
Loan loan = (Loan) loanEntity;
updateLoanArrearsAgeingDetails(loan);
}
}
}
}