diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 2f9cf0685..b95df2d30 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -3573,7 +3573,7 @@ public class CommandWrapperBuilder { this.actionName = "ACTIVATE"; this.entityName = "CURRENTACCOUNT"; this.savingsId = accountId; - this.entityId = null; + this.entityId = accountId; this.href = "/currentaccounts/" + accountId + "?command=activate"; return this; } diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java index 88ae1c759..078d9aaf4 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/DepositsApiConstants.java @@ -224,7 +224,7 @@ public final class DepositsApiConstants { * Deposit Product Parameters */ private static final Set DEPOSIT_PRODUCT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, - monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName, + dateFormatParamName, monthDayFormatParamName, nameParamName, shortNameParamName, descriptionParamName, currencyCodeParamName, digitsAfterDecimalParamName, inMultiplesOfParamName, nominalAnnualInterestRateParamName, interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, accountingRuleParamName, chargesParamName, diff --git a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductReadPlatformServiceImpl.java b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductReadPlatformServiceImpl.java index 90b2d3299..08aca6829 100644 --- a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductReadPlatformServiceImpl.java +++ b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductReadPlatformServiceImpl.java @@ -51,6 +51,14 @@ 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; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; +import java.util.Set; @RequiredArgsConstructor public class CurrentProductReadPlatformServiceImpl implements CurrentProductReadPlatformService { @@ -60,6 +68,11 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead private final CurrentProductFacilityConfigRepository facilityConfigRepository; private final CurrentProductFacilityLimitRepository facilityLimitRepository; private final CurrentProductDepositLimitKycRepository depositLimitKycRepository; + private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; + private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository; private final CurrentProductMapper currentProductRowMapper = new CurrentProductMapper(); private final CurrentProductLookupMapper currentProductLookupsRowMapper = new CurrentProductLookupMapper(); @@ -150,41 +163,50 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead } private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, " - + "cp.name as savingsProductName, spt.maker_id as makerId, maker.username as makerUsername, " - + "spt.checker_id as checkerId, checker.username as checkerUsername, spt.created_on as createdOn, " - + "spt.checked_on as checkedOn, spt.status as status, spt.reject_reason as rejectReason, " - + "spt.proposed_payload_json as proposedPayloadJson " - + "from m_savings_product_temp spt " + + "cp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, " + + "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, " + + "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason " + + "from m_saving_product_temp spt " + "join m_current_product cp on cp.id = spt.product_id " - + "join m_appuser maker on maker.id = spt.maker_id " - + "left join m_appuser checker on checker.id = spt.checker_id " + + "join m_appuser maker on maker.id = spt.createdby_id " + + "left join m_appuser checker on checker.id = spt.lastmodifiedby_id " + "where spt.product_type = 'CURRENT' "; - private static final RowMapper PENDING_EDIT_MAPPER = (rs, rowNum) -> { - final SavingsProductTempData data = new SavingsProductTempData(); - data.setId(rs.getLong("tempId")); - data.setSavingsProductId(rs.getLong("savingsProductId")); - data.setSavingsProductName(rs.getString("savingsProductName")); - data.setMakerId(rs.getLong("makerId")); - data.setMakerUsername(rs.getString("makerUsername")); - data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); - data.setCheckerUsername(rs.getString("checkerUsername")); - data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); - data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); - data.setStatus(rs.getString("status")); - data.setRejectReason(rs.getString("rejectReason")); - final String proposedPayloadJson = rs.getString("proposedPayloadJson"); - if (proposedPayloadJson != null) { - data.setProposedChanges(GSON.fromJson(proposedPayloadJson, new TypeToken>() {}.getType())); - } - return data; - }; + private RowMapper getPendingEditRowMapper() { + return (rs, rowNum) -> { + final SavingsProductTempData data = new SavingsProductTempData(); + data.setId(rs.getLong("tempId")); + data.setSavingsProductId(rs.getLong("savingsProductId")); + data.setSavingsProductName(rs.getString("savingsProductName")); + data.setMakerId(rs.getLong("makerId")); + data.setMakerUsername(rs.getString("makerUsername")); + data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); + data.setCheckerUsername(rs.getString("checkerUsername")); + data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); + data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); + data.setStatus(rs.getString("status")); + data.setRejectReason(rs.getString("rejectReason")); + + final Long tempId = data.getId(); + final java.util.Optional opt = this.productPendingEditRepository.findById(tempId); + if (opt.isPresent()) { + final ProductPendingEdit pendingEdit = opt.get(); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final Map proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap( + pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, + this.pendingEditFacilityLimitRepository, GSON); + data.setProposedChanges(proposedChanges); + } + return data; + }; + } @Override public Collection retrieveAllPendingEdits() { this.context.authenticatedUser(); - final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_on desc"; - return this.jdbcTemplate.query(sql, PENDING_EDIT_MAPPER); + final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc"; + return this.jdbcTemplate.query(sql, getPendingEditRowMapper()); } @Override @@ -192,7 +214,7 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead this.context.authenticatedUser(); final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?"; try { - return this.jdbcTemplate.queryForObject(sql, PENDING_EDIT_MAPPER, new Object[] { productId }); // NOSONAR + return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR } catch (final EmptyResultDataAccessException e) { throw new CurrentProductNoPendingEditException(productId); } @@ -249,7 +271,7 @@ public class CurrentProductReadPlatformServiceImpl implements CurrentProductRead sqlBuilder.append("cp.balance_swap_option as balanceSwapOption, cp.msme as msme, cp.auto_debit_facility as autoDebitFacility, "); sqlBuilder.append("cp.avg_balance_calc_period as avgBalanceCalcPeriod, cp.valid_for_period as validForPeriod, "); sqlBuilder.append("cp.od_account_closed_zero_balance_in_day_closing as odAccountClosedZeroBalanceInDayClosing, "); - sqlBuilder.append("exists(select 1 from m_savings_product_temp spt where spt.product_id = cp.id " + sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = cp.id " + "and spt.product_type = 'CURRENT' and spt.status = 'PENDING') as isPendingApproval "); sqlBuilder.append("from m_current_product cp "); diff --git a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductWritePlatformServiceJpaRepositoryImpl.java index dbdc417cf..7ef44cd2c 100644 --- a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/service/CurrentProductWritePlatformServiceJpaRepositoryImpl.java @@ -63,6 +63,16 @@ import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimit; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; +import com.google.gson.JsonObject; import org.apache.fineract.portfolio.tax.domain.TaxGroup; import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.dao.DataAccessException; @@ -72,6 +82,7 @@ import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements CurrentProductWritePlatformService { + private static final com.google.gson.Gson GSON = new com.google.gson.Gson(); private final PlatformSecurityContext context; private final CurrentProductRepository currentProductRepository; private final CurrentProductDataValidator fromApiJsonDataValidator; @@ -81,6 +92,10 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr private final CurrentProductFacilityLimitRepository facilityLimitRepository; private final CurrentProductDepositLimitKycRepository depositLimitKycRepository; private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; private final FromJsonHelper fromApiJsonHelper; private final CommandSourceRepository commandSourceRepository; @@ -281,7 +296,28 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr this.fromApiJsonDataValidator.validateForUpdate(command.json(), product); - final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_CURRENT, productId, maker, command.json()); + final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_CURRENT, productId, maker); + + final java.util.Set submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields()); + final java.util.Set currentOnlySubmitted = ProductPendingEditFieldMapper.populateCurrentOnlyFields(command, pendingEdit.getCurrentOnlyFields()); + final boolean facilityConfigSubmitted = ProductPendingEditFieldMapper.populateFacilityConfig(command, pendingEdit.getFacilityConfigFields()); + final boolean depositLimitKycSubmitted = ProductPendingEditFieldMapper.populateDepositLimitKyc(command, pendingEdit.getDepositLimitKycFields()); + final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields()); + + if (!currentOnlySubmitted.isEmpty()) { + submitted.add("avgBalanceCalcPeriod"); + submitted.add("validForPeriod"); + submitted.add("odAccountClosedZeroBalanceInDayClosing"); + } + if (facilityConfigSubmitted) { + submitted.add("accountFacilityConfig"); + } + if (depositLimitKycSubmitted) { + submitted.add("depositLimitKyc"); + } + if (glMappingSubmitted) { + submitted.add("glAccountMapping"); + } final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // @@ -300,6 +336,30 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr this.productPendingEditRepository.saveAndFlush(pendingEdit); + for (final String fieldName : submitted) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName)); + } + + final List chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command); + if (chargeIds != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges")); + for (final Long chargeId : chargeIds) { + this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId)); + } + } + + final List facilityLimits = ProductPendingEditFieldMapper.extractFacilityLimits(command, pendingEdit); + if (facilityLimits != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "accountFacilityLimits")); + this.pendingEditFacilityLimitRepository.saveAll(facilityLimits); + } + + final List glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit); + if (glMappings != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings")); + this.pendingEditGlMappingRepository.saveAll(glMappings); + } + return new CommandProcessingResultBuilder() // .withEntityId(product.getId()) // .build(); @@ -318,8 +378,33 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr throw new CurrentProductSelfApprovalNotAllowedException(productId); } - final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, - this.fromApiJsonHelper.parse(pendingEdit.getProposedPayloadJson()), this.fromApiJsonHelper); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields); + ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), + submittedFields.contains("accountFacilityConfig")); + ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), + submittedFields.contains("depositLimitKyc")); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("accountFacilityLimits")) { + ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root, + this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper); final CommandProcessingResult result = update(productId, replayedCommand); pendingEdit.markApproved(checker); @@ -340,12 +425,40 @@ public class CurrentProductWritePlatformServiceJpaRepositoryImpl implements Curr return existing.get(); } } + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields); + ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), + submittedFields.contains("accountFacilityConfig")); + ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), + submittedFields.contains("depositLimitKyc")); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("accountFacilityLimits")) { + ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root, + this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final String commandJson = GSON.toJson(root); + final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // .entityName("CURRENTPRODUCT") // .resourceGetUrl("/currentproducts/" + productId) // .resourceId(productId) // - .commandAsJson(pendingEdit.getProposedPayloadJson()) // + .commandAsJson(commandJson) // .maker(pendingEdit.getMaker()) // .madeOnDate(DateUtils.getAuditOffsetDateTime()) // .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) // diff --git a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/starter/CurrentProductConfiguration.java b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/starter/CurrentProductConfiguration.java index 503d63186..8b7af3502 100644 --- a/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/starter/CurrentProductConfiguration.java +++ b/fineract-current-product/src/main/java/org/apache/fineract/portfolio/currentproduct/starter/CurrentProductConfiguration.java @@ -23,6 +23,10 @@ import org.apache.fineract.commands.domain.CommandSourceRepository; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; import org.apache.fineract.portfolio.currentproduct.data.CurrentProductDataValidator; import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductAssembler; import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository; @@ -46,9 +50,17 @@ public class CurrentProductConfiguration { public CurrentProductReadPlatformService currentProductReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate, CurrentProductFacilityConfigRepository currentFacilityConfigRepository, CurrentProductFacilityLimitRepository currentFacilityLimitRepository, - CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository) { + CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository, + ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository, + ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository) { return new CurrentProductReadPlatformServiceImpl(context, jdbcTemplate, currentFacilityConfigRepository, - currentFacilityLimitRepository, currentDepositLimitKycRepository); + currentFacilityLimitRepository, currentDepositLimitKycRepository, + productPendingEditRepository, pendingEditSubmittedFieldRepository, + pendingEditChargeRepository, pendingEditGlMappingRepository, + pendingEditFacilityLimitRepository); } @Bean @@ -61,11 +73,16 @@ public class CurrentProductConfiguration { CurrentProductFacilityLimitRepository currentFacilityLimitRepository, CurrentProductDepositLimitKycRepository currentDepositLimitKycRepository, ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository, FromJsonHelper fromApiJsonHelper, CommandSourceRepository commandSourceRepository) { return new CurrentProductWritePlatformServiceJpaRepositoryImpl(context, currentProductRepository, currentProductDataValidator, currentProductAssembler, accountMappingWritePlatformService, currentFacilityConfigRepository, - currentFacilityLimitRepository, currentDepositLimitKycRepository, productPendingEditRepository, fromApiJsonHelper, - commandSourceRepository); + currentFacilityLimitRepository, currentDepositLimitKycRepository, productPendingEditRepository, + pendingEditSubmittedFieldRepository, pendingEditChargeRepository, pendingEditFacilityLimitRepository, + pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java index 2eb1e82c8..2be58645f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositProductReadPlatformServiceImpl.java @@ -44,6 +44,13 @@ 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; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; +import java.util.Set; @RequiredArgsConstructor public class DepositProductReadPlatformServiceImpl implements DepositProductReadPlatformService { @@ -54,6 +61,10 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead private final PlatformSecurityContext context; private final JdbcTemplate jdbcTemplate; private final InterestRateChartReadPlatformService chartReadPlatformService; + private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; @Override public Collection retrieveAll(final DepositAccountType depositAccountType) { @@ -135,41 +146,50 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead return depositAccountType.isFixedDeposit() ? "FD" : "RD"; } - private static final RowMapper PENDING_EDIT_MAPPER = (rs, rowNum) -> { - final SavingsProductTempData data = new SavingsProductTempData(); - data.setId(rs.getLong("tempId")); - data.setSavingsProductId(rs.getLong("savingsProductId")); - data.setSavingsProductName(rs.getString("savingsProductName")); - data.setMakerId(rs.getLong("makerId")); - data.setMakerUsername(rs.getString("makerUsername")); - data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); - data.setCheckerUsername(rs.getString("checkerUsername")); - data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); - data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); - data.setStatus(rs.getString("status")); - data.setRejectReason(rs.getString("rejectReason")); - final String proposedPayloadJson = rs.getString("proposedPayloadJson"); - if (proposedPayloadJson != null) { - data.setProposedChanges(GSON.fromJson(proposedPayloadJson, new TypeToken>() {}.getType())); - } - return data; - }; + private RowMapper getPendingEditRowMapper() { + return (rs, rowNum) -> { + final SavingsProductTempData data = new SavingsProductTempData(); + data.setId(rs.getLong("tempId")); + data.setSavingsProductId(rs.getLong("savingsProductId")); + data.setSavingsProductName(rs.getString("savingsProductName")); + data.setMakerId(rs.getLong("makerId")); + data.setMakerUsername(rs.getString("makerUsername")); + data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); + data.setCheckerUsername(rs.getString("checkerUsername")); + data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); + data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); + data.setStatus(rs.getString("status")); + data.setRejectReason(rs.getString("rejectReason")); + + final Long tempId = data.getId(); + final java.util.Optional opt = this.productPendingEditRepository.findById(tempId); + if (opt.isPresent()) { + final ProductPendingEdit pendingEdit = opt.get(); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final Map proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap( + pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON); + data.setProposedChanges(proposedChanges); + } + return data; + }; + } private static String pendingEditSchema(final String productType) { - return "spt.id as tempId, spt.product_id as savingsProductId, " + "sp.name as savingsProductName, spt.maker_id as makerId, " - + "maker.username as makerUsername, spt.checker_id as checkerId, checker.username as checkerUsername, " - + "spt.created_on as createdOn, spt.checked_on as checkedOn, spt.status as status, " - + "spt.reject_reason as rejectReason, spt.proposed_payload_json as proposedPayloadJson " - + "from m_savings_product_temp spt " + "join m_savings_product sp on sp.id = spt.product_id " - + "join m_appuser maker on maker.id = spt.maker_id " + "left join m_appuser checker on checker.id = spt.checker_id " + return "spt.id as tempId, spt.product_id as savingsProductId, " + "sp.name as savingsProductName, spt.createdby_id as makerId, " + + "maker.username as makerUsername, spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, " + + "spt.created_date as createdOn, spt.lastmodified_date as checkedOn, spt.status as status, " + + "spt.reject_reason as rejectReason " + + "from m_saving_product_temp spt " + "join m_savings_product sp on sp.id = spt.product_id " + + "join m_appuser maker on maker.id = spt.createdby_id " + "left join m_appuser checker on checker.id = spt.lastmodifiedby_id " + "where spt.product_type = '" + productType + "' "; } @Override public Collection retrieveAllPendingEdits(final DepositAccountType depositAccountType) { final String sql = "select " + pendingEditSchema(productPendingType(depositAccountType)) - + "and spt.status = 'PENDING' order by spt.created_on desc"; - return this.jdbcTemplate.query(sql, PENDING_EDIT_MAPPER); + + "and spt.status = 'PENDING' order by spt.created_date desc"; + return this.jdbcTemplate.query(sql, getPendingEditRowMapper()); } @Override @@ -177,7 +197,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead final String sql = "select " + pendingEditSchema(productPendingType(depositAccountType)) + "and spt.status = 'PENDING' and spt.product_id = ?"; try { - return this.jdbcTemplate.queryForObject(sql, PENDING_EDIT_MAPPER, new Object[] { productId }); // NOSONAR + return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR } catch (final EmptyResultDataAccessException e) { throw new SavingsProductNoPendingEditException(productId); } @@ -315,7 +335,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead sqlBuilder.append("sp.deceased_settlement as deceasedSettlement, "); sqlBuilder.append("sp.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate, "); sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); - sqlBuilder.append("exists(select 1 from m_savings_product_temp spt where spt.product_id = sp.id " + sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = sp.id " + "and spt.product_type = 'FD' and spt.status = 'PENDING') as isPendingApproval "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id "); @@ -455,7 +475,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead sqlBuilder.append("sp.deceased_settlement as deceasedSettlement, "); sqlBuilder.append("sp.allow_debit_txn_after_due_date as allowDebitTxnAfterDueDate, "); sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, "); - sqlBuilder.append("exists(select 1 from m_savings_product_temp spt where spt.product_id = sp.id " + sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = sp.id " + "and spt.product_type = 'RD' and spt.status = 'PENDING') as isPendingApproval "); sqlBuilder.append("from m_savings_product sp "); sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java index 23166a11d..301e3bbcf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/FixedDepositProductWritePlatformServiceJpaRepositoryImpl.java @@ -55,6 +55,14 @@ import org.apache.fineract.portfolio.savings.domain.FixedDepositProduct; import org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository; import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; +import com.google.gson.JsonObject; import org.apache.fineract.portfolio.savings.exception.FixedDepositProductNotFoundException; import org.apache.fineract.portfolio.savings.exception.SavingsProductEditPendingApprovalException; import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException; @@ -68,6 +76,7 @@ import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements FixedDepositProductWritePlatformService { + private static final com.google.gson.Gson GSON = new com.google.gson.Gson(); private final PlatformSecurityContext context; private final FixedDepositProductRepository fixedDepositProductRepository; private final DepositProductDataValidator fromApiJsonDataValidator; @@ -75,6 +84,9 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; private final InterestRateChartAssembler chartAssembler; private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; private final FromJsonHelper fromApiJsonHelper; private final CommandSourceRepository commandSourceRepository; @@ -108,10 +120,16 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements @Transactional @Override public CommandProcessingResult update(final Long productId, final JsonCommand command) { + return updateInternal(productId, command, true); + } + + private CommandProcessingResult updateInternal(final Long productId, final JsonCommand command, final boolean validate) { try { this.context.authenticatedUser(); - this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json()); + if (validate) { + this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json()); + } final FixedDepositProduct product = this.fixedDepositProductRepository.findById(productId) .orElseThrow(() -> new FixedDepositProductNotFoundException(productId)); @@ -180,7 +198,18 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json()); - final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_FD, productId, maker, command.json()); + final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_FD, productId, maker); + + final java.util.Set submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields()); + final java.util.Set fdSubmitted = ProductPendingEditFieldMapper.populateFixedDepositFields(command, pendingEdit.getFixedDepositFields()); + final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields()); + + if (!fdSubmitted.isEmpty()) { + submitted.addAll(fdSubmitted); + } + if (glMappingSubmitted) { + submitted.add("glAccountMapping"); + } final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // @@ -199,6 +228,24 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements this.productPendingEditRepository.saveAndFlush(pendingEdit); + for (final String fieldName : submitted) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName)); + } + + final List chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command); + if (chargeIds != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges")); + for (final Long chargeId : chargeIds) { + this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId)); + } + } + + final List glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit); + if (glMappings != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings")); + this.pendingEditGlMappingRepository.saveAll(glMappings); + } + return new CommandProcessingResultBuilder() // .withEntityId(product.getId()) // .build(); @@ -217,9 +264,26 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements throw new SavingsProductSelfApprovalNotAllowedException(productId); } - final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, - this.fromApiJsonHelper.parse(pendingEdit.getProposedPayloadJson()), this.fromApiJsonHelper); - final CommandProcessingResult result = update(productId, replayedCommand); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper); + final CommandProcessingResult result = updateInternal(productId, replayedCommand, false); pendingEdit.markApproved(checker); this.productPendingEditRepository.saveAndFlush(pendingEdit); @@ -239,12 +303,32 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements return existing.get(); } } + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final String commandJson = GSON.toJson(root); + final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // .entityName("FIXEDDEPOSITPRODUCT") // .resourceGetUrl("/fixeddepositproducts/" + productId) // .resourceId(productId) // - .commandAsJson(pendingEdit.getProposedPayloadJson()) // + .commandAsJson(commandJson) // .maker(pendingEdit.getMaker()) // .madeOnDate(DateUtils.getAuditOffsetDateTime()) // .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java index 363d6e4ed..3abffe3ad 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/RecurringDepositProductWritePlatformServiceJpaRepositoryImpl.java @@ -52,10 +52,18 @@ import org.apache.fineract.portfolio.savings.SavingsApiConstants; import org.apache.fineract.portfolio.savings.data.DepositProductDataValidator; import org.apache.fineract.portfolio.savings.domain.DepositProductAssembler; import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; import org.apache.fineract.portfolio.savings.domain.RecurringDepositProduct; import org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository; import org.apache.fineract.portfolio.savings.exception.RecurringDepositProductNotFoundException; +import com.google.gson.JsonObject; import org.apache.fineract.portfolio.savings.exception.SavingsProductEditPendingApprovalException; import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException; import org.apache.fineract.portfolio.savings.exception.SavingsProductSelfApprovalNotAllowedException; @@ -68,6 +76,7 @@ import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implements RecurringDepositProductWritePlatformService { + private static final com.google.gson.Gson GSON = new com.google.gson.Gson(); private final PlatformSecurityContext context; private final RecurringDepositProductRepository recurringDepositProductRepository; private final DepositProductDataValidator fromApiJsonDataValidator; @@ -75,6 +84,9 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService; private final InterestRateChartAssembler chartAssembler; private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; private final FromJsonHelper fromApiJsonHelper; private final CommandSourceRepository commandSourceRepository; @@ -108,10 +120,16 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem @Transactional @Override public CommandProcessingResult update(final Long productId, final JsonCommand command) { + return updateInternal(productId, command, true); + } + + private CommandProcessingResult updateInternal(final Long productId, final JsonCommand command, final boolean validate) { try { this.context.authenticatedUser(); - this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json()); + if (validate) { + this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json()); + } final RecurringDepositProduct product = this.recurringDepositProductRepository.findById(productId) .orElseThrow(() -> new RecurringDepositProductNotFoundException(productId)); @@ -180,7 +198,22 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json()); - final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_RD, productId, maker, command.json()); + final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_RD, productId, maker); + + final java.util.Set submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields()); + final java.util.Set fdSubmitted = ProductPendingEditFieldMapper.populateFixedDepositFields(command, pendingEdit.getFixedDepositFields()); + final java.util.Set rdSubmitted = ProductPendingEditFieldMapper.populateRecurringDepositFields(command, pendingEdit.getRecurringDepositFields()); + final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields()); + + if (!fdSubmitted.isEmpty()) { + submitted.addAll(fdSubmitted); + } + if (!rdSubmitted.isEmpty()) { + submitted.addAll(rdSubmitted); + } + if (glMappingSubmitted) { + submitted.add("glAccountMapping"); + } final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // @@ -199,6 +232,24 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem this.productPendingEditRepository.saveAndFlush(pendingEdit); + for (final String fieldName : submitted) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName)); + } + + final List chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command); + if (chargeIds != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges")); + for (final Long chargeId : chargeIds) { + this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId)); + } + } + + final List glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit); + if (glMappings != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings")); + this.pendingEditGlMappingRepository.saveAll(glMappings); + } + return new CommandProcessingResultBuilder() // .withEntityId(product.getId()) // .build(); @@ -217,9 +268,28 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem throw new SavingsProductSelfApprovalNotAllowedException(productId); } - final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, - this.fromApiJsonHelper.parse(pendingEdit.getProposedPayloadJson()), this.fromApiJsonHelper); - final CommandProcessingResult result = update(productId, replayedCommand); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + submittedFields.remove("fdType"); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields); + ProductPendingEditFieldMapper.writeRecurringDepositFieldsToJson(root, pendingEdit.getRecurringDepositFields(), submittedFields); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper); + final CommandProcessingResult result = updateInternal(productId, replayedCommand, false); pendingEdit.markApproved(checker); this.productPendingEditRepository.saveAndFlush(pendingEdit); @@ -239,12 +309,33 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem return existing.get(); } } + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields); + ProductPendingEditFieldMapper.writeRecurringDepositFieldsToJson(root, pendingEdit.getRecurringDepositFields(), submittedFields); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final String commandJson = GSON.toJson(root); + final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // .entityName("RECURRINGDEPOSITPRODUCT") // .resourceGetUrl("/recurringdepositproducts/" + productId) // .resourceId(productId) // - .commandAsJson(pendingEdit.getProposedPayloadJson()) // + .commandAsJson(commandJson) // .maker(pendingEdit.getMaker()) // .madeOnDate(DateUtils.getAuditOffsetDateTime()) // .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index 7c1b9f3a0..2663310e8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -45,12 +45,19 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKy import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException; import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; 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; +import java.util.Set; @RequiredArgsConstructor public class SavingsProductReadPlatformServiceImpl implements SavingsProductReadPlatformService { @@ -63,6 +70,10 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead private final SavingsProductFacilityConfigRepository facilityConfigRepository; private final SavingsProductFacilityLimitRepository facilityLimitRepository; private final SavingsProductDepositLimitKycRepository depositLimitKycRepository; + private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; private static final Gson GSON = new Gson(); private static final Type OTHER_GL_MAPPINGS_TYPE = new TypeToken>>() {}.getType(); private static final Type OTHER_GL_MAPPINGS_MAP_TYPE = new TypeToken>>>() {}.getType(); @@ -173,43 +184,49 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead } private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, " - + "sp.name as savingsProductName, spt.maker_id as makerId, maker.username as makerUsername, " - + "spt.checker_id as checkerId, checker.username as checkerUsername, spt.created_on as createdOn, " - + "spt.checked_on as checkedOn, spt.status as status, spt.reject_reason as rejectReason, " - + "spt.proposed_payload_json as proposedPayloadJson " - + "from m_savings_product_temp spt " + + "sp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, " + + "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, " + + "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason " + + "from m_saving_product_temp spt " + "join m_savings_product sp on sp.id = spt.product_id " - + "join m_appuser maker on maker.id = spt.maker_id " - + "left join m_appuser checker on checker.id = spt.checker_id " + + "join m_appuser maker on maker.id = spt.createdby_id " + + "left join m_appuser checker on checker.id = spt.lastmodifiedby_id " + "where spt.product_type = 'SAVINGS' "; - private static final RowMapper PENDING_EDIT_MAPPER = (rs, rowNum) -> { - final SavingsProductTempData data = new SavingsProductTempData(); - data.setId(rs.getLong("tempId")); - data.setSavingsProductId(rs.getLong("savingsProductId")); - data.setSavingsProductName(rs.getString("savingsProductName")); - data.setMakerId(rs.getLong("makerId")); - data.setMakerUsername(rs.getString("makerUsername")); - data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); - data.setCheckerUsername(rs.getString("checkerUsername")); - data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); - data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); - data.setStatus(rs.getString("status")); - data.setRejectReason(rs.getString("rejectReason")); - final String proposedPayloadJson = rs.getString("proposedPayloadJson"); - if (proposedPayloadJson != null) { - final Map proposedChanges = GSON.fromJson(proposedPayloadJson, - new TypeToken>() {}.getType()); - data.setProposedChanges(proposedChanges); - } - return data; - }; + private RowMapper getPendingEditRowMapper() { + return (rs, rowNum) -> { + final SavingsProductTempData data = new SavingsProductTempData(); + data.setId(rs.getLong("tempId")); + data.setSavingsProductId(rs.getLong("savingsProductId")); + data.setSavingsProductName(rs.getString("savingsProductName")); + data.setMakerId(rs.getLong("makerId")); + data.setMakerUsername(rs.getString("makerUsername")); + data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); + data.setCheckerUsername(rs.getString("checkerUsername")); + data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); + data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); + data.setStatus(rs.getString("status")); + data.setRejectReason(rs.getString("rejectReason")); + + final Long tempId = data.getId(); + final java.util.Optional opt = this.productPendingEditRepository.findById(tempId); + if (opt.isPresent()) { + final ProductPendingEdit pendingEdit = opt.get(); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final Map proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap( + pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON); + data.setProposedChanges(proposedChanges); + } + return data; + }; + } @Override public Collection retrieveAllPendingEdits() { this.context.authenticatedUser(); - final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_on desc"; - return this.jdbcTemplate.query(sql, PENDING_EDIT_MAPPER); + final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc"; + return this.jdbcTemplate.query(sql, getPendingEditRowMapper()); } @Override @@ -217,7 +234,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead this.context.authenticatedUser(); final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?"; try { - return this.jdbcTemplate.queryForObject(sql, PENDING_EDIT_MAPPER, new Object[] { productId }); // NOSONAR + return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR } catch (final EmptyResultDataAccessException e) { throw new SavingsProductNoPendingEditException(productId); } @@ -271,7 +288,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead sqlBuilder.append("sp.enable_lockin_period as enableLockinPeriod, "); sqlBuilder.append("sp.balance_frequency as balanceFrequency, "); sqlBuilder.append("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, "); - sqlBuilder.append("exists(select 1 from m_savings_product_temp spt where spt.product_id = sp.id " + sqlBuilder.append("exists(select 1 from m_saving_product_temp spt where spt.product_id = sp.id " + "and spt.product_type = 'SAVINGS' and spt.status = 'PENDING') as isPendingApproval "); sqlBuilder.append("from m_savings_product sp "); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java index a65a1e1c3..5a2ee8187 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductWritePlatformServiceJpaRepositoryImpl.java @@ -87,6 +87,7 @@ import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements SavingsProductWritePlatformService { + private static final com.google.gson.Gson GSON = new com.google.gson.Gson(); private final PlatformSecurityContext context; private final SavingsProductRepository savingProductRepository; private final SavingsProductDataValidator fromApiJsonDataValidator; @@ -324,7 +325,7 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi // Reuses the same validator the direct-update path uses, so bad data can't sit "pending" undetected. this.fromApiJsonDataValidator.validateForUpdate(command.json(), product); - final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SAVINGS, productId, maker, command.json()); + final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SAVINGS, productId, maker); // Typed-column storage: extract every recognised field into the pending edit's own columns // (plus satellite rows for the 1:many pieces) instead of relying solely on the JSON blob @@ -409,44 +410,32 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi throw new SavingsProductSelfApprovalNotAllowedException(productId); } - // Replay through the real update path so charges, GL/accounting mappings and other related - // tables are applied exactly as a direct edit would apply them - not hand-copied field by - // field. The replay JSON is rebuilt from this row's typed columns/satellite tables (the - // manifest records exactly which top-level parameters the maker actually submitted); pending - // edits created before this cutover have no manifest rows, so they fall back to replaying the - // legacy JSON blob directly. final Set submittedFields = new java.util.HashSet<>( this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); - final JsonCommand replayedCommand; - if (submittedFields.isEmpty()) { - replayedCommand = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(pendingEdit.getProposedPayloadJson()), - this.fromApiJsonHelper); - } else { - final JsonObject root = new JsonObject(); - root.addProperty("locale", "en"); - root.addProperty("dateFormat", "yyyy-MM-dd"); - ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); - ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), - submittedFields.contains("accountFacilityConfig")); - ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), - submittedFields.contains("depositLimitKyc")); - if (submittedFields.contains("glAccountMapping")) { - ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); - } - if (submittedFields.contains("charges")) { - ProductPendingEditFieldMapper.writeChargesToJson(root, - this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); - } - if (submittedFields.contains("accountFacilityLimits")) { - ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root, - this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId())); - } - if (submittedFields.contains("glArrayMappings")) { - ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, - this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); - } - replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), + submittedFields.contains("accountFacilityConfig")); + ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), + submittedFields.contains("depositLimitKyc")); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("accountFacilityLimits")) { + ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root, + this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper); final CommandProcessingResult result = update(productId, replayedCommand); pendingEdit.markApproved(checker); @@ -460,11 +449,6 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi return result; } - /** - * Every edit submitted through submitEditForApproval() links a CommandSource audit row. This fallback only - * matters for pending edits that were created before that linking existed - it backfills one now so approve - * and reject always leave an entry in m_portfolio_command_source, with no silent no-op case. - */ private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) { if (pendingEdit.getCommandSourceId() != null) { final java.util.Optional existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId()); @@ -472,12 +456,39 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi return existing.get(); } } + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + ProductPendingEditFieldMapper.writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + ProductPendingEditFieldMapper.writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), + submittedFields.contains("accountFacilityConfig")); + ProductPendingEditFieldMapper.writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), + submittedFields.contains("depositLimitKyc")); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("accountFacilityLimits")) { + ProductPendingEditFieldMapper.writeFacilityLimitsToJson(root, + this.pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final String commandJson = GSON.toJson(root); + final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // .entityName("SAVINGSPRODUCT") // .resourceGetUrl("/savingsproducts/" + productId) // .resourceId(productId) // - .commandAsJson(pendingEdit.getProposedPayloadJson()) // + .commandAsJson(commandJson) // .maker(pendingEdit.getMaker()) // .madeOnDate(DateUtils.getAuditOffsetDateTime()) // .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java index d9537ad2f..b5fa180bc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java @@ -97,6 +97,9 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKy import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig; import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; import org.apache.fineract.commands.domain.CommandSourceRepository; import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit; import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; @@ -311,8 +314,14 @@ public class SavingsConfiguration { @Bean @ConditionalOnMissingBean(DepositProductReadPlatformService.class) public DepositProductReadPlatformService depositProductReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate, - InterestRateChartReadPlatformService interestRateChartReadPlatformService) { - return new DepositProductReadPlatformServiceImpl(context, jdbcTemplate, interestRateChartReadPlatformService); + InterestRateChartReadPlatformService interestRateChartReadPlatformService, + ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository) { + return new DepositProductReadPlatformServiceImpl(context, jdbcTemplate, interestRateChartReadPlatformService, + productPendingEditRepository, pendingEditSubmittedFieldRepository, + pendingEditChargeRepository, pendingEditGlMappingRepository); } @Bean @@ -327,11 +336,16 @@ public class SavingsConfiguration { FixedDepositProductRepository fixedDepositProductRepository, DepositProductDataValidator fromApiJsonDataValidator, DepositProductAssembler depositProductAssembler, ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler, - ProductPendingEditRepository productPendingEditRepository, FromJsonHelper fromApiJsonHelper, + ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository, + FromJsonHelper fromApiJsonHelper, CommandSourceRepository commandSourceRepository) { return new FixedDepositProductWritePlatformServiceJpaRepositoryImpl(context, fixedDepositProductRepository, fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler, - productPendingEditRepository, fromApiJsonHelper, commandSourceRepository); + productPendingEditRepository, pendingEditSubmittedFieldRepository, pendingEditChargeRepository, + pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository); } @Bean @@ -354,11 +368,16 @@ public class SavingsConfiguration { RecurringDepositProductRepository recurringDepositProductRepository, DepositProductDataValidator fromApiJsonDataValidator, DepositProductAssembler depositProductAssembler, ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler, - ProductPendingEditRepository productPendingEditRepository, FromJsonHelper fromApiJsonHelper, + ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository, + FromJsonHelper fromApiJsonHelper, CommandSourceRepository commandSourceRepository) { return new RecurringDepositProductWritePlatformServiceJpaRepositoryImpl(context, recurringDepositProductRepository, fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler, - productPendingEditRepository, fromApiJsonHelper, commandSourceRepository); + productPendingEditRepository, pendingEditSubmittedFieldRepository, pendingEditChargeRepository, + pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository); } @Bean @@ -481,9 +500,15 @@ public class SavingsConfiguration { @ConditionalOnMissingBean(SavingsProductReadPlatformService.class) public SavingsProductReadPlatformService savingsProductReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate, FineractEntityAccessUtil fineractEntityAccessUtil, SavingsProductFacilityConfigRepository facilityConfigRepository, - SavingsProductFacilityLimitRepository facilityLimitRepository, SavingsProductDepositLimitKycRepository depositLimitKycRepository) { + SavingsProductFacilityLimitRepository facilityLimitRepository, SavingsProductDepositLimitKycRepository depositLimitKycRepository, + ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository) { return new SavingsProductReadPlatformServiceImpl(context, jdbcTemplate, fineractEntityAccessUtil, facilityConfigRepository, - facilityLimitRepository, depositLimitKycRepository); + facilityLimitRepository, depositLimitKycRepository, + productPendingEditRepository, pendingEditSubmittedFieldRepository, + pendingEditChargeRepository, pendingEditGlMappingRepository); } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java index 425f6b2e8..a9525484e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductReadPlatformServiceImpl.java @@ -56,6 +56,12 @@ import org.apache.fineract.portfolio.shareproducts.exception.ShareProductNoPendi import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; @RequiredArgsConstructor public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlatformService { @@ -66,6 +72,10 @@ public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlat private final ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService; private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService; private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService; + private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; private final PaginationHelper shareProductDataPaginationHelper; private final DatabaseSpecificSQLGenerator sqlGenerator; @@ -159,47 +169,55 @@ public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlat private static final Gson GSON = new Gson(); private static final String PENDING_EDIT_SCHEMA = "spt.id as tempId, spt.product_id as savingsProductId, " - + "sp.name as savingsProductName, spt.maker_id as makerId, maker.username as makerUsername, " - + "spt.checker_id as checkerId, checker.username as checkerUsername, spt.created_on as createdOn, " - + "spt.checked_on as checkedOn, spt.status as status, spt.reject_reason as rejectReason, " - + "spt.proposed_payload_json as proposedPayloadJson " - + "from m_savings_product_temp spt " + + "sp.name as savingsProductName, spt.createdby_id as makerId, maker.username as makerUsername, " + + "spt.lastmodifiedby_id as checkerId, checker.username as checkerUsername, spt.created_date as createdOn, " + + "spt.lastmodified_date as checkedOn, spt.status as status, spt.reject_reason as rejectReason " + + "from m_saving_product_temp spt " + "join m_share_product sp on sp.id = spt.product_id " - + "join m_appuser maker on maker.id = spt.maker_id " - + "left join m_appuser checker on checker.id = spt.checker_id " + + "join m_appuser maker on maker.id = spt.createdby_id " + + "left join m_appuser checker on checker.id = spt.lastmodifiedby_id " + "where spt.product_type = 'SHARE' "; - private static final RowMapper PENDING_EDIT_MAPPER = (rs, rowNum) -> { - final SavingsProductTempData data = new SavingsProductTempData(); - data.setId(rs.getLong("tempId")); - data.setSavingsProductId(rs.getLong("savingsProductId")); - data.setSavingsProductName(rs.getString("savingsProductName")); - data.setMakerId(rs.getLong("makerId")); - data.setMakerUsername(rs.getString("makerUsername")); - data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); - data.setCheckerUsername(rs.getString("checkerUsername")); - data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); - data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); - data.setStatus(rs.getString("status")); - data.setRejectReason(rs.getString("rejectReason")); - final String proposedPayloadJson = rs.getString("proposedPayloadJson"); - if (proposedPayloadJson != null) { - data.setProposedChanges(GSON.fromJson(proposedPayloadJson, new TypeToken>() {}.getType())); - } - return data; - }; + private RowMapper getPendingEditRowMapper() { + return (rs, rowNum) -> { + final SavingsProductTempData data = new SavingsProductTempData(); + data.setId(rs.getLong("tempId")); + data.setSavingsProductId(rs.getLong("savingsProductId")); + data.setSavingsProductName(rs.getString("savingsProductName")); + data.setMakerId(rs.getLong("makerId")); + data.setMakerUsername(rs.getString("makerUsername")); + data.setCheckerId(JdbcSupport.getLong(rs, "checkerId")); + data.setCheckerUsername(rs.getString("checkerUsername")); + data.setCreatedOn(rs.getTimestamp("createdOn") == null ? null : rs.getTimestamp("createdOn").toLocalDateTime()); + data.setCheckedOn(rs.getTimestamp("checkedOn") == null ? null : rs.getTimestamp("checkedOn").toLocalDateTime()); + data.setStatus(rs.getString("status")); + data.setRejectReason(rs.getString("rejectReason")); + + final Long tempId = data.getId(); + final java.util.Optional opt = this.productPendingEditRepository.findById(tempId); + if (opt.isPresent()) { + final ProductPendingEdit pendingEdit = opt.get(); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final Map proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap( + pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON); + data.setProposedChanges(proposedChanges); + } + return data; + }; + } @Override public Collection retrieveAllPendingEdits() { - final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_on desc"; - return this.jdbcTemplate.query(sql, PENDING_EDIT_MAPPER); + final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' order by spt.created_date desc"; + return this.jdbcTemplate.query(sql, getPendingEditRowMapper()); } @Override public SavingsProductTempData retrievePendingEdit(final Long productId) { final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?"; try { - return this.jdbcTemplate.queryForObject(sql, PENDING_EDIT_MAPPER, new Object[] { productId }); // NOSONAR + return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR } catch (final EmptyResultDataAccessException e) { throw new ShareProductNoPendingEditException(productId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java index a0020f108..e3714d10d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/service/ShareProductWritePlatformServiceJpaRepositoryImpl.java @@ -22,7 +22,9 @@ import com.google.gson.JsonElement; import jakarta.persistence.PersistenceException; import java.math.BigDecimal; import java.time.LocalDate; +import java.util.List; import java.util.Map; +import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -52,6 +54,14 @@ import org.apache.fineract.portfolio.shareproducts.exception.ShareProductEditPen import org.apache.fineract.portfolio.shareproducts.exception.ShareProductNoPendingEditException; import org.apache.fineract.portfolio.shareproducts.exception.ShareProductSelfApprovalNotAllowedException; import org.apache.fineract.portfolio.shareproducts.serialization.ShareProductDataSerializer; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedField; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditCharge; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMapping; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper; +import com.google.gson.JsonObject; import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.orm.jpa.JpaSystemException; @@ -61,6 +71,7 @@ import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareProductWritePlatformService { + private static final com.google.gson.Gson GSON = new com.google.gson.Gson(); private final ShareProductRepositoryWrapper repository; private final ShareProductDataSerializer serializer; private final FromJsonHelper fromApiJsonHelper; @@ -70,6 +81,9 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP private final BusinessEventNotifierService businessEventNotifierService; private final PlatformSecurityContext context; private final ProductPendingEditRepository productPendingEditRepository; + private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository; + private final ProductPendingEditChargeRepository pendingEditChargeRepository; + private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository; private final CommandSourceRepository commandSourceRepository; @Override @@ -208,7 +222,13 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP throw new ShareProductEditPendingApprovalException(productId); } - final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SHARE, productId, maker, command.json()); + final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SHARE, productId, maker); + + final java.util.Set submitted = ProductPendingEditFieldMapper.populateShareOnlyFields(command, pendingEdit.getShareOnlyFields()); + final boolean glMappingSubmitted = ProductPendingEditFieldMapper.populateGlAccountMapping(command, pendingEdit.getGlAccountMappingFields()); + if (glMappingSubmitted) { + submitted.add("glAccountMapping"); + } final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // @@ -227,6 +247,24 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP this.productPendingEditRepository.saveAndFlush(pendingEdit); + for (final String fieldName : submitted) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName)); + } + + final List chargeIds = ProductPendingEditFieldMapper.extractChargeIds(command); + if (chargeIds != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "charges")); + for (final Long chargeId : chargeIds) { + this.pendingEditChargeRepository.save(new ProductPendingEditCharge(pendingEdit, chargeId)); + } + } + + final List glMappings = ProductPendingEditFieldMapper.extractGlArrayMappings(command, pendingEdit); + if (glMappings != null) { + this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "glArrayMappings")); + this.pendingEditGlMappingRepository.saveAll(glMappings); + } + return new CommandProcessingResultBuilder() // .withEntityId(product.getId()) // .build(); @@ -245,8 +283,23 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP throw new ShareProductSelfApprovalNotAllowedException(productId); } - final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, - this.fromApiJsonHelper.parse(pendingEdit.getProposedPayloadJson()), this.fromApiJsonHelper); + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + ProductPendingEditFieldMapper.writeShareOnlyFieldsToJson(root, pendingEdit.getShareOnlyFields(), submittedFields); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper); final CommandProcessingResult result = updateProduct(productId, replayedCommand); pendingEdit.markApproved(checker); @@ -267,12 +320,30 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP return existing.get(); } } + final Set submittedFields = new java.util.HashSet<>( + this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId())); + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + ProductPendingEditFieldMapper.writeShareOnlyFieldsToJson(root, pendingEdit.getShareOnlyFields(), submittedFields); + if (submittedFields.contains("glAccountMapping")) { + ProductPendingEditFieldMapper.writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges")) { + ProductPendingEditFieldMapper.writeChargesToJson(root, + this.pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings")) { + ProductPendingEditFieldMapper.writeGlArrayMappingsToJson(root, + this.pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + final String commandJson = GSON.toJson(root); + final CommandSource auditEntry = CommandSource.builder() // .actionName("UPDATE") // .entityName("SHAREPRODUCT") // .resourceGetUrl("/products/share/" + productId) // .resourceId(productId) // - .commandAsJson(pendingEdit.getProposedPayloadJson()) // + .commandAsJson(commandJson) // .maker(pendingEdit.getMaker()) // .madeOnDate(DateUtils.getAuditOffsetDateTime()) // .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/starter/ShareProductsConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/starter/ShareProductsConfiguration.java index 38a61e059..0134e629d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/starter/ShareProductsConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareproducts/starter/ShareProductsConfiguration.java @@ -32,6 +32,9 @@ import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformSer import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; import org.apache.fineract.portfolio.products.service.ShareProductReadPlatformService; import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository; +import org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository; import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService; import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepositoryWrapper; import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper; @@ -89,9 +92,15 @@ public class ShareProductsConfiguration { ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService, AccountingDropdownReadPlatformService accountingDropdownReadPlatformService, ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService, - PaginationHelper shareProductDataPaginationHelper, DatabaseSpecificSQLGenerator sqlGenerator) { + PaginationHelper shareProductDataPaginationHelper, DatabaseSpecificSQLGenerator sqlGenerator, + ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository) { return new ShareProductReadPlatformServiceImpl(jdbcTemplate, currencyReadPlatformService, chargeReadPlatformService, shareProductDropdownReadPlatformService, accountingDropdownReadPlatformService, accountMappingReadPlatformService, + productPendingEditRepository, pendingEditSubmittedFieldRepository, + pendingEditChargeRepository, pendingEditGlMappingRepository, shareProductDataPaginationHelper, sqlGenerator); } @@ -105,10 +114,14 @@ public class ShareProductsConfiguration { BusinessEventNotifierService businessEventNotifierService, PlatformSecurityContext context, ProductPendingEditRepository productPendingEditRepository, + ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository, + ProductPendingEditChargeRepository pendingEditChargeRepository, + ProductPendingEditGlMappingRepository pendingEditGlMappingRepository, CommandSourceRepository commandSourceRepository) { return new ShareProductWritePlatformServiceJpaRepositoryImpl(repository, serializer, fromApiJsonHelper, shareProductDividentPayOutDetailsRepository, shareProductDividendAssembler, accountMappingWritePlatformService, - businessEventNotifierService, context, productPendingEditRepository, commandSourceRepository); + businessEventNotifierService, context, productPendingEditRepository, pendingEditSubmittedFieldRepository, + pendingEditChargeRepository, pendingEditGlMappingRepository, commandSourceRepository); } } diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEdit.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEdit.java index dfc0c6347..744f18cad 100644 --- a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEdit.java +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEdit.java @@ -39,7 +39,7 @@ import org.apache.fineract.useradministration.domain.AppUser; * (covering charges, GL/accounting mappings and other related tables, not just scalar columns). */ @Entity -@Table(name = "m_savings_product_temp") +@Table(name = "m_saving_product_temp") @Getter @Setter public class ProductPendingEdit extends AbstractPersistableCustom { @@ -61,17 +61,17 @@ public class ProductPendingEdit extends AbstractPersistableCustom { private String productType; @ManyToOne - @JoinColumn(name = "maker_id", nullable = false) + @JoinColumn(name = "createdby_id", nullable = false) private AppUser maker; @ManyToOne - @JoinColumn(name = "checker_id") + @JoinColumn(name = "lastmodifiedby_id") private AppUser checker; - @Column(name = "created_on", nullable = false) + @Column(name = "created_date", nullable = false) private LocalDateTime createdOn; - @Column(name = "checked_on") + @Column(name = "lastmodified_date") private LocalDateTime checkedOn; @Column(name = "status", nullable = false, length = 20) @@ -80,9 +80,7 @@ public class ProductPendingEdit extends AbstractPersistableCustom { @Column(name = "reject_reason", length = 500) private String rejectReason; - @Lob - @Column(name = "proposed_payload_json", nullable = false) - private String proposedPayloadJson; + /** * ":" while PENDING, NULL otherwise - the real DB-level lock. Unique constraints treat @@ -130,11 +128,10 @@ public class ProductPendingEdit extends AbstractPersistableCustom { protected ProductPendingEdit() {} - public ProductPendingEdit(final String productType, final Long productId, final AppUser maker, final String proposedPayloadJson) { + public ProductPendingEdit(final String productType, final Long productId, final AppUser maker) { this.productType = productType; this.productId = productId; this.maker = maker; - this.proposedPayloadJson = proposedPayloadJson; this.createdOn = LocalDateTime.now(); this.status = STATUS_PENDING; this.pendingLockKey = lockKeyFor(productType, productId); @@ -176,9 +173,7 @@ public class ProductPendingEdit extends AbstractPersistableCustom { return this.rejectReason; } - public String getProposedPayloadJson() { - return this.proposedPayloadJson; - } + public Long getCommandSourceId() { return this.commandSourceId; diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEditFieldMapper.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEditFieldMapper.java index 3eec82403..030703976 100644 --- a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEditFieldMapper.java +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ProductPendingEditFieldMapper.java @@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.savings.domain; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import java.math.BigDecimal; import java.time.LocalDate; import java.util.HashSet; import java.util.List; @@ -308,10 +309,25 @@ public final class ProductPendingEditFieldMapper { f.setAllowDebitTxnAfterDueDate(command.booleanObjectValueOfParameterNamed("allowDebitTxnAfterDueDate")); present.add("allowDebitTxnAfterDueDate"); } + if (command.hasParameter("depositTypeEnum")) { + f.setDepositTypeEnum(command.integerValueOfParameterNamed("depositTypeEnum")); + present.add("depositTypeEnum"); + } + if (command.hasParameter("withdrawalFeeAmount")) { + f.setWithdrawalFeeAmount(command.bigDecimalValueOfParameterNamed("withdrawalFeeAmount")); + present.add("withdrawalFeeAmount"); + } + if (command.hasParameter("withdrawalFeeTypeEnum")) { + f.setWithdrawalFeeTypeEnum(command.integerValueOfParameterNamed("withdrawalFeeTypeEnum")); + present.add("withdrawalFeeTypeEnum"); + } return present; } public static void writeCoreFieldsToJson(final JsonObject root, final SavingsCoreEditFields f, final Set submitted) { + if (f == null) { + return; + } putString(root, submitted, "name", f.getName()); putString(root, submitted, "shortName", f.getShortName()); putString(root, submitted, "description", f.getDescription()); @@ -377,6 +393,9 @@ public final class ProductPendingEditFieldMapper { putBoolean(root, submitted, "noPenaltyNomineeHeirClosure", f.getNoPenaltyNomineeHeirClosure()); putBoolean(root, submitted, "deceasedSettlement", f.getDeceasedSettlement()); putBoolean(root, submitted, "allowDebitTxnAfterDueDate", f.getAllowDebitTxnAfterDueDate()); + putNumber(root, submitted, "depositTypeEnum", f.getDepositTypeEnum()); + putNumber(root, submitted, "withdrawalFeeAmount", f.getWithdrawalFeeAmount()); + putNumber(root, submitted, "withdrawalFeeTypeEnum", f.getWithdrawalFeeTypeEnum()); } // ---- accountFacilityConfig (1:1 container) -------------------------------------------------- @@ -405,7 +424,7 @@ public final class ProductPendingEditFieldMapper { } public static void writeFacilityConfigToJson(final JsonObject root, final FacilityConfigEditFields f, final boolean submitted) { - if (!submitted) { + if (f == null || !submitted) { return; } final JsonObject config = new JsonObject(); @@ -460,7 +479,7 @@ public final class ProductPendingEditFieldMapper { } public static void writeDepositLimitKycToJson(final JsonObject root, final DepositLimitKycEditFields f, final boolean submitted) { - if (!submitted) { + if (f == null || !submitted) { return; } final JsonObject kyc = new JsonObject(); @@ -505,6 +524,9 @@ public final class ProductPendingEditFieldMapper { } public static void writeGlAccountMappingToJson(final JsonObject root, final GlAccountMappingEditFields f) { + if (f == null) { + return; + } putNumber(root, "savingsReferenceAccountId", f.getSavingsReferenceAccountId()); putNumber(root, "savingsControlAccountId", f.getSavingsControlAccountId()); putNumber(root, "incomeFromFeeAccountId", f.getIncomeFromFeeAccountId()); @@ -693,4 +715,282 @@ public final class ProductPendingEditFieldMapper { root.addProperty(name, value); } } + + public static Set populateCurrentOnlyFields(final JsonCommand command, final CurrentOnlyEditFields f) { + final Set present = new java.util.HashSet<>(); + if (command.hasParameter("avgBalanceCalcPeriod")) { + f.setAvgBalanceCalcPeriod(command.stringValueOfParameterNamed("avgBalanceCalcPeriod")); + present.add("avgBalanceCalcPeriod"); + } + if (command.hasParameter("validForPeriod")) { + f.setValidForPeriod(command.stringValueOfParameterNamed("validForPeriod")); + present.add("validForPeriod"); + } + if (command.hasParameter("odAccountClosedZeroBalanceInDayClosing")) { + f.setOdAccountClosedZeroBalanceInDayClosing(command.booleanObjectValueOfParameterNamed("odAccountClosedZeroBalanceInDayClosing")); + present.add("odAccountClosedZeroBalanceInDayClosing"); + } + return present; + } + + public static void writeCurrentOnlyFieldsToJson(final JsonObject root, final CurrentOnlyEditFields f, final Set submitted) { + if (f == null) { + return; + } + putString(root, submitted, "avgBalanceCalcPeriod", f.getAvgBalanceCalcPeriod()); + putString(root, submitted, "validForPeriod", f.getValidForPeriod()); + putBoolean(root, submitted, "odAccountClosedZeroBalanceInDayClosing", f.getOdAccountClosedZeroBalanceInDayClosing()); + } + + public static Set populateShareOnlyFields(final JsonCommand command, final ShareOnlyEditFields f) { + final Set present = new java.util.HashSet<>(); + if (command.hasParameter("totalShares")) { + f.setTotalShares(command.longValueOfParameterNamed("totalShares")); + present.add("totalShares"); + } + if (command.hasParameter("totalSharesIssued") || command.hasParameter("issuedShares")) { + final Long val = command.hasParameter("totalSharesIssued") ? command.longValueOfParameterNamed("totalSharesIssued") : command.longValueOfParameterNamed("issuedShares"); + f.setTotalSharesIssued(val); + present.add("totalSharesIssued"); + } + if (command.hasParameter("unitPrice")) { + f.setUnitPrice(command.bigDecimalValueOfParameterNamed("unitPrice")); + present.add("unitPrice"); + } + if (command.hasParameter("minimumShares") || command.hasParameter("minimumClientShares")) { + final Long val = command.hasParameter("minimumShares") ? command.longValueOfParameterNamed("minimumShares") : command.longValueOfParameterNamed("minimumClientShares"); + f.setMinimumShares(val); + present.add("minimumShares"); + } + if (command.hasParameter("nominalShares") || command.hasParameter("nominalClientShares")) { + final Long val = command.hasParameter("nominalShares") ? command.longValueOfParameterNamed("nominalShares") : command.longValueOfParameterNamed("nominalClientShares"); + f.setNominalShares(val); + present.add("nominalShares"); + } + if (command.hasParameter("maximumShares") || command.hasParameter("maximumClientShares")) { + final Long val = command.hasParameter("maximumShares") ? command.longValueOfParameterNamed("maximumShares") : command.longValueOfParameterNamed("maximumClientShares"); + f.setMaximumShares(val); + present.add("maximumShares"); + } + if (command.hasParameter("allowDividendCalcForInactiveClients") || command.hasParameter("allowDividendsInactiveClients")) { + final Boolean val = command.hasParameter("allowDividendCalcForInactiveClients") ? command.booleanObjectValueOfParameterNamed("allowDividendCalcForInactiveClients") : command.booleanObjectValueOfParameterNamed("allowDividendsInactiveClients"); + f.setAllowDividendCalcForInactiveClients(val); + present.add("allowDividendCalcForInactiveClients"); + } + if (command.hasParameter("minimumActivePeriodForDividends") || command.hasParameter("minimumActivePeriodFrequency")) { + final Integer val = command.hasParameter("minimumActivePeriodForDividends") ? command.integerValueOfParameterNamed("minimumActivePeriodForDividends") : command.integerValueOfParameterNamed("minimumActivePeriodFrequency"); + f.setMinimumActivePeriodForDividends(val); + present.add("minimumActivePeriodForDividends"); + } + if (command.hasParameter("minimumActivePeriodFrequencyType") || command.hasParameter("minimumActivePeriodFrequencyEnum")) { + final Integer val = command.hasParameter("minimumActivePeriodFrequencyType") ? command.integerValueOfParameterNamed("minimumActivePeriodFrequencyType") : command.integerValueOfParameterNamed("minimumActivePeriodFrequencyEnum"); + f.setMinimumActivePeriodFrequencyType(val); + present.add("minimumActivePeriodFrequencyType"); + } + if (command.hasParameter("shareLockinPeriod")) { + f.setShareLockinPeriod(command.integerValueOfParameterNamed("shareLockinPeriod")); + present.add("shareLockinPeriod"); + } + if (command.hasParameter("shareLockinPeriodFrequencyType")) { + f.setShareLockinPeriodFrequencyType(command.integerValueOfParameterNamed("shareLockinPeriodFrequencyType")); + present.add("shareLockinPeriodFrequencyType"); + } + if (command.hasParameter("externalId")) { + f.setExternalId(command.stringValueOfParameterNamed("externalId")); + present.add("externalId"); + } + if (command.hasParameter("startDate")) { + f.setStartDate(command.localDateValueOfParameterNamed("startDate")); + present.add("startDate"); + } + if (command.hasParameter("totalsubscribedShares")) { + f.setTotalsubscribedShares(command.longValueOfParameterNamed("totalsubscribedShares")); + present.add("totalsubscribedShares"); + } + if (command.hasParameter("capitalAmount") || command.hasParameter("shareCapital")) { + final BigDecimal val = command.hasParameter("capitalAmount") ? command.bigDecimalValueOfParameterNamed("capitalAmount") : command.bigDecimalValueOfParameterNamed("shareCapital"); + f.setCapitalAmount(val); + present.add("shareCapital"); + } + return present; + } + + public static void writeShareOnlyFieldsToJson(final JsonObject root, final ShareOnlyEditFields f, final Set submitted) { + if (f == null) { + return; + } + putNumber(root, submitted, "totalShares", f.getTotalShares()); + putNumber(root, submitted, "totalSharesIssued", f.getTotalSharesIssued()); + putNumber(root, submitted, "unitPrice", f.getUnitPrice()); + putNumber(root, submitted, "minimumShares", f.getMinimumShares()); + putNumber(root, submitted, "nominalShares", f.getNominalShares()); + putNumber(root, submitted, "maximumShares", f.getMaximumShares()); + putBoolean(root, submitted, "allowDividendCalcForInactiveClients", f.getAllowDividendCalcForInactiveClients()); + putNumber(root, submitted, "minimumActivePeriodForDividends", f.getMinimumActivePeriodForDividends()); + putNumber(root, submitted, "minimumActivePeriodFrequencyType", f.getMinimumActivePeriodFrequencyType()); + putNumber(root, submitted, "shareLockinPeriod", f.getShareLockinPeriod()); + putNumber(root, submitted, "shareLockinPeriodFrequencyType", f.getShareLockinPeriodFrequencyType()); + putString(root, submitted, "externalId", f.getExternalId()); + putDate(root, submitted, "startDate", f.getStartDate()); + putNumber(root, submitted, "totalsubscribedShares", f.getTotalsubscribedShares()); + if (submitted.contains("capitalAmount") || submitted.contains("shareCapital")) { + if (f.getCapitalAmount() != null) { + root.addProperty("shareCapital", f.getCapitalAmount()); + } + } + } + + public static Set populateFixedDepositFields(final JsonCommand command, final FixedDepositEditFields f) { + final Set present = new java.util.HashSet<>(); + if (command.hasParameter("fdType")) { + f.setFdType(command.stringValueOfParameterNamed("fdType")); + present.add("fdType"); + } + if (command.hasParameter("preClosurePenalApplicable")) { + f.setPreClosurePenalApplicable(command.booleanObjectValueOfParameterNamed("preClosurePenalApplicable")); + present.add("preClosurePenalApplicable"); + } + if (command.hasParameter("preClosurePenalInterest")) { + f.setPreClosurePenalInterest(command.bigDecimalValueOfParameterNamed("preClosurePenalInterest")); + present.add("preClosurePenalInterest"); + } + if (command.hasParameter("preClosurePenalInterestOnTypeId")) { + f.setPreClosurePenalInterestOnTypeId(command.integerValueOfParameterNamed("preClosurePenalInterestOnTypeId")); + present.add("preClosurePenalInterestOnTypeId"); + } + if (command.hasParameter("minDepositTerm")) { + f.setMinDepositTerm(command.integerValueOfParameterNamed("minDepositTerm")); + present.add("minDepositTerm"); + } + if (command.hasParameter("maxDepositTerm")) { + f.setMaxDepositTerm(command.integerValueOfParameterNamed("maxDepositTerm")); + present.add("maxDepositTerm"); + } + if (command.hasParameter("minDepositTermTypeId")) { + f.setMinDepositTermTypeId(command.integerValueOfParameterNamed("minDepositTermTypeId")); + present.add("minDepositTermTypeId"); + } + if (command.hasParameter("maxDepositTermTypeId")) { + f.setMaxDepositTermTypeId(command.integerValueOfParameterNamed("maxDepositTermTypeId")); + present.add("maxDepositTermTypeId"); + } + if (command.hasParameter("inMultiplesOfDepositTerm")) { + f.setInMultiplesOfDepositTerm(command.integerValueOfParameterNamed("inMultiplesOfDepositTerm")); + present.add("inMultiplesOfDepositTerm"); + } + if (command.hasParameter("inMultiplesOfDepositTermTypeId")) { + f.setInMultiplesOfDepositTermTypeId(command.integerValueOfParameterNamed("inMultiplesOfDepositTermTypeId")); + present.add("inMultiplesOfDepositTermTypeId"); + } + if (command.hasParameter("minDepositAmount")) { + f.setMinDepositAmount(command.bigDecimalValueOfParameterNamed("minDepositAmount")); + present.add("minDepositAmount"); + } + if (command.hasParameter("maxDepositAmount")) { + f.setMaxDepositAmount(command.bigDecimalValueOfParameterNamed("maxDepositAmount")); + present.add("maxDepositAmount"); + } + if (command.hasParameter("depositAmount")) { + f.setDepositAmount(command.bigDecimalValueOfParameterNamed("depositAmount")); + present.add("depositAmount"); + } + return present; + } + + public static void writeFixedDepositFieldsToJson(final JsonObject root, final FixedDepositEditFields f, final Set submitted) { + if (f == null) { + return; + } + if (f.getFdType() != null) putString(root, submitted, "fdType", f.getFdType()); + if (f.getPreClosurePenalApplicable() != null) putBoolean(root, submitted, "preClosurePenalApplicable", f.getPreClosurePenalApplicable()); + if (f.getPreClosurePenalInterest() != null) putNumber(root, submitted, "preClosurePenalInterest", f.getPreClosurePenalInterest()); + if (f.getPreClosurePenalInterestOnTypeId() != null) putNumber(root, submitted, "preClosurePenalInterestOnTypeId", f.getPreClosurePenalInterestOnTypeId()); + if (f.getMinDepositTerm() != null) putNumber(root, submitted, "minDepositTerm", f.getMinDepositTerm()); + if (f.getMaxDepositTerm() != null) putNumber(root, submitted, "maxDepositTerm", f.getMaxDepositTerm()); + if (f.getMinDepositTermTypeId() != null) putNumber(root, submitted, "minDepositTermTypeId", f.getMinDepositTermTypeId()); + if (f.getMaxDepositTermTypeId() != null) putNumber(root, submitted, "maxDepositTermTypeId", f.getMaxDepositTermTypeId()); + if (f.getInMultiplesOfDepositTerm() != null) putNumber(root, submitted, "inMultiplesOfDepositTerm", f.getInMultiplesOfDepositTerm()); + if (f.getInMultiplesOfDepositTermTypeId() != null) putNumber(root, submitted, "inMultiplesOfDepositTermTypeId", f.getInMultiplesOfDepositTermTypeId()); + if (f.getMinDepositAmount() != null) putNumber(root, submitted, "minDepositAmount", f.getMinDepositAmount()); + if (f.getMaxDepositAmount() != null) putNumber(root, submitted, "maxDepositAmount", f.getMaxDepositAmount()); + if (f.getDepositAmount() != null) putNumber(root, submitted, "depositAmount", f.getDepositAmount()); + } + + public static Set populateRecurringDepositFields(final JsonCommand command, final RecurringDepositEditFields f) { + final Set present = new java.util.HashSet<>(); + if (command.hasParameter("isMandatoryDeposit")) { + f.setIsMandatoryDeposit(command.booleanObjectValueOfParameterNamed("isMandatoryDeposit")); + present.add("isMandatoryDeposit"); + } + if (command.hasParameter("allowWithdrawal")) { + f.setAllowWithdrawal(command.booleanObjectValueOfParameterNamed("allowWithdrawal")); + present.add("allowWithdrawal"); + } + if (command.hasParameter("adjustAdvanceTowardsFuturePayments")) { + f.setAdjustAdvanceTowardsFuturePayments(command.booleanObjectValueOfParameterNamed("adjustAdvanceTowardsFuturePayments")); + present.add("adjustAdvanceTowardsFuturePayments"); + } + return present; + } + + public static void writeRecurringDepositFieldsToJson(final JsonObject root, final RecurringDepositEditFields f, final Set submitted) { + if (f == null) { + return; + } + if (f.getIsMandatoryDeposit() != null) putBoolean(root, submitted, "isMandatoryDeposit", f.getIsMandatoryDeposit()); + if (f.getAllowWithdrawal() != null) putBoolean(root, submitted, "allowWithdrawal", f.getAllowWithdrawal()); + if (f.getAdjustAdvanceTowardsFuturePayments() != null) putBoolean(root, submitted, "adjustAdvanceTowardsFuturePayments", f.getAdjustAdvanceTowardsFuturePayments()); + } + + public static java.util.Map reconstructProposedChangesMap( + final ProductPendingEdit pendingEdit, + final Set submittedFields, + final ProductPendingEditChargeRepository pendingEditChargeRepository, + final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository, + final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository, + final com.google.gson.Gson gson) { + final JsonObject root = new JsonObject(); + root.addProperty("locale", "en"); + root.addProperty("dateFormat", "yyyy-MM-dd"); + + final String productType = pendingEdit.getProductType(); + if ((ProductPendingEdit.TYPE_SAVINGS.equals(productType) || ProductPendingEdit.TYPE_CURRENT.equals(productType) || ProductPendingEdit.TYPE_FD.equals(productType) || ProductPendingEdit.TYPE_RD.equals(productType)) && pendingEdit.getCoreFields() != null) { + writeCoreFieldsToJson(root, pendingEdit.getCoreFields(), submittedFields); + } + if (ProductPendingEdit.TYPE_CURRENT.equals(productType) && pendingEdit.getCurrentOnlyFields() != null) { + writeCurrentOnlyFieldsToJson(root, pendingEdit.getCurrentOnlyFields(), submittedFields); + } + if (ProductPendingEdit.TYPE_SAVINGS.equals(productType) || ProductPendingEdit.TYPE_CURRENT.equals(productType)) { + if (submittedFields.contains("accountFacilityConfig") && pendingEdit.getFacilityConfigFields() != null) { + writeFacilityConfigToJson(root, pendingEdit.getFacilityConfigFields(), true); + } + if (submittedFields.contains("depositLimitKyc") && pendingEdit.getDepositLimitKycFields() != null) { + writeDepositLimitKycToJson(root, pendingEdit.getDepositLimitKycFields(), true); + } + if (submittedFields.contains("accountFacilityLimits") && pendingEditFacilityLimitRepository != null) { + writeFacilityLimitsToJson(root, pendingEditFacilityLimitRepository.findByPendingEditId(pendingEdit.getId())); + } + } + if ((ProductPendingEdit.TYPE_FD.equals(productType) || ProductPendingEdit.TYPE_RD.equals(productType)) && pendingEdit.getFixedDepositFields() != null) { + writeFixedDepositFieldsToJson(root, pendingEdit.getFixedDepositFields(), submittedFields); + } + if (ProductPendingEdit.TYPE_RD.equals(productType) && pendingEdit.getRecurringDepositFields() != null) { + writeRecurringDepositFieldsToJson(root, pendingEdit.getRecurringDepositFields(), submittedFields); + } + if (ProductPendingEdit.TYPE_SHARE.equals(productType) && pendingEdit.getShareOnlyFields() != null) { + writeShareOnlyFieldsToJson(root, pendingEdit.getShareOnlyFields(), submittedFields); + } + if (submittedFields.contains("glAccountMapping") && pendingEdit.getGlAccountMappingFields() != null) { + writeGlAccountMappingToJson(root, pendingEdit.getGlAccountMappingFields()); + } + if (submittedFields.contains("charges") && pendingEditChargeRepository != null) { + writeChargesToJson(root, pendingEditChargeRepository.findByPendingEditId(pendingEdit.getId())); + } + if (submittedFields.contains("glArrayMappings") && pendingEditGlMappingRepository != null) { + writeGlArrayMappingsToJson(root, pendingEditGlMappingRepository.findByPendingEditId(pendingEdit.getId())); + } + + final String json = gson.toJson(root); + return gson.fromJson(json, new com.google.gson.reflect.TypeToken>() {}.getType()); + } } diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsCoreEditFields.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsCoreEditFields.java index 086038946..b531290e5 100644 --- a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsCoreEditFields.java +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsCoreEditFields.java @@ -48,28 +48,28 @@ public class SavingsCoreEditFields { @Column(name = "currency_code") private String currencyCode; - @Column(name = "digits_after_decimal") + @Column(name = "currency_digits") private Integer digitsAfterDecimal; - @Column(name = "in_multiples_of") + @Column(name = "currency_multiplesof") private Integer inMultiplesOf; - @Column(name = "accounting_rule") + @Column(name = "accounting_type") private Integer accountingRule; @Column(name = "nominal_annual_interest_rate") private BigDecimal nominalAnnualInterestRate; - @Column(name = "interest_compounding_period_type") + @Column(name = "interest_compounding_period_enum") private Integer interestCompoundingPeriodType; - @Column(name = "interest_posting_period_type") + @Column(name = "interest_posting_period_enum") private Integer interestPostingPeriodType; - @Column(name = "interest_calculation_type") + @Column(name = "interest_calculation_type_enum") private Integer interestCalculationType; - @Column(name = "interest_calculation_days_in_year_type") + @Column(name = "interest_calculation_days_in_year_type_enum") private Integer interestCalculationDaysInYearType; @Column(name = "min_required_opening_balance") @@ -78,10 +78,10 @@ public class SavingsCoreEditFields { @Column(name = "lockin_period_frequency") private Integer lockinPeriodFrequency; - @Column(name = "lockin_period_frequency_type") + @Column(name = "lockin_period_frequency_enum") private Integer lockinPeriodFrequencyType; - @Column(name = "withdrawal_fee_for_transfers") + @Column(name = "withdrawal_fee_for_transfer") private Boolean withdrawalFeeForTransfers; @Column(name = "allow_overdraft") @@ -102,7 +102,7 @@ public class SavingsCoreEditFields { @Column(name = "min_required_balance") private BigDecimal minRequiredBalance; - @Column(name = "lien_allowed") + @Column(name = "is_lien_allowed") private Boolean lienAllowed; @Column(name = "max_allowed_lien_limit") @@ -111,7 +111,7 @@ public class SavingsCoreEditFields { @Column(name = "min_balance_for_interest_calculation") private BigDecimal minBalanceForInterestCalculation; - @Column(name = "with_hold_tax") + @Column(name = "withhold_tax") private Boolean withHoldTax; @Column(name = "tax_group_id") @@ -231,4 +231,13 @@ public class SavingsCoreEditFields { @Column(name = "allow_debit_txn_after_due_date") private Boolean allowDebitTxnAfterDueDate; + + @Column(name = "deposit_type_enum") + private Integer depositTypeEnum; + + @Column(name = "withdrawal_fee_amount") + private BigDecimal withdrawalFeeAmount; + + @Column(name = "withdrawal_fee_type_enum") + private Integer withdrawalFeeTypeEnum; } diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ShareOnlyEditFields.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ShareOnlyEditFields.java index 0fbb74896..2a614fe95 100644 --- a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ShareOnlyEditFields.java +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/domain/ShareOnlyEditFields.java @@ -32,28 +32,28 @@ public class ShareOnlyEditFields { @Column(name = "total_shares") private Long totalShares; - @Column(name = "total_shares_issued") + @Column(name = "issued_shares") private Long totalSharesIssued; @Column(name = "unit_price") private BigDecimal unitPrice; - @Column(name = "minimum_shares") + @Column(name = "minimum_client_shares") private Long minimumShares; - @Column(name = "nominal_shares") + @Column(name = "nominal_client_shares") private Long nominalShares; - @Column(name = "maximum_shares") + @Column(name = "maximum_client_shares") private Long maximumShares; - @Column(name = "allow_dividend_calc_for_inactive_clients") + @Column(name = "allow_dividends_inactive_clients") private Boolean allowDividendCalcForInactiveClients; - @Column(name = "minimum_active_period_for_dividends") + @Column(name = "minimum_active_period_frequency") private Integer minimumActivePeriodForDividends; - @Column(name = "minimum_active_period_frequency_type") + @Column(name = "minimum_active_period_frequency_enum") private Integer minimumActivePeriodFrequencyType; @Column(name = "share_lockin_period") @@ -61,4 +61,16 @@ public class ShareOnlyEditFields { @Column(name = "share_lockin_period_frequency_type") private Integer shareLockinPeriodFrequencyType; + + @Column(name = "external_id") + private String externalId; + + @Column(name = "start_date") + private java.time.LocalDate startDate; + + @Column(name = "totalsubscribed_shares") + private Long totalsubscribedShares; + + @Column(name = "capital_amount") + private BigDecimal capitalAmount; } diff --git a/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/module-changelog-master.xml b/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/module-changelog-master.xml index de44a7419..c717feeaa 100644 --- a/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/module-changelog-master.xml +++ b/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/module-changelog-master.xml @@ -49,4 +49,6 @@ + + diff --git a/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/parts/2028_update_saving_product_temp_table.xml b/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/parts/2028_update_saving_product_temp_table.xml new file mode 100644 index 000000000..c6bfea24a --- /dev/null +++ b/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/parts/2028_update_saving_product_temp_table.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/parts/2029_remove_proposed_payload_json.xml b/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/parts/2029_remove_proposed_payload_json.xml new file mode 100644 index 000000000..19a3970fe --- /dev/null +++ b/fineract-savings/src/main/resources/db/changelog/tenant/module/savings/parts/parts/2029_remove_proposed_payload_json.xml @@ -0,0 +1,30 @@ + + + + + + + + + + +