| /** |
| * 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.loanproduct.service; |
| |
| import java.math.BigDecimal; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| |
| import org.apache.fineract.accounting.common.AccountingEnumerations; |
| import org.apache.fineract.infrastructure.core.data.EnumOptionData; |
| import org.apache.fineract.infrastructure.core.domain.JdbcSupport; |
| import org.apache.fineract.infrastructure.core.service.DateUtils; |
| import org.apache.fineract.infrastructure.core.service.RoutingDataSource; |
| import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityType; |
| import org.apache.fineract.infrastructure.entityaccess.service.FineractEntityAccessUtil; |
| import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; |
| import org.apache.fineract.organisation.monetary.data.CurrencyData; |
| import org.apache.fineract.portfolio.charge.data.ChargeData; |
| import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; |
| import org.apache.fineract.portfolio.common.service.CommonEnumerations; |
| import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData; |
| import org.apache.fineract.portfolio.loanproduct.data.LoanProductData; |
| import org.apache.fineract.portfolio.loanproduct.data.LoanProductGuaranteeData; |
| import org.apache.fineract.portfolio.loanproduct.data.LoanProductInterestRecalculationData; |
| import org.apache.fineract.portfolio.loanproduct.domain.LoanProductConfigurableAttributes; |
| import org.apache.fineract.portfolio.loanproduct.domain.LoanProductParamType; |
| import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException; |
| import org.joda.time.LocalDate; |
| import org.springframework.beans.factory.annotation.Autowired; |
| import org.springframework.dao.EmptyResultDataAccessException; |
| import org.springframework.jdbc.core.JdbcTemplate; |
| import org.springframework.jdbc.core.RowMapper; |
| import org.springframework.stereotype.Service; |
| |
| @Service |
| public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatformService { |
| |
| private final PlatformSecurityContext context; |
| private final JdbcTemplate jdbcTemplate; |
| private final ChargeReadPlatformService chargeReadPlatformService; |
| private final FineractEntityAccessUtil fineractEntityAccessUtil; |
| |
| @Autowired |
| public LoanProductReadPlatformServiceImpl(final PlatformSecurityContext context, |
| final ChargeReadPlatformService chargeReadPlatformService, final RoutingDataSource dataSource, |
| final FineractEntityAccessUtil fineractEntityAccessUtil) { |
| this.context = context; |
| this.chargeReadPlatformService = chargeReadPlatformService; |
| this.jdbcTemplate = new JdbcTemplate(dataSource); |
| this.fineractEntityAccessUtil = fineractEntityAccessUtil; |
| } |
| |
| @Override |
| public LoanProductData retrieveLoanProduct(final Long loanProductId) { |
| |
| try { |
| final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProductId); |
| final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas = retrieveLoanProductBorrowerCycleVariations(loanProductId); |
| final LoanProductMapper rm = new LoanProductMapper(charges, borrowerCycleVariationDatas); |
| final String sql = "select " + rm.loanProductSchema() + " where lp.id = ?"; |
| |
| return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanProductId }); |
| |
| } catch (final EmptyResultDataAccessException e) { |
| throw new LoanProductNotFoundException(loanProductId); |
| } |
| } |
| |
| @Override |
| public Collection<LoanProductBorrowerCycleVariationData> retrieveLoanProductBorrowerCycleVariations(final Long loanProductId) { |
| final LoanProductBorrowerCycleMapper rm = new LoanProductBorrowerCycleMapper(); |
| final String sql = "select " + rm.schema() + " where bc.loan_product_id=? order by bc.borrower_cycle_number,bc.value_condition"; |
| return this.jdbcTemplate.query(sql, rm, new Object[] { loanProductId }); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAllLoanProducts() { |
| |
| this.context.authenticatedUser(); |
| |
| final LoanProductMapper rm = new LoanProductMapper(null, null); |
| |
| String sql = "select " + rm.loanProductSchema(); |
| |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause != null) && (!(inClause.trim().isEmpty()))) { |
| sql += " where lp.id in ( " + inClause + " ) "; |
| } |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] {}); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAllLoanProductsForLookup(String inClause) { |
| |
| this.context.authenticatedUser(); |
| |
| final LoanProductLookupMapper rm = new LoanProductLookupMapper(); |
| |
| String sql = "select " + rm.schema(); |
| |
| if ((inClause != null) && (!(inClause.trim().isEmpty()))) { |
| sql += " where lp.id in ( " + inClause + " ) "; |
| } |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] {}); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAllLoanProductsForLookup() { |
| return retrieveAllLoanProductsForLookup(false); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAllLoanProductsForLookup(final boolean activeOnly) { |
| this.context.authenticatedUser(); |
| |
| final LoanProductLookupMapper rm = new LoanProductLookupMapper(); |
| |
| String sql = "select "; |
| if (activeOnly) { |
| sql += rm.activeOnlySchema(); |
| } else { |
| sql += rm.schema(); |
| } |
| |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause != null) && (!(inClause.trim().isEmpty()))) { |
| if (activeOnly) { |
| sql += " and id in ( " + inClause + " )"; |
| } else { |
| sql += " where id in ( " + inClause + " ) "; |
| } |
| } |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] {}); |
| } |
| |
| @Override |
| public LoanProductData retrieveNewLoanProductDetails() { |
| return LoanProductData.sensibleDefaultsForNewLoanProductCreation(); |
| } |
| |
| private static final class LoanProductMapper implements RowMapper<LoanProductData> { |
| |
| private final Collection<ChargeData> charges; |
| |
| private final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas; |
| |
| public LoanProductMapper(final Collection<ChargeData> charges, |
| final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas) { |
| this.charges = charges; |
| this.borrowerCycleVariationDatas = borrowerCycleVariationDatas; |
| } |
| |
| public String loanProductSchema() { |
| return "lp.id as id, lp.fund_id as fundId, f.name as fundName, lp.loan_transaction_strategy_id as transactionStrategyId, ltps.name as transactionStrategyName, " |
| + "lp.name as name, lp.short_name as shortName, lp.description as description, " |
| + "lp.principal_amount as principal, lp.min_principal_amount as minPrincipal, lp.max_principal_amount as maxPrincipal, lp.currency_code as currencyCode, lp.currency_digits as currencyDigits, lp.currency_multiplesof as inMultiplesOf, " |
| + "lp.nominal_interest_rate_per_period as interestRatePerPeriod, lp.min_nominal_interest_rate_per_period as minInterestRatePerPeriod, lp.max_nominal_interest_rate_per_period as maxInterestRatePerPeriod, lp.interest_period_frequency_enum as interestRatePerPeriodFreq, " |
| + "lp.annual_nominal_interest_rate as annualInterestRate, lp.interest_method_enum as interestMethod, lp.interest_calculated_in_period_enum as interestCalculationInPeriodMethod,lp.allow_partial_period_interest_calcualtion as allowPartialPeriodInterestCalcualtion, " |
| + "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, " |
| + "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,lp.grace_on_arrears_ageing as graceOnArrearsAgeing,lp.overdue_days_for_npa as overdueDaysForNPA, " |
| + "lp.min_days_between_disbursal_and_first_repayment As minimumDaysBetweenDisbursalAndFirstRepayment, " |
| + "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, " |
| + "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, " |
| + "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, " |
| + "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, " |
| + "lp.can_define_fixed_emi_amount as canDefineInstallmentAmount, lp.instalment_amount_in_multiples_of as installmentAmountInMultiplesOf, " |
| + "lpr.pre_close_interest_calculation_strategy as preCloseInterestCalculationStrategy, " |
| + "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, " |
| + "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, " |
| + "lpr.rest_freqency_date as restFrequencyDate, lpr.arrears_based_on_original_schedule as isArrearsBasedOnOriginalSchedule, " |
| + "lpr.compounding_frequency_type_enum as compoundingFrequencyTypeEnum, lpr.compounding_frequency_interval as compoundingInterval, " |
| + "lpr.compounding_freqency_date as compoundingFrequencyDate, " |
| + "lp.hold_guarantee_funds as holdGuaranteeFunds, " |
| + "lp.principal_threshold_for_last_installment as principalThresholdForLastInstallment, " |
| + "lpg.id as lpgId, lpg.mandatory_guarantee as mandatoryGuarantee, " |
| + "lpg.minimum_guarantee_from_own_funds as minimumGuaranteeFromOwnFunds, lpg.minimum_guarantee_from_guarantor_funds as minimumGuaranteeFromGuarantor, " |
| + "lp.account_moves_out_of_npa_only_on_arrears_completion as accountMovesOutOfNPAOnlyOnArrearsCompletion, " |
| + "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId, " |
| + "lca.id as lcaId, lca.amortization_method_enum as amortizationBoolean, lca.interest_method_enum as interestMethodConfigBoolean, " |
| + "lca.loan_transaction_strategy_id as transactionProcessingStrategyBoolean,lca.interest_calculated_in_period_enum as interestCalcPeriodBoolean, lca.arrearstolerance_amount as arrearsToleranceBoolean, " |
| + "lca.repay_every as repaymentFrequencyBoolean, lca.moratorium as graceOnPrincipalAndInterestBoolean, lca.grace_on_arrears_ageing as graceOnArrearsAgingBoolean, " |
| + "lp.is_linked_to_floating_interest_rates as isLinkedToFloatingInterestRates, " |
| + "lfr.floating_rates_id as floatingRateId, " |
| + "fr.name as floatingRateName, " |
| + "lfr.interest_rate_differential as interestRateDifferential, " |
| + "lfr.min_differential_lending_rate as minDifferentialLendingRate, " |
| + "lfr.default_differential_lending_rate as defaultDifferentialLendingRate, " |
| + "lfr.max_differential_lending_rate as maxDifferentialLendingRate, " |
| + "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed, " |
| + "lp.allow_variabe_installments as isVariableIntallmentsAllowed, " |
| + "lvi.minimum_gap as minimumGap, " |
| + "lvi.maximum_gap as maximumGap " |
| + " from m_product_loan lp " |
| + " left join m_fund f on f.id = lp.fund_id " |
| + " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id " |
| + " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id " |
| + " left join ref_loan_transaction_processing_strategy ltps on ltps.id = lp.loan_transaction_strategy_id" |
| + " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id " |
| + " left join m_product_loan_floating_rates as lfr on lfr.loan_product_id = lp.id " |
| + " left join m_floating_rates as fr on lfr.floating_rates_id = fr.id " |
| + " left join m_product_loan_variable_installment_config as lvi on lvi.loan_product_id = lp.id " |
| + " join m_currency curr on curr.code = lp.currency_code"; |
| |
| } |
| |
| @Override |
| public LoanProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { |
| |
| final Long id = JdbcSupport.getLong(rs, "id"); |
| final String name = rs.getString("name"); |
| final String shortName = rs.getString("shortName"); |
| final String description = rs.getString("description"); |
| final Long fundId = JdbcSupport.getLong(rs, "fundId"); |
| final String fundName = rs.getString("fundName"); |
| final Long transactionStrategyId = JdbcSupport.getLong(rs, "transactionStrategyId"); |
| final String transactionStrategyName = rs.getString("transactionStrategyName"); |
| |
| 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 currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); |
| final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); |
| |
| final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, |
| currencyDisplaySymbol, currencyNameCode); |
| |
| final BigDecimal principal = rs.getBigDecimal("principal"); |
| final BigDecimal minPrincipal = rs.getBigDecimal("minPrincipal"); |
| final BigDecimal maxPrincipal = rs.getBigDecimal("maxPrincipal"); |
| final BigDecimal tolerance = rs.getBigDecimal("tolerance"); |
| |
| final Integer numberOfRepayments = JdbcSupport.getInteger(rs, "numberOfRepayments"); |
| final Integer minNumberOfRepayments = JdbcSupport.getInteger(rs, "minNumberOfRepayments"); |
| final Integer maxNumberOfRepayments = JdbcSupport.getInteger(rs, "maxNumberOfRepayments"); |
| final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaidEvery"); |
| |
| final Integer graceOnPrincipalPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnPrincipalPayment"); |
| final Integer graceOnInterestPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestPayment"); |
| final Integer graceOnInterestCharged = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestCharged"); |
| final Integer graceOnArrearsAgeing = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnArrearsAgeing"); |
| final Integer overdueDaysForNPA = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "overdueDaysForNPA"); |
| final Integer minimumDaysBetweenDisbursalAndFirstRepayment = JdbcSupport.getInteger(rs, |
| "minimumDaysBetweenDisbursalAndFirstRepayment"); |
| |
| final Integer accountingRuleId = JdbcSupport.getInteger(rs, "accountingType"); |
| final EnumOptionData accountingRuleType = AccountingEnumerations.accountingRuleType(accountingRuleId); |
| |
| final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod"); |
| final BigDecimal minInterestRatePerPeriod = rs.getBigDecimal("minInterestRatePerPeriod"); |
| final BigDecimal maxInterestRatePerPeriod = rs.getBigDecimal("maxInterestRatePerPeriod"); |
| final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate"); |
| |
| final boolean isLinkedToFloatingInterestRates = rs.getBoolean("isLinkedToFloatingInterestRates"); |
| final Integer floatingRateId = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "floatingRateId"); |
| final String floatingRateName = rs.getString("floatingRateName"); |
| final BigDecimal interestRateDifferential = rs.getBigDecimal("interestRateDifferential"); |
| final BigDecimal minDifferentialLendingRate = rs.getBigDecimal("minDifferentialLendingRate"); |
| final BigDecimal defaultDifferentialLendingRate = rs.getBigDecimal("defaultDifferentialLendingRate"); |
| final BigDecimal maxDifferentialLendingRate = rs.getBigDecimal("maxDifferentialLendingRate"); |
| final boolean isFloatingInterestRateCalculationAllowed = rs.getBoolean("isFloatingInterestRateCalculationAllowed"); |
| |
| final boolean isVariableIntallmentsAllowed = rs.getBoolean("isVariableIntallmentsAllowed"); |
| final Integer minimumGap = rs.getInt("minimumGap"); |
| final Integer maximumGap = rs.getInt("maximumGap"); |
| |
| final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency"); |
| final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId); |
| |
| final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod"); |
| final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId); |
| |
| final Integer interestRateFrequencyTypeId = JdbcSupport.getInteger(rs, "interestRatePerPeriodFreq"); |
| final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeId); |
| |
| final int interestTypeId = JdbcSupport.getInteger(rs, "interestMethod"); |
| final EnumOptionData interestType = LoanEnumerations.interestType(interestTypeId); |
| |
| final int interestCalculationPeriodTypeId = JdbcSupport.getInteger(rs, "interestCalculationInPeriodMethod"); |
| final Boolean allowPartialPeriodInterestCalcualtion = rs.getBoolean("allowPartialPeriodInterestCalcualtion"); |
| final EnumOptionData interestCalculationPeriodType = LoanEnumerations |
| .interestCalculationPeriodType(interestCalculationPeriodTypeId); |
| |
| final boolean includeInBorrowerCycle = rs.getBoolean("includeInBorrowerCycle"); |
| final boolean useBorrowerCycle = rs.getBoolean("useBorrowerCycle"); |
| final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate"); |
| final LocalDate closeDate = JdbcSupport.getLocalDate(rs, "closeDate"); |
| String status = ""; |
| if (closeDate != null && closeDate.isBefore(DateUtils.getLocalDateOfTenant())) { |
| status = "loanProduct.inActive"; |
| } else { |
| status = "loanProduct.active"; |
| } |
| final String externalId = rs.getString("externalId"); |
| final Collection<LoanProductBorrowerCycleVariationData> principalVariationsForBorrowerCycle = new ArrayList<>(); |
| final Collection<LoanProductBorrowerCycleVariationData> interestRateVariationsForBorrowerCycle = new ArrayList<>(); |
| final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle = new ArrayList<>(); |
| if (this.borrowerCycleVariationDatas != null) { |
| for (final LoanProductBorrowerCycleVariationData borrowerCycleVariationData : this.borrowerCycleVariationDatas) { |
| final LoanProductParamType loanProductParamType = borrowerCycleVariationData.getParamType(); |
| if (loanProductParamType.isParamTypePrincipal()) { |
| principalVariationsForBorrowerCycle.add(borrowerCycleVariationData); |
| } else if (loanProductParamType.isParamTypeInterestTate()) { |
| interestRateVariationsForBorrowerCycle.add(borrowerCycleVariationData); |
| } else if (loanProductParamType.isParamTypeRepayment()) { |
| numberOfRepaymentVariationsForBorrowerCycle.add(borrowerCycleVariationData); |
| } |
| } |
| } |
| |
| final Boolean multiDisburseLoan = rs.getBoolean("multiDisburseLoan"); |
| final Integer maxTrancheCount = rs.getInt("maxTrancheCount"); |
| final BigDecimal outstandingLoanBalance = rs.getBigDecimal("outstandingLoanBalance"); |
| |
| final int daysInMonth = JdbcSupport.getInteger(rs, "daysInMonth"); |
| final EnumOptionData daysInMonthType = CommonEnumerations.daysInMonthType(daysInMonth); |
| final int daysInYear = JdbcSupport.getInteger(rs, "daysInYear"); |
| final EnumOptionData daysInYearType = CommonEnumerations.daysInYearType(daysInYear); |
| final Integer installmentAmountInMultiplesOf = JdbcSupport.getInteger(rs, "installmentAmountInMultiplesOf"); |
| final boolean canDefineInstallmentAmount = rs.getBoolean("canDefineInstallmentAmount"); |
| final boolean isInterestRecalculationEnabled = rs.getBoolean("isInterestRecalculationEnabled"); |
| |
| LoanProductInterestRecalculationData interestRecalculationData = null; |
| if (isInterestRecalculationEnabled) { |
| |
| final Long lprId = JdbcSupport.getLong(rs, "lprId"); |
| final Long productId = JdbcSupport.getLong(rs, "productId"); |
| final int compoundTypeEnumValue = JdbcSupport.getInteger(rs, "compoundType"); |
| final EnumOptionData interestRecalculationCompoundingType = LoanEnumerations |
| .interestRecalculationCompoundingType(compoundTypeEnumValue); |
| final int rescheduleStrategyEnumValue = JdbcSupport.getInteger(rs, "rescheduleStrategy"); |
| final EnumOptionData rescheduleStrategyType = LoanEnumerations.rescheduleStrategyType(rescheduleStrategyEnumValue); |
| final int restFrequencyEnumValue = JdbcSupport.getInteger(rs, "restFrequencyEnum"); |
| final EnumOptionData restFrequencyType = LoanEnumerations.interestRecalculationFrequencyType(restFrequencyEnumValue); |
| final int restFrequencyInterval = JdbcSupport.getInteger(rs, "restFrequencyInterval"); |
| final LocalDate restFrequencyDate = JdbcSupport.getLocalDate(rs, "restFrequencyDate"); |
| final Integer compoundingFrequencyEnumValue = JdbcSupport.getInteger(rs, "compoundingFrequencyTypeEnum"); |
| EnumOptionData compoundingFrequencyType = null; |
| if (compoundingFrequencyEnumValue != null) { |
| compoundingFrequencyType = LoanEnumerations.interestRecalculationFrequencyType(compoundingFrequencyEnumValue); |
| } |
| final Integer compoundingInterval = JdbcSupport.getInteger(rs, "compoundingInterval"); |
| final LocalDate compoundingFrequencyDate = JdbcSupport.getLocalDate(rs, "compoundingFrequencyDate"); |
| final boolean isArrearsBasedOnOriginalSchedule = rs.getBoolean("isArrearsBasedOnOriginalSchedule"); |
| final int preCloseInterestCalculationStrategyEnumValue = JdbcSupport.getInteger(rs, "preCloseInterestCalculationStrategy"); |
| final EnumOptionData preCloseInterestCalculationStrategy = LoanEnumerations |
| .preCloseInterestCalculationStrategy(preCloseInterestCalculationStrategyEnumValue); |
| |
| interestRecalculationData = new LoanProductInterestRecalculationData(lprId, productId, |
| interestRecalculationCompoundingType, rescheduleStrategyType, restFrequencyType, restFrequencyInterval, |
| restFrequencyDate, compoundingFrequencyType, compoundingInterval, compoundingFrequencyDate, |
| isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy); |
| } |
| |
| final boolean amortization = rs.getBoolean("amortizationBoolean"); |
| final boolean interestMethod = rs.getBoolean("interestMethodConfigBoolean"); |
| final boolean transactionProcessingStrategy = rs.getBoolean("transactionProcessingStrategyBoolean"); |
| final boolean interestCalcPeriod = rs.getBoolean("interestCalcPeriodBoolean"); |
| final boolean arrearsTolerance = rs.getBoolean("arrearsToleranceBoolean"); |
| final boolean repaymentFrequency = rs.getBoolean("repaymentFrequencyBoolean"); |
| final boolean graceOnPrincipalAndInterest = rs.getBoolean("graceOnPrincipalAndInterestBoolean"); |
| final boolean graceOnArrearsAging = rs.getBoolean("graceOnArrearsAgingBoolean"); |
| |
| LoanProductConfigurableAttributes allowAttributeOverrides = null; |
| |
| allowAttributeOverrides = new LoanProductConfigurableAttributes(amortization, interestMethod, transactionProcessingStrategy, |
| interestCalcPeriod, arrearsTolerance, repaymentFrequency, graceOnPrincipalAndInterest, graceOnArrearsAging); |
| |
| final boolean holdGuaranteeFunds = rs.getBoolean("holdGuaranteeFunds"); |
| LoanProductGuaranteeData loanProductGuaranteeData = null; |
| if (holdGuaranteeFunds) { |
| final Long lpgId = JdbcSupport.getLong(rs, "lpgId"); |
| final BigDecimal mandatoryGuarantee = rs.getBigDecimal("mandatoryGuarantee"); |
| final BigDecimal minimumGuaranteeFromOwnFunds = rs.getBigDecimal("minimumGuaranteeFromOwnFunds"); |
| final BigDecimal minimumGuaranteeFromGuarantor = rs.getBigDecimal("minimumGuaranteeFromGuarantor"); |
| loanProductGuaranteeData = LoanProductGuaranteeData.instance(lpgId, id, mandatoryGuarantee, minimumGuaranteeFromOwnFunds, |
| minimumGuaranteeFromGuarantor); |
| } |
| |
| final BigDecimal principalThresholdForLastInstallment = rs.getBigDecimal("principalThresholdForLastInstallment"); |
| final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = rs.getBoolean("accountMovesOutOfNPAOnlyOnArrearsCompletion"); |
| |
| return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, |
| numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, |
| minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, |
| interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, |
| allowPartialPeriodInterestCalcualtion, fundId, fundName, transactionStrategyId, transactionStrategyName, |
| graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, this.charges, accountingRuleType, |
| includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, |
| principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, |
| numberOfRepaymentVariationsForBorrowerCycle, multiDisburseLoan, maxTrancheCount, outstandingLoanBalance, |
| graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, |
| interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData, |
| principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, |
| installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId, |
| floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, |
| maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap, |
| maximumGap); |
| } |
| } |
| |
| private static final class LoanProductLookupMapper implements RowMapper<LoanProductData> { |
| |
| public String schema() { |
| return "lp.id as id, lp.name as name from m_product_loan lp"; |
| } |
| |
| public String activeOnlySchema() { |
| return schema() + " where (close_date is null or close_date >= CURDATE())"; |
| } |
| |
| public String productMixSchema() { |
| return "lp.id as id, lp.name as name FROM m_product_loan lp left join m_product_mix pm on pm.product_id=lp.id where lp.id not IN(" |
| + "select lp.id from m_product_loan lp inner join m_product_mix pm on pm.product_id=lp.id)"; |
| } |
| |
| public String restrictedProductsSchema() { |
| return "pm.restricted_product_id as id, rp.name as name from m_product_mix pm join m_product_loan rp on rp.id = pm.restricted_product_id "; |
| } |
| |
| public String derivedRestrictedProductsSchema() { |
| return "pm.product_id as id, lp.name as name from m_product_mix pm join m_product_loan lp on lp.id=pm.product_id"; |
| } |
| |
| @Override |
| public LoanProductData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { |
| |
| final Long id = rs.getLong("id"); |
| final String name = rs.getString("name"); |
| |
| return LoanProductData.lookup(id, name); |
| } |
| } |
| |
| private static final class LoanProductBorrowerCycleMapper implements RowMapper<LoanProductBorrowerCycleVariationData> { |
| |
| public String schema() { |
| return "bc.id as id,bc.borrower_cycle_number as cycleNumber,bc.value_condition as conditionType,bc.param_type as paramType," |
| + "bc.default_value as defaultValue,bc.max_value as maxVal,bc.min_value as minVal " |
| + "from m_product_loan_variations_borrower_cycle bc"; |
| } |
| |
| @Override |
| public LoanProductBorrowerCycleVariationData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) |
| throws SQLException { |
| final Long id = rs.getLong("id"); |
| final Integer cycleNumber = JdbcSupport.getInteger(rs, "cycleNumber"); |
| final Integer conditionType = JdbcSupport.getInteger(rs, "conditionType"); |
| final EnumOptionData conditionTypeData = LoanEnumerations.loanCycleValueConditionType(conditionType); |
| final Integer paramType = JdbcSupport.getInteger(rs, "paramType"); |
| final EnumOptionData paramTypeData = LoanEnumerations.loanCycleParamType(paramType); |
| final BigDecimal defaultValue = rs.getBigDecimal("defaultValue"); |
| final BigDecimal maxValue = rs.getBigDecimal("maxVal"); |
| final BigDecimal minValue = rs.getBigDecimal("minVal"); |
| |
| final LoanProductBorrowerCycleVariationData borrowerCycleVariationData = new LoanProductBorrowerCycleVariationData(id, |
| cycleNumber, paramTypeData, conditionTypeData, defaultValue, minValue, maxValue); |
| return borrowerCycleVariationData; |
| } |
| |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAllLoanProductsForCurrency(String currencyCode) { |
| this.context.authenticatedUser(); |
| |
| final LoanProductMapper rm = new LoanProductMapper(null, null); |
| |
| String sql = "select " + rm.loanProductSchema() + " where lp.currency_code='" + currencyCode + "'"; |
| |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause != null) && (!(inClause.trim().isEmpty()))) { |
| sql += " and id in ( " + inClause + " ) "; |
| } |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] {}); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAvailableLoanProductsForMix() { |
| |
| this.context.authenticatedUser(); |
| |
| final LoanProductLookupMapper rm = new LoanProductLookupMapper(); |
| |
| String sql = "Select " + rm.productMixSchema(); |
| |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause != null) && (!(inClause.trim().isEmpty()))) { |
| sql += " and lp.id in ( " + inClause + " ) "; |
| } |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] {}); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveRestrictedProductsForMix(final Long productId) { |
| |
| this.context.authenticatedUser(); |
| |
| final LoanProductLookupMapper rm = new LoanProductLookupMapper(); |
| |
| String sql = "Select " + rm.restrictedProductsSchema() + " where pm.product_id=? "; |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause1 = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause1 != null) && (!(inClause1.trim().isEmpty()))) { |
| sql += " and rp.id in ( " + inClause1 + " ) "; |
| } |
| |
| sql += " UNION Select " + rm.derivedRestrictedProductsSchema() + " where pm.restricted_product_id=?"; |
| |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause2 = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause2 != null) && (!(inClause2.trim().isEmpty()))) { |
| sql += " and lp.id in ( " + inClause2 + " ) "; |
| } |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId }); |
| } |
| |
| @Override |
| public Collection<LoanProductData> retrieveAllowedProductsForMix(final Long productId) { |
| |
| this.context.authenticatedUser(); |
| |
| final LoanProductLookupMapper rm = new LoanProductLookupMapper(); |
| |
| String sql = "Select " + rm.schema() + " where "; |
| |
| // Check if branch specific products are enabled. If yes, fetch only |
| // products mapped to current user's office |
| String inClause = fineractEntityAccessUtil |
| .getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.LOAN_PRODUCT); |
| if ((inClause != null) && (!(inClause.trim().isEmpty()))) { |
| sql += " lp.id in ( " + inClause + " ) and "; |
| } |
| |
| sql += "lp.id not in (" + "Select pm.restricted_product_id from m_product_mix pm where pm.product_id=? " + "UNION " |
| + "Select pm.product_id from m_product_mix pm where pm.restricted_product_id=?)"; |
| |
| return this.jdbcTemplate.query(sql, rm, new Object[] { productId, productId }); |
| } |
| |
| } |