diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java index 43680cb5b75..5e953ff27be 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java @@ -29,6 +29,7 @@ import lombok.Setter; import lombok.experimental.Accessors; import org.apache.fineract.accounting.glaccount.domain.GLAccount; +import org.apache.fineract.infrastructure.codes.domain.CodeValue; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.paymenttype.domain.PaymentType; @@ -63,13 +64,14 @@ public class ProductToGLAccountMapping extends AbstractPersistableCustom { @Column(name = "financial_account_type", nullable = true) private int financialAccountType; - @Column(name = "charge_off_reason_id", nullable = true) - private Long chargeOffReasonId; + @ManyToOne + @JoinColumn(name = "charge_off_reason_id", nullable = true) + private CodeValue chargeOffReason; public static ProductToGLAccountMapping createNew(final GLAccount glAccount, final Long productId, final int productType, - final int financialAccountType, final Long chargeOffReasonId) { + final int financialAccountType, final CodeValue chargeOffReasonId) { return new ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType) - .setFinancialAccountType(financialAccountType).setChargeOffReasonId(chargeOffReasonId); + .setFinancialAccountType(financialAccountType).setChargeOffReason(chargeOffReasonId); } } diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java index acd32ad33a6..b10f8ff84f3 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java @@ -35,7 +35,7 @@ ProductToGLAccountMapping findProductIdAndProductTypeAndFinancialAccountTypeAndC @Param("productType") int productType, @Param("financialAccountType") int financialAccountType, @Param("chargeId") Long ChargeId); - @Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL and mapping.charge is NULL and mapping.chargeOffReasonId is NULL") + @Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL and mapping.charge is NULL and mapping.chargeOffReason.id is NULL") ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType, @Param("financialAccountType") int financialAccountType); @@ -62,10 +62,20 @@ List findAllPenaltyToIncomeAccountMappings(@Param("pr List findByProductIdAndProductType(Long productId, int productType); - @Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.chargeOffReasonId is not NULL") + @Query("select mapping from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.chargeOffReason.id is not NULL") List findAllChargesOffReasonsMappings(@Param("productId") Long productId, @Param("productType") int productType); - @Query("select mapping from ProductToGLAccountMapping mapping where mapping.chargeOffReasonId =:chargeOffReasonId") + @Query("select mapping from ProductToGLAccountMapping mapping where mapping.chargeOffReason.id =:chargeOffReasonId") ProductToGLAccountMapping findChargesOffReasonMappingById(@Param("chargeOffReasonId") Integer chargeOffReasonId); + + List findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(Long productId, + Integer productType); + + List findAllByProductIdAndProductTypeAndChargeOffReasonIdIsNotNull(Long productId, Integer productType); + + List findAllByProductIdAndProductTypeAndPaymentTypeIsNotNull(Long productId, Integer productType); + + List findAllByProductIdAndProductTypeAndCharge_PenaltyAndCharge_IdIsNotNull(Long productId, + Integer productType, boolean isPenalty); } diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java index 5c01b16706b..34b9e06d762 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java @@ -414,7 +414,7 @@ public void updateChargeOffReasonToGLAccountMappings(final JsonCommand command, this.accountMappingRepository.deleteAllInBatch(existingChargeOffReasonToGLAccountMappings); } else { for (final ProductToGLAccountMapping existingChargeOffReasonToGLAccountMapping : existingChargeOffReasonToGLAccountMappings) { - final Long currentChargeOffReasonId = existingChargeOffReasonToGLAccountMapping.getChargeOffReasonId(); + final Long currentChargeOffReasonId = existingChargeOffReasonToGLAccountMapping.getChargeOffReason().getId(); if (currentChargeOffReasonId != null) { existingChargeOffReasons.add(currentChargeOffReasonId); // update existing mappings (if required) @@ -496,12 +496,14 @@ private void saveChargeOffReasonToExpenseMapping(final Long productId, final Lon final boolean reasonMappingExists = this.accountMappingRepository .findAllChargesOffReasonsMappings(productId, portfolioProductType.getValue()).stream() - .anyMatch(mapping -> mapping.getChargeOffReasonId().equals(reasonId)); + .anyMatch(mapping -> mapping.getChargeOffReason().getId().equals(reasonId)); - if (glAccount.isPresent() && !reasonMappingExists) { + final Optional codeValueOptional = codeValueRepository.findById(reasonId); + + if (glAccount.isPresent() && !reasonMappingExists && codeValueOptional.isPresent()) { final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping().setGlAccount(glAccount.get()) .setProductId(productId).setProductType(portfolioProductType.getValue()) - .setFinancialAccountType(CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue()).setChargeOffReasonId(reasonId); + .setFinancialAccountType(CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue()).setChargeOffReason(codeValueOptional.get()); this.accountMappingRepository.saveAndFlush(accountMapping); } diff --git a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java index c880fd6db9f..fd3cbfd8543 100644 --- a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java +++ b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java @@ -18,8 +18,6 @@ */ package org.apache.fineract.accounting.producttoaccountmapping.service; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -40,13 +38,13 @@ import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeOffReasonToGLAccountMapper; import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; import org.apache.fineract.accounting.producttoaccountmapping.data.PaymentTypeToGLAccountMapper; +import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping; +import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; import org.apache.fineract.infrastructure.codes.data.CodeValueData; -import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; @Slf4j @@ -56,86 +54,24 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product private final JdbcTemplate jdbcTemplate; - private static final class ProductToGLAccountMappingMapper implements RowMapper> { - - public String schema() { - return " mapping.id as id, mapping.gl_account_id as glAccountId,glaccount.name as name,glaccount.gl_code as code," - + " mapping.product_id as productId, mapping.product_type as productType,mapping.financial_account_type as financialAccountType, " - + " mapping.payment_type as paymentTypeId,pt.value as paymentTypeValue, mapping.charge_id as chargeId, mapping.charge_off_reason_id as chargeOffReasonId, charge.is_penalty as penalty, " - + " charge.name as chargeName, codeValue.code_value as codeValueName, codeValue.code_description as codeDescription, codeValue.order_position as orderPosition, codeValue.is_active as isActive, codeValue.is_mandatory as isMandatory " - + " from acc_product_mapping mapping left join m_charge charge on mapping.charge_id=charge.id " - + " left join acc_gl_account as glaccount on mapping.gl_account_id = glaccount.id " - + " left join m_code_value as codeValue on mapping.charge_off_reason_id = codeValue.id " - + " left join m_payment_type pt on mapping.payment_type=pt.id" + " where mapping.product_type= ? "; - } - - @Override - public Map mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = rs.getLong("id"); - final Long glAccountId = rs.getLong("glAccountId"); - final Long productId = rs.getLong("productId"); - final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentTypeId"); - final Long chargeId = rs.getLong("chargeId"); - final Integer productType = rs.getInt("productType"); - final String paymentTypeValue = rs.getString("paymentTypeValue"); - final Integer financialAccountType = rs.getInt("financialAccountType"); - final String glAccountName = rs.getString("name"); - final String glCode = rs.getString("code"); - final String chargeName = rs.getString("chargeName"); - final Boolean penalty = rs.getBoolean("penalty"); - final Integer chargeOffReasonId = rs.getInt("chargeOffReasonId"); - final String codeValue = rs.getString("codeValueName"); - final String codeDescription = rs.getString("codeDescription"); - final Integer orderPosition = rs.getInt("orderPosition"); - final Integer isActive = rs.getInt("isActive"); - final Integer isMandatory = rs.getInt("isMandatory"); - - final Map loanProductToGLAccountMap = new LinkedHashMap<>(5); - loanProductToGLAccountMap.put("id", id); - loanProductToGLAccountMap.put("glAccountId", glAccountId); - loanProductToGLAccountMap.put("productId", productId); - loanProductToGLAccountMap.put("productType", productType); - loanProductToGLAccountMap.put("financialAccountType", financialAccountType); - loanProductToGLAccountMap.put("paymentTypeId", paymentTypeId); - loanProductToGLAccountMap.put("paymentTypeValue", paymentTypeValue); - loanProductToGLAccountMap.put("chargeId", chargeId); - loanProductToGLAccountMap.put("chargeName", chargeName); - loanProductToGLAccountMap.put("penalty", penalty); - loanProductToGLAccountMap.put("glAccountName", glAccountName); - loanProductToGLAccountMap.put("glCode", glCode); - loanProductToGLAccountMap.put("chargeOffReasonId", chargeOffReasonId); - loanProductToGLAccountMap.put("codeValue", codeValue); - loanProductToGLAccountMap.put("codeDescription", codeDescription); - loanProductToGLAccountMap.put("orderPosition", orderPosition); - loanProductToGLAccountMap.put("isActive", isActive); - loanProductToGLAccountMap.put("isMandatory", isMandatory); - return loanProductToGLAccountMap; - } - } + private final ProductToGLAccountMappingRepository productToGLAccountMappingRepository; @Override public Map fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType) { final Map accountMappingDetails = new LinkedHashMap<>(8); - final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); - final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null"; - - final List> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR - new Object[] { PortfolioProductType.LOAN.getValue(), loanProductId }); + final List mappings = productToGLAccountMappingRepository + .findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(loanProductId, PortfolioProductType.LOAN.getValue()); if (AccountingValidations.isCashBasedAccounting(accountingType)) { - for (final Map productToGLAccountMap : listOfProductToGLAccountMaps) { + for (final ProductToGLAccountMapping mapping : mappings) { - final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); - final CashAccountsForLoan glAccountForLoan = CashAccountsForLoan.fromInt(financialAccountType); + final CashAccountsForLoan glAccountForLoan = CashAccountsForLoan.fromInt(mapping.getFinancialAccountType()); - final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); - final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); - final String glCode = (String) productToGLAccountMap.get("glCode"); - final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); + final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); if (glAccountForLoan.equals(CashAccountsForLoan.FUND_SOURCE)) { accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData); @@ -181,14 +117,11 @@ public Map fetchAccountMappingDetailsForLoanProduct(final Long l } else if (AccountingValidations.isAccrualBasedAccounting(accountingType) || AccountingValidations.isUpfrontAccrualAccounting(accountingType)) { - for (final Map productToGLAccountMap : listOfProductToGLAccountMaps) { - final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); - final AccrualAccountsForLoan glAccountForLoan = AccrualAccountsForLoan.fromInt(financialAccountType); + for (ProductToGLAccountMapping mapping : mappings) { + final AccrualAccountsForLoan glAccountForLoan = AccrualAccountsForLoan.fromInt(mapping.getFinancialAccountType()); - final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); - final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); - final String glCode = (String) productToGLAccountMap.get("glCode"); - final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); + final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); if (glAccountForLoan.equals(AccrualAccountsForLoan.FUND_SOURCE)) { accountMappingDetails.put(LoanProductAccountingDataParams.FUND_SOURCE.getValue(), gLAccountData); @@ -244,18 +177,17 @@ public Map fetchAccountMappingDetailsForLoanProduct(final Long l @Override public Map fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final Integer accountingType) { - final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); - final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null "; - final List> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR - new Object[] { PortfolioProductType.SAVING.getValue(), savingsProductId }); + final List mappings = productToGLAccountMappingRepository + .findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(savingsProductId, + PortfolioProductType.SAVING.getValue()); Map accountMappingDetails = null; if (AccountingValidations.isCashBasedAccounting(accountingType)) { - accountMappingDetails = setCashSavingsProductToGLAccountMaps(listOfProductToGLAccountMaps); + accountMappingDetails = setCashSavingsProductToGLAccountMaps(mappings); } else if (AccountingValidations.isAccrualPeriodicBasedAccounting(accountingType)) { - accountMappingDetails = setAccrualPeriodicSavingsProductToGLAccountMaps(listOfProductToGLAccountMaps); + accountMappingDetails = setAccrualPeriodicSavingsProductToGLAccountMaps(mappings); } @@ -278,25 +210,18 @@ public List fetchPaymentTypeToFundSourceMappingsFo */ private List fetchPaymentTypeToFundSourceMappings(final PortfolioProductType portfolioProductType, final Long loanProductId) { - final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); - final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is not null"; - - final List> paymentTypeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR - new Object[] { portfolioProductType.getValue(), loanProductId }); + final List mappings = productToGLAccountMappingRepository + .findAllByProductIdAndProductTypeAndPaymentTypeIsNotNull(loanProductId, portfolioProductType.getValue()); List paymentTypeToGLAccountMappers = null; - for (final Map productToGLAccountMap : paymentTypeToFundSourceMappingsList) { + for (final ProductToGLAccountMapping mapping : mappings) { if (paymentTypeToGLAccountMappers == null) { paymentTypeToGLAccountMappers = new ArrayList<>(); } - final Long paymentTypeId = (Long) productToGLAccountMap.get("paymentTypeId"); - final String paymentTypeValue = (String) productToGLAccountMap.get("paymentTypeValue"); - final PaymentTypeData paymentTypeData = PaymentTypeData.instance(paymentTypeId, paymentTypeValue); - final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); - final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); - final String glCode = (String) productToGLAccountMap.get("glCode"); - - final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); + final PaymentTypeData paymentTypeData = PaymentTypeData.instance(mapping.getPaymentType().getId(), + mapping.getPaymentType().getName()); + final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); final PaymentTypeToGLAccountMapper paymentTypeToGLAccountMapper = new PaymentTypeToGLAccountMapper() .setPaymentType(paymentTypeData).setFundSourceAccount(gLAccountData); @@ -327,29 +252,18 @@ public List fetchPenaltyToIncomeAccountMappingsForSavin private List fetchChargeToIncomeAccountMappings(final PortfolioProductType portfolioProductType, final Long loanProductId, final boolean penalty) { - final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); - String sql = "select " + rm.schema() + " and product_id = ? and mapping.charge_id is not null and charge.is_penalty="; - if (penalty) { - sql = sql + " true"; - } else { - sql = sql + " false"; - } - - final List> chargeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR - new Object[] { portfolioProductType.getValue(), loanProductId }); + final List mappings = productToGLAccountMappingRepository + .findAllByProductIdAndProductTypeAndCharge_PenaltyAndCharge_IdIsNotNull(loanProductId, portfolioProductType.getValue(), + penalty); List chargeToGLAccountMappers = null; - for (final Map chargeToIncomeAccountMap : chargeToFundSourceMappingsList) { + for (final ProductToGLAccountMapping mapping : mappings) { if (chargeToGLAccountMappers == null) { chargeToGLAccountMappers = new ArrayList<>(); } - final Long glAccountId = (Long) chargeToIncomeAccountMap.get("glAccountId"); - final String glAccountName = (String) chargeToIncomeAccountMap.get("glAccountName"); - final String glCode = (String) chargeToIncomeAccountMap.get("glCode"); - final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); - final Long chargeId = (Long) chargeToIncomeAccountMap.get("chargeId"); - final String chargeName = (String) chargeToIncomeAccountMap.get("chargeName"); - final Boolean penalty1 = (Boolean) chargeToIncomeAccountMap.get("penalty"); - final ChargeData chargeData = ChargeData.builder().id(chargeId).name(chargeName).penalty(penalty1).build(); + final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); + final ChargeData chargeData = ChargeData.builder().id(mapping.getCharge().getId()).name(mapping.getCharge().getName()) + .penalty(mapping.getCharge().isPenalty()).build(); final ChargeToGLAccountMapper chargeToGLAccountMapper = new ChargeToGLAccountMapper().setCharge(chargeData) .setIncomeAccount(gLAccountData); chargeToGLAccountMappers.add(chargeToGLAccountMapper); @@ -359,30 +273,25 @@ private List fetchChargeToIncomeAccountMappings(final P private List fetchChargeOffReasonMappings(final PortfolioProductType portfolioProductType, final Long loanProductId) { - final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); - String sql = "select " + rm.schema() + " and product_id = ? and mapping.charge_off_reason_id is not null"; - - final List> chargeOffReasonMappingsList = this.jdbcTemplate.query(sql, rm, // NOSONAR - new Object[] { portfolioProductType.getValue(), loanProductId }); + final List mappings = productToGLAccountMappingRepository + .findAllByProductIdAndProductTypeAndChargeOffReasonIdIsNotNull(loanProductId, portfolioProductType.getValue()); List chargeOffReasonToGLAccountMappers = null; - for (final Map chargeOffReasonMap : chargeOffReasonMappingsList) { + for (final ProductToGLAccountMapping mapping : mappings) { if (chargeOffReasonToGLAccountMappers == null) { chargeOffReasonToGLAccountMappers = new ArrayList<>(); } - final Long glAccountId = (Long) chargeOffReasonMap.get("glAccountId"); - final String glAccountName = (String) chargeOffReasonMap.get("glAccountName"); - final String glCode = (String) chargeOffReasonMap.get("glCode"); + final Long glAccountId = mapping.getGlAccount().getId(); + final String glAccountName = mapping.getGlAccount().getName(); + final String glCode = mapping.getGlAccount().getGlCode(); final GLAccountData chargeOffExpenseAccount = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); - final Integer chargeOffReasonId = (Integer) chargeOffReasonMap.get("chargeOffReasonId"); - final String codeValue = (String) chargeOffReasonMap.get("codeValue"); - final String codeDescription = (String) chargeOffReasonMap.get("codeDescription"); - final Integer orderPosition = (Integer) chargeOffReasonMap.get("orderPosition"); - final Integer isActive = (Integer) chargeOffReasonMap.get("isActive"); - final Integer isMandatory = (Integer) chargeOffReasonMap.get("isMandatory"); - final boolean active = isActive != null && isActive == 1; - final boolean mandatory = isMandatory != null && isMandatory == 1; - final CodeValueData chargeOffReasonsCodeValue = CodeValueData.builder().id(Long.valueOf(chargeOffReasonId)).name(codeValue) - .description(codeDescription).position(orderPosition).active(active).mandatory(mandatory).build(); + final Long chargeOffReasonId = mapping.getChargeOffReason().getId(); + final String codeValue = mapping.getChargeOffReason().getLabel(); + final String codeDescription = mapping.getChargeOffReason().getDescription(); + final Integer orderPosition = mapping.getChargeOffReason().getPosition(); + final boolean isActive = mapping.getChargeOffReason().isActive(); + final boolean isMandatory = mapping.getChargeOffReason().isMandatory(); + final CodeValueData chargeOffReasonsCodeValue = CodeValueData.builder().id(chargeOffReasonId).name(codeValue) + .description(codeDescription).position(orderPosition).active(isActive).mandatory(isMandatory).build(); final ChargeOffReasonToGLAccountMapper chargeOffReasonToGLAccountMapper = new ChargeOffReasonToGLAccountMapper() .setChargeOffReasonsCodeValue(chargeOffReasonsCodeValue).setChargeOffExpenseAccount(chargeOffExpenseAccount); @@ -396,21 +305,15 @@ public Map fetchAccountMappingDetailsForShareProduct(Long produc final Map accountMappingDetails = new LinkedHashMap<>(8); - final ProductToGLAccountMappingMapper rm = new ProductToGLAccountMappingMapper(); - final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is null and mapping.charge_id is null "; - - final List> listOfProductToGLAccountMaps = this.jdbcTemplate.query(sql, rm, // NOSONAR - new Object[] { PortfolioProductType.SHARES.getValue(), productId }); + final List mappings = productToGLAccountMappingRepository + .findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(productId, PortfolioProductType.SHARES.getValue()); if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) { - for (final Map productToGLAccountMap : listOfProductToGLAccountMaps) { - final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); - final CashAccountsForShares glAccountForShares = CashAccountsForShares.fromInt(financialAccountType); + for (final ProductToGLAccountMapping mapping : mappings) { + final CashAccountsForShares glAccountForShares = CashAccountsForShares.fromInt(mapping.getFinancialAccountType()); - final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); - final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); - final String glCode = (String) productToGLAccountMap.get("glCode"); - final GLAccountData gLAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); + final GLAccountData gLAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); if (glAccountForShares.equals(CashAccountsForShares.SHARES_REFERENCE)) { accountMappingDetails.put(SharesProductAccountingParams.SHARES_REFERENCE.getValue(), gLAccountData); @@ -442,20 +345,16 @@ public List fetchChargeOffReasonMappingsForLoa return fetchChargeOffReasonMappings(PortfolioProductType.LOAN, loanProductId); } - private Map setAccrualPeriodicSavingsProductToGLAccountMaps( - final List> listOfProductToGLAccountMaps) { + private Map setAccrualPeriodicSavingsProductToGLAccountMaps(final List mappings) { final Map accountMappingDetails = new LinkedHashMap<>(8); - for (final Map productToGLAccountMap : listOfProductToGLAccountMaps) { + for (final ProductToGLAccountMapping mapping : mappings) { - final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); - AccrualAccountsForSavings glAccountForSavings = AccrualAccountsForSavings.fromInt(financialAccountType); + AccrualAccountsForSavings glAccountForSavings = AccrualAccountsForSavings.fromInt(mapping.getFinancialAccountType()); if (glAccountForSavings != null) { - final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); - final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); - final String glCode = (String) productToGLAccountMap.get("glCode"); - final GLAccountData glAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); + final GLAccountData glAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); // Assets if (glAccountForSavings.equals(AccrualAccountsForSavings.SAVINGS_REFERENCE)) { @@ -489,26 +388,23 @@ private Map setAccrualPeriodicSavingsProductToGLAccountMaps( accountMappingDetails.put(SavingProductAccountingDataParams.LOSSES_WRITTEN_OFF.getValue(), glAccountData); } } else { - log.error("Accounting mapping null {}", financialAccountType); + log.error("Accounting mapping null {}", mapping.getFinancialAccountType()); } } return accountMappingDetails; } - private Map setCashSavingsProductToGLAccountMaps(final List> listOfProductToGLAccountMaps) { + private Map setCashSavingsProductToGLAccountMaps(final List mappings) { final Map accountMappingDetails = new LinkedHashMap<>(8); - for (final Map productToGLAccountMap : listOfProductToGLAccountMaps) { + for (final ProductToGLAccountMapping mapping : mappings) { - final Integer financialAccountType = (Integer) productToGLAccountMap.get("financialAccountType"); - CashAccountsForSavings glAccountForSavings = CashAccountsForSavings.fromInt(financialAccountType); + CashAccountsForSavings glAccountForSavings = CashAccountsForSavings.fromInt(mapping.getFinancialAccountType()); if (glAccountForSavings != null) { - final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId"); - final String glAccountName = (String) productToGLAccountMap.get("glAccountName"); - final String glCode = (String) productToGLAccountMap.get("glCode"); - final GLAccountData glAccountData = new GLAccountData().setId(glAccountId).setName(glAccountName).setGlCode(glCode); + final GLAccountData glAccountData = new GLAccountData().setId(mapping.getGlAccount().getId()) + .setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode()); // Assets if (glAccountForSavings.equals(CashAccountsForSavings.SAVINGS_REFERENCE)) { @@ -536,7 +432,7 @@ private Map setCashSavingsProductToGLAccountMaps(final List paymentChannelToFundSourceMappings; public List feeToIncomeAccountMappings; - public List chargeOffReasonsToExpenseMappings; + public List chargeOffReasonsToExpenseMappings; public List penaltyToIncomeAccountMappings; // Multi Disburse @@ -325,6 +325,16 @@ private RateData() {} public List supportedInterestRefundTypes; @Schema(example = "REGULAR") public String chargeOffBehaviour; + + static final class PostChargeOffReasonsToExpenseMappings { + + private PostChargeOffReasonsToExpenseMappings() {} + + @Schema(example = "1") + public Long chargeOffReasonCodeValueId; + @Schema(example = "1") + public Long expenseGLAccountId; + } } @Schema(description = "PostLoanProductsResponse") @@ -1223,10 +1233,37 @@ static final class GetChargeOffReasonsToExpenseMappings { private GetChargeOffReasonsToExpenseMappings() {} - @Schema(example = "1") - public Long chargeOffReasonCodeValueId; - @Schema(example = "12") - public Long expenseGLAccountId; + public GetCodeValueData chargeOffReasonCodeValue; + public GetGLAccountData chargeOffExpenseAccount; + + static final class GetCodeValueData { + + private GetCodeValueData() {} + + @Schema(example = "1") + public Long id; + @Schema(example = "ChargeOffReasons") + public String name; + @Schema(example = "1") + public Integer position; + public String description; + @Schema(example = "true") + public Boolean active; + @Schema(example = "false") + public Boolean mandatory; + } + + static final class GetGLAccountData { + + private GetGLAccountData() {} + + @Schema(example = "1") + public Long id; + @Schema(example = "Written off") + public String name; + @Schema(example = "e4") + public String glCode; + } } static final class GetLoanFeeToIncomeAccountMappings { @@ -1339,7 +1376,7 @@ private GetLoanCharge() {} public GetLoanAccountingMappings accountingMappings; public Set paymentChannelToFundSourceMappings; public Set feeToIncomeAccountMappings; - public Set chargeOffReasonsToExpenseMappings; + public Set chargeOffReasonToGLAccountMappings; @Schema(example = "false") public Boolean isRatesEnabled; @Schema(example = "true") @@ -1599,7 +1636,7 @@ private PutLoanProductsProductIdRequest() {} public Long incomeFromChargeOffPenaltyAccountId; public List paymentChannelToFundSourceMappings; public List feeToIncomeAccountMappings; - public List chargeOffReasonsToExpenseMappings; + public List chargeOffReasonsToExpenseMappings; public List penaltyToIncomeAccountMappings; @Schema(example = "false") public Boolean enableAccrualActivityPosting; diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 24619ba2dae..21358435884 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -176,4 +176,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0158_add_acc_product_mapping_product_id_index.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0158_add_acc_product_mapping_product_id_index.xml new file mode 100644 index 00000000000..7ec53fe2861 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0158_add_acc_product_mapping_product_id_index.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java index 95d8a735cbc..c87d674ad11 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java @@ -31,24 +31,22 @@ import org.apache.fineract.client.models.AllowAttributeOverrides; import org.apache.fineract.client.models.ChargeData; import org.apache.fineract.client.models.ChargeToGLAccountMapper; -import org.apache.fineract.client.models.GetChargeOffReasonsToExpenseMappings; import org.apache.fineract.client.models.GetLoanFeeToIncomeAccountMappings; import org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings; +import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; +import org.apache.fineract.client.models.PostChargeOffReasonsToExpenseMappings; import org.apache.fineract.client.models.PostLoanProductsRequest; import org.apache.fineract.client.models.PutLoanProductsProductIdRequest; import org.apache.fineract.client.util.CallFailedRuntimeException; import org.apache.fineract.integrationtests.common.BusinessStepHelper; import org.apache.fineract.integrationtests.common.Utils; -import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper; import org.apache.fineract.integrationtests.common.system.CodeHelper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(LoanTestLifecycleExtension.class) public class LoanProductChargeOffReasonMappingsTest extends BaseLoanIntegrationTest { private static final String CODE_VALUE_NAME = "ChargeOffReasons"; @@ -77,10 +75,14 @@ public void testCreateLoanProductWithValidChargeOffReason() { final String creationBusinessDay = "15 January 2023"; runAt(creationBusinessDay, () -> { Integer chargeOffReasons = createChargeOffReason(); - Long localLoanProductId = loanTransactionHelper.createLoanProduct(loanProductsRequest(Long.valueOf(chargeOffReasons), 15L)) + Long localLoanProductId = loanTransactionHelper.createLoanProduct(loanProductsRequest(Long.valueOf(chargeOffReasons), 16L)) .getResourceId(); Assertions.assertNotNull(localLoanProductId); + + final GetLoanProductsProductIdResponse loanProduct = loanTransactionHelper.getLoanProduct(localLoanProductId.intValue()); + Assertions.assertNotNull(loanProduct.getChargeOffReasonToGLAccountMappings()); + Assertions.assertFalse(loanProduct.getChargeOffReasonToGLAccountMappings().isEmpty()); }); } @@ -89,8 +91,8 @@ public void testUpdateLoanProductWithValidChargeOffReason() { final String creationBusinessDay = "15 January 2023"; runAt(creationBusinessDay, () -> { Integer chargeOffReasons = createChargeOffReason(); - List chargeOffReasonsToExpenseMappings = new ArrayList<>(); - GetChargeOffReasonsToExpenseMappings getChargeOffReasonsToExpenseMappings = new GetChargeOffReasonsToExpenseMappings(); + List chargeOffReasonsToExpenseMappings = new ArrayList<>(); + PostChargeOffReasonsToExpenseMappings getChargeOffReasonsToExpenseMappings = new PostChargeOffReasonsToExpenseMappings(); getChargeOffReasonsToExpenseMappings.setChargeOffReasonCodeValueId(Long.valueOf(chargeOffReasons)); getChargeOffReasonsToExpenseMappings.setExpenseGLAccountId(15L); chargeOffReasonsToExpenseMappings.add(getChargeOffReasonsToExpenseMappings); @@ -139,8 +141,8 @@ private PostLoanProductsRequest loanProductsRequest(Long chargeOffReasonId, Long List penaltyToIncomeAccountMappings = new ArrayList<>(); List feeToIncomeAccountMappings = new ArrayList<>(); - List chargeOffReasonsToExpenseMappings = new ArrayList<>(); - GetChargeOffReasonsToExpenseMappings getChargeOffReasonsToExpenseMappings = new GetChargeOffReasonsToExpenseMappings(); + List chargeOffReasonsToExpenseMappings = new ArrayList<>(); + PostChargeOffReasonsToExpenseMappings getChargeOffReasonsToExpenseMappings = new PostChargeOffReasonsToExpenseMappings(); getChargeOffReasonsToExpenseMappings.setChargeOffReasonCodeValueId(chargeOffReasonId); getChargeOffReasonsToExpenseMappings.setExpenseGLAccountId(glAccountId); chargeOffReasonsToExpenseMappings.add(getChargeOffReasonsToExpenseMappings);