| /** |
| * 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); |
| } |
| } |
| |
| } |