blob: 79d2e4532985715e96bac0a0f5d990a1f15e5302 [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.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
import org.apache.fineract.portfolio.charge.service.ChargeDropdownReadPlatformService;
import org.apache.fineract.portfolio.charge.service.ChargeEnumerations;
import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService;
private final DropdownReadPlatformService dropdownReadPlatformService;
private final LoanChargeRepository loanChargeRepository;
private static final class LoanChargeMapper implements RowMapper<LoanChargeData> {
public String schema() {
return "lc.id as id, lc.external_id as externalId, c.id as chargeId, c.name as name, lc.submitted_on_date as submittedOnDate, " //
+ "lc.amount as amountDue, lc.amount_paid_derived as amountPaid, lc.amount_waived_derived as amountWaived, " //
+ "lc.amount_writtenoff_derived as amountWrittenOff, lc.amount_outstanding_derived as amountOutstanding, " //
+ "lc.calculation_percentage as percentageOf, lc.calculation_on_amount as amountPercentageAppliedTo, " //
+ "lc.charge_time_enum as chargeTime, lc.is_penalty as penalty, " //
+ "lc.due_for_collection_as_of_date as dueAsOfDate, lc.charge_calculation_enum as chargeCalculation, " //
+ "lc.charge_payment_mode_enum as chargePaymentMode, lc.is_paid_derived as paid, lc.waived as waived, " //
+ "lc.min_cap as minCap, lc.max_cap as maxCap, lc.charge_amount_or_percentage as amountOrPercentage, " //
+ "lc.loan_id as loanId, c.currency_code as currencyCode, oc.name as currencyName, " //
+ "date(coalesce(dd.disbursedon_date,dd.expected_disburse_date)) as disbursementDate, " //
+ "oc.decimal_places as currencyDecimalPlaces, oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, " //
+ "oc.internationalized_name_code as currencyNameCode, l.external_id as externalLoanId from m_charge c " //
+ "join m_organisation_currency oc on c.currency_code = oc.code join m_loan_charge lc on lc.charge_id = c.id " //
+ "left join m_loan_tranche_disbursement_charge dc on dc.loan_charge_id=lc.id left join m_loan_disbursement_detail dd on dd.id=dc.disbursement_detail_id " //
+ " join m_loan l on lc.loan_id = l.id";
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId");
final Long loanId = rs.getLong("loanId");
final String name = rs.getString("name");
final BigDecimal amount = rs.getBigDecimal("amountDue");
final BigDecimal amountPaid = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPaid");
final BigDecimal amountWaived = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWaived");
final BigDecimal amountWrittenOff = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountWrittenOff");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal percentageOf = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentageOf");
final BigDecimal amountPercentageAppliedTo = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "amountPercentageAppliedTo");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDecimalPlaces = JdbcSupport.getInteger(rs, "currencyDecimalPlaces");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDecimalPlaces, inMultiplesOf,
currencyDisplaySymbol, currencyNameCode);
final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final int chargeCalculation = rs.getInt("chargeCalculation");
final EnumOptionData chargeCalculationType = ChargeEnumerations.chargeCalculationType(chargeCalculation);
final boolean penalty = rs.getBoolean("penalty");
final int chargePaymentMode = rs.getInt("chargePaymentMode");
final EnumOptionData paymentMode = ChargeEnumerations.chargePaymentMode(chargePaymentMode);
final boolean paid = rs.getBoolean("paid");
final boolean waived = rs.getBoolean("waived");
final BigDecimal minCap = rs.getBigDecimal("minCap");
final BigDecimal maxCap = rs.getBigDecimal("maxCap");
final BigDecimal amountOrPercentage = rs.getBigDecimal("amountOrPercentage");
final LocalDate disbursementDate = JdbcSupport.getLocalDate(rs, "disbursementDate");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
if (disbursementDate != null) {
dueAsOfDate = disbursementDate;
}
final String externalIdStr = rs.getString("externalId");
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
final String externalLoanIdStr = rs.getString("externalLoanId");
final ExternalId externalLoanId = ExternalIdFactory.produce(externalLoanIdStr);
return new LoanChargeData(id, chargeId, name, currency, amount, amountPaid, amountWaived, amountWrittenOff, amountOutstanding,
chargeTimeType, submittedOnDate, dueAsOfDate, chargeCalculationType, percentageOf, amountPercentageAppliedTo, penalty,
paymentMode, paid, waived, loanId, externalLoanId, minCap, maxCap, amountOrPercentage, null, externalId);
}
}
@Override
public ChargeData retrieveLoanChargeTemplate() {
final List<EnumOptionData> allowedChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService.retrieveCalculationTypes();
final List<EnumOptionData> allowedChargeTimeOptions = this.chargeDropdownReadPlatformService.retrieveCollectionTimeTypes();
final List<EnumOptionData> loansChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveLoanCalculationTypes();
final List<EnumOptionData> loansChargeTimeTypeOptions = this.chargeDropdownReadPlatformService.retrieveLoanCollectionTimeTypes();
final List<EnumOptionData> savingsChargeCalculationTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCalculationTypes();
final List<EnumOptionData> savingsChargeTimeTypeOptions = this.chargeDropdownReadPlatformService
.retrieveSavingsCollectionTimeTypes();
final List<EnumOptionData> clientChargeCalculationTypeOptions = null;
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
// this field is applicable only for client charges
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
final List<EnumOptionData> shareChargeCalculationTypeOptions = null;
final List<EnumOptionData> shareChargeTimeTypeOptions = null;
final Collection<TaxGroupData> taxGroupOptions = null;
final String accountMappingForChargeConfig = null;
final List<GLAccountData> expenseAccountOptions = null;
final List<GLAccountData> assetAccountOptions = null;
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
incomeOrLiabilityAccountOptions, taxGroupOptions, shareChargeCalculationTypeOptions, shareChargeTimeTypeOptions,
accountMappingForChargeConfig, expenseAccountOptions, assetAccountOptions);
}
@Override
public LoanChargeData retrieveLoanChargeDetails(final Long id, final Long loanId) {
try {
final LoanChargeMapper rm = new LoanChargeMapper();
final String sql = "select " + rm.schema() + " where lc.id=? and lc.loan_id=?";
return this.jdbcTemplate.queryForObject(sql, rm, id, loanId); // NOSONAR
} catch (final EmptyResultDataAccessException e) {
throw new LoanChargeNotFoundException(id, loanId, e);
}
}
@Override
public Collection<LoanChargeData> retrieveLoanCharges(final Long loanId) {
final LoanChargeMapper rm = new LoanChargeMapper();
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = true"
+ " order by coalesce(lc.due_for_collection_as_of_date,date(coalesce(dd.disbursedon_date,dd.expected_disburse_date))),lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
return this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
}
@Override
public Collection<LoanChargeData> retrieveLoanChargesForFeePayment(final Integer paymentMode, final Integer loanStatus) {
final LoanChargeMapperWithLoanId rm = new LoanChargeMapperWithLoanId();
final String sql = "select " + rm.schema()
+ "where loan.loan_status_id= ? and lc.charge_payment_mode_enum=? and lc.waived = false and lc.is_paid_derived=false and lc.is_active = true";
return this.jdbcTemplate.query(sql, rm, loanStatus, paymentMode); // NOSONAR
}
private static final class LoanChargeMapperWithLoanId implements RowMapper<LoanChargeData> {
public String schema() {
return " lc.id as id, lc.due_for_collection_as_of_date as dueAsOfDate, lc.amount_outstanding_derived as amountOutstanding, lc.submitted_on_date as submittedOnDate, "
+ " lc.charge_time_enum as chargeTime, loan.id as loanId, loan.external_id as externalLoanId, lc.external_id as externalId from m_loan_charge lc "
+ " join m_loan loan on loan.id = lc.loan_id ";
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final Long loanId = rs.getLong("loanId");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
final String externalIdStr = rs.getString("externalId");
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
final String externalLoanIdStr = rs.getString("externalLoanId");
final ExternalId externalLoanId = ExternalIdFactory.produce(externalLoanIdStr);
return new LoanChargeData(id, dueAsOfDate, submittedOnDate, amountOutstanding, chargeTimeType, loanId, externalLoanId, null,
externalId);
}
}
@Override
public Collection<LoanInstallmentChargeData> retrieveInstallmentLoanCharges(Long loanChargeId, boolean onlyPaymentPendingCharges) {
final LoanInstallmentChargeMapper rm = new LoanInstallmentChargeMapper();
String sql = "select " + rm.schema() + "where lic.loan_charge_id= ? ";
if (onlyPaymentPendingCharges) {
sql = sql + "and lic.waived = false and lic.is_paid_derived=false";
}
sql = sql + " order by lsi.installment";
return this.jdbcTemplate.query(sql, rm, loanChargeId); // NOSONAR
}
private static final class LoanInstallmentChargeMapper implements RowMapper<LoanInstallmentChargeData> {
public String schema() {
return " lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, "
+ "lic.amount_outstanding_derived as amountOutstanding, lic.amount as amount, lic.is_paid_derived as paid, "
+ "lic.amount_waived_derived as amountWaived, lic.waived as waived from m_loan_installment_charge lic "
+ "join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ";
}
@Override
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Integer installmentNumber = rs.getInt("installmentNumber");
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal amount = rs.getBigDecimal("amount");
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
final boolean paid = rs.getBoolean("paid");
final boolean waived = rs.getBoolean("waived");
return LoanInstallmentChargeData.builder().installmentNumber(installmentNumber).dueDate(dueAsOfDate).amount(amount)
.amountOutstanding(amountOutstanding).amountWaived(amountWaived).paid(paid).waived(waived).build();
}
}
@Override
public Collection<Integer> retrieveOverdueInstallmentChargeFrequencyNumber(final Loan loan, final Charge charge,
final Integer periodNumber) {
List<Integer> frequencyNumbers = new ArrayList<>();
for (LoanCharge loanCharge : loan.getLoanCharges()) {
if (loanCharge.isOverdueInstallmentCharge() && charge.equals(loanCharge.getCharge()) && loanCharge.isActive()
&& periodNumber.equals(loanCharge.getOverdueInstallmentCharge().getInstallment().getInstallmentNumber())) {
frequencyNumbers.add(loanCharge.getOverdueInstallmentCharge().getFrequencyNumber());
}
}
return frequencyNumbers;
}
@Override
public Collection<LoanChargeData> retrieveLoanChargesForAccrual(final Long loanId) {
final LoanChargeAccrualMapper rm = new LoanChargeAccrualMapper();
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = true group by lc.id "
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
Collection<LoanChargeData> charges = this.jdbcTemplate.query(sql, rm, // NOSONAR
LoanTransactionType.ACCRUAL.getValue(), loanId, loanId);
charges = updateLoanChargesWithUnrecognizedIncome(loanId, charges);
Collection<LoanChargeData> removeCharges = new ArrayList<>();
for (LoanChargeData loanChargeData : charges) {
if (loanChargeData.isInstallmentFee()) {
removeCharges.add(loanChargeData);
}
}
charges.removeAll(removeCharges);
for (LoanChargeData loanChargeData : removeCharges) {
if (loanChargeData.isInstallmentFee()) {
Collection<LoanInstallmentChargeData> installmentChargeDatas = retrieveInstallmentLoanChargesForAccrual(
loanChargeData.getId());
LoanChargeData modifiedChargeData = new LoanChargeData(loanChargeData, installmentChargeDatas);
charges.add(modifiedChargeData);
}
}
return charges;
}
private static final class LoanChargeAccrualMapper implements RowMapper<LoanChargeData> {
private final String schemaSql;
LoanChargeAccrualMapper() {
StringBuilder sb = new StringBuilder(50);
sb.append(" lc.id as id, lc.charge_id as chargeId, lc.external_id as externalId, ");
sb.append(" lc.amount as amountDue, ");
sb.append(" lc.amount_waived_derived as amountWaived, ");
sb.append(" lc.charge_time_enum as chargeTime, ");
sb.append(" sum(cp.amount) as amountAccrued, ");
sb.append(" lc.is_penalty as penalty, ");
sb.append(" lc.due_for_collection_as_of_date as dueAsOfDate, ");
sb.append(" lc.submitted_on_date as submittedOnDate ");
sb.append(" from m_loan_charge lc ");
sb.append(" left join ( ");
sb.append(" select lcp.loan_charge_id, lcp.amount ");
sb.append(" from m_loan_charge_paid_by lcp ");
sb.append(
" inner join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ? and lt.loan_id = ? ");
sb.append(" ) cp on cp.loan_charge_id= lc.id ");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId");
final BigDecimal amount = rs.getBigDecimal("amountDue");
final BigDecimal amountAccrued = rs.getBigDecimal("amountAccrued");
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
final int chargeTime = rs.getInt("chargeTime");
final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime);
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final boolean penalty = rs.getBoolean("penalty");
final String externalIdStr = rs.getString("externalId");
final ExternalId externalId = ExternalIdFactory.produce(externalIdStr);
return new LoanChargeData(id, chargeId, dueAsOfDate, submittedOnDate, chargeTimeType, amount, amountAccrued, amountWaived,
penalty, externalId);
}
}
private Collection<LoanChargeData> updateLoanChargesWithUnrecognizedIncome(final Long loanId,
Collection<LoanChargeData> loanChargeDatas) {
final LoanChargeUnRecognizedIncomeMapper rm = new LoanChargeUnRecognizedIncomeMapper(loanChargeDatas);
final String sql = "select " + rm.schema() + " where lc.loan_id=? AND lc.is_active = true group by lc.id "
+ " order by lc.charge_time_enum ASC, lc.due_for_collection_as_of_date ASC, lc.is_penalty ASC";
return this.jdbcTemplate.query(sql, rm, LoanTransactionType.WAIVE_CHARGES.getValue(), loanId, loanId); // NOSONAR
}
private static final class LoanChargeUnRecognizedIncomeMapper implements RowMapper<LoanChargeData> {
private final String schemaSql;
private final Map<Long, LoanChargeData> chargeDataMap;
LoanChargeUnRecognizedIncomeMapper(final Collection<LoanChargeData> datas) {
this.chargeDataMap = new HashMap<>();
for (LoanChargeData chargeData : datas) {
this.chargeDataMap.put(chargeData.getId(), chargeData);
}
StringBuilder sb = new StringBuilder(50);
sb.append("lc.id as id, ");
sb.append(" sum(wt.unrecognized_income_portion) as amountUnrecognized ");
sb.append(" from m_loan_charge lc ");
sb.append("left join (");
sb.append("select cpb.loan_charge_id, lt.unrecognized_income_portion");
sb.append(" from m_loan_charge_paid_by cpb ");
sb.append(
"inner join m_loan_transaction lt on lt.id = cpb.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ? and lt.loan_id = ? ");
sb.append(") wt on wt.loan_charge_id= lc.id ");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
LoanChargeData chargeData = this.chargeDataMap.get(id);
return new LoanChargeData(amountUnrecognized, chargeData);
}
}
private Collection<LoanInstallmentChargeData> retrieveInstallmentLoanChargesForAccrual(Long loanChargeId) {
final LoanInstallmentChargeAccrualMapper rm = new LoanInstallmentChargeAccrualMapper();
String sql = "select " + rm.schema()
+ " where lic.loan_charge_id= ? group by lsi.installment, lsi.duedate, lic.amount_outstanding_derived, lic.amount, lic.is_paid_derived, lic.amount_waived_derived, lic.waived";
Collection<LoanInstallmentChargeData> chargeDatas = this.jdbcTemplate.query(sql, rm, // NOSONAR
LoanTransactionType.ACCRUAL.getValue(), loanChargeId);
final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas = new HashMap<>();
for (LoanInstallmentChargeData installmentChargeData : chargeDatas) {
installmentChargeDatas.put(installmentChargeData.getInstallmentNumber(), installmentChargeData);
}
chargeDatas = updateInstallmentLoanChargesWithUnrecognizedIncome(loanChargeId, installmentChargeDatas);
for (LoanInstallmentChargeData installmentChargeData : chargeDatas) {
installmentChargeDatas.put(installmentChargeData.getInstallmentNumber(), installmentChargeData);
}
return installmentChargeDatas.values();
}
private static final class LoanInstallmentChargeAccrualMapper implements RowMapper<LoanInstallmentChargeData> {
private final String schemaSql;
LoanInstallmentChargeAccrualMapper() {
StringBuilder sb = new StringBuilder(50);
sb.append(" lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, ");
sb.append("lic.amount_outstanding_derived as amountOutstanding,");
sb.append("lic.amount as amount, ");
sb.append("lic.is_paid_derived as paid, ");
sb.append("lic.amount_waived_derived as amountWaived, ");
sb.append(" sum(cp.amount) as amountAccrued, ");
sb.append("lic.waived as waived ");
sb.append("from m_loan_installment_charge lic ");
sb.append("join m_loan_repayment_schedule lsi on lsi.id = lic.loan_schedule_id ");
sb.append("left join (");
sb.append("select lcp.loan_charge_id, lcp.amount as amount, lcp.installment_number ");
sb.append(" from m_loan_charge_paid_by lcp ");
sb.append(
"inner join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ?");
sb.append(") cp on cp.loan_charge_id= lic.loan_charge_id and cp.installment_number = lsi.installment ");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Integer installmentNumber = rs.getInt("installmentNumber");
final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate");
final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding");
final BigDecimal amount = rs.getBigDecimal("amount");
final BigDecimal amountWaived = rs.getBigDecimal("amountWaived");
final boolean paid = rs.getBoolean("paid");
final boolean waived = rs.getBoolean("waived");
final BigDecimal amountAccrued = rs.getBigDecimal("amountAccrued");
return LoanInstallmentChargeData.builder().installmentNumber(installmentNumber).dueDate(dueAsOfDate).amount(amount)
.amountOutstanding(amountOutstanding).amountWaived(amountWaived).paid(paid).waived(waived).amountAccrued(amountAccrued)
.build();
}
}
private Collection<LoanInstallmentChargeData> updateInstallmentLoanChargesWithUnrecognizedIncome(final Long loanChargeId,
final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas) {
final LoanInstallmentChargeUnRecognizedIncomeMapper rm = new LoanInstallmentChargeUnRecognizedIncomeMapper(installmentChargeDatas);
String sql = "select " + rm.schema() + " where cpb.loan_charge_id = ? group by cpb.installment_number ";
return this.jdbcTemplate.query(sql, rm, LoanTransactionType.WAIVE_CHARGES.getValue(), loanChargeId); // NOSONAR
}
private static final class LoanInstallmentChargeUnRecognizedIncomeMapper implements RowMapper<LoanInstallmentChargeData> {
private final String schemaSql;
private final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas;
LoanInstallmentChargeUnRecognizedIncomeMapper(final Map<Integer, LoanInstallmentChargeData> installmentChargeDatas) {
this.installmentChargeDatas = installmentChargeDatas;
StringBuilder sb = new StringBuilder(50);
sb.append(" cpb.installment_number as installmentNumber, ");
sb.append(" sum(lt.unrecognized_income_portion) as amountUnrecognized ");
sb.append(" from m_loan_charge_paid_by cpb ");
sb.append(
"inner join m_loan_transaction lt on lt.id = cpb.loan_transaction_id and lt.is_reversed = false and lt.transaction_type_enum = ?");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanInstallmentChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Integer installmentNumber = rs.getInt("installmentNumber");
final BigDecimal amountUnrecognized = rs.getBigDecimal("amountUnrecognized");
LoanInstallmentChargeData installmentChargeData = this.installmentChargeDatas.get(installmentNumber);
return LoanInstallmentChargeData.builder().installmentNumber(installmentChargeData.getInstallmentNumber())
.dueDate(installmentChargeData.getDueDate()).amount(installmentChargeData.getAmount())
.amountOutstanding(installmentChargeData.getAmountOutstanding()).amountWaived(installmentChargeData.getAmountWaived())
.paid(installmentChargeData.isPaid()).waived(installmentChargeData.isWaived())
.amountAccrued(installmentChargeData.getAmountAccrued()).amountUnrecognized(amountUnrecognized).build();
}
}
@Override
public Collection<LoanChargePaidByData> retrieveLoanChargesPaidBy(Long chargeId, final LoanTransactionType transactionType,
final Integer installmentNumber) {
LoanChargesPaidByMapper rm = new LoanChargesPaidByMapper();
StringBuilder sb = new StringBuilder(100);
sb.append("select ");
sb.append(rm.schema());
sb.append(" where lcp.loan_charge_id = ?");
List<Object> args = new ArrayList<>(3);
args.add(chargeId);
if (transactionType != null) {
sb.append(" and lt.transaction_type_enum = ?");
args.add(transactionType.getValue());
}
if (installmentNumber != null) {
sb.append(" and lcp.installment_number = ?");
args.add(installmentNumber);
}
return this.jdbcTemplate.query(sb.toString(), rm, args.toArray());
}
@Override
public Long retrieveLoanChargeIdByExternalId(ExternalId externalId) {
return loanChargeRepository.findIdByExternalId(externalId);
}
private static final class LoanChargesPaidByMapper implements RowMapper<LoanChargePaidByData> {
private final String schemaSql;
LoanChargesPaidByMapper() {
StringBuilder sb = new StringBuilder(100);
sb.append("lcp.id as id, lcp.loan_charge_id as chargeId, ");
sb.append("lcp.amount as amount, ");
sb.append("lcp.loan_transaction_id as transactionId, ");
sb.append("lcp.installment_number as installmentNumber ");
sb.append(" from m_loan_charge_paid_by lcp ");
sb.append(" join m_loan_transaction lt on lt.id = lcp.loan_transaction_id and lt.is_reversed=false");
schemaSql = sb.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public LoanChargePaidByData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId");
final BigDecimal amount = rs.getBigDecimal("amount");
final Long transactionId = rs.getLong("transactionId");
final Integer installmentNumber = rs.getInt("installmentNumber");
return new LoanChargePaidByData(id, amount, installmentNumber, chargeId, transactionId);
}
}
}