/** * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mifosplatform.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.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_ENTITY; import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS; import org.mifosplatform.portfolio.common.service.BusinessEventListner; import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; import org.mifosplatform.scheduledjobs.service.ScheduledJobRunnerServiceImpl; 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); } } } }