merge(release): merge feature/minor-update into release/share-account-module
Combines election module + UAT catch-up migrations (0338-0367) with minor-update changes: soft delete for tellers (0338), share linking for current account (0339), terminate flag for savings/current (0340). Resolved changelog conflict by appending minor-update entries after 0367. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -120,7 +120,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
||||
|
||||
try {
|
||||
final TellerMapper tm = new TellerMapper();
|
||||
final String sql = "select " + tm.schema() + " where t.id = ?";
|
||||
final String sql = "select " + tm.schema() + " where t.id = ? and t.is_deleted = false";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, tm, new Object[] { tellerId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
@@ -198,7 +198,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
|
||||
hierarchySearchString = hierarchy + "%";
|
||||
}
|
||||
final TellerMapper tm = new TellerMapper();
|
||||
final String sql = "select " + tm.schema() + "where o.hierarchy like ? order by o.hierarchy";
|
||||
final String sql = "select " + tm.schema() + "where o.hierarchy like ? and t.is_deleted = false order by o.hierarchy";
|
||||
|
||||
return this.jdbcTemplate.query(sql, tm, new Object[] { hierarchySearchString }); // NOSONAR
|
||||
}
|
||||
|
||||
+7
-12
@@ -46,6 +46,7 @@ import org.apache.fineract.organisation.teller.domain.Teller;
|
||||
import org.apache.fineract.organisation.teller.domain.TellerRepositoryWrapper;
|
||||
import org.apache.fineract.organisation.teller.exception.CashierExistForTellerException;
|
||||
import org.apache.fineract.organisation.teller.exception.CashierNotFoundException;
|
||||
import org.apache.fineract.organisation.teller.exception.TellerAlreadyExistsException;
|
||||
import org.apache.fineract.organisation.teller.serialization.TellerCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
@@ -71,6 +72,10 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
if (this.tellerRepositoryWrapper.existsAny()) {
|
||||
throw new TellerAlreadyExistsException();
|
||||
}
|
||||
|
||||
final Long officeId = command.longValueOfParameterNamed("officeId");
|
||||
|
||||
this.fromApiJsonDeserializer.validateForCreateAndUpdateTeller(command.json());
|
||||
@@ -152,22 +157,12 @@ public class TellerWritePlatformServiceJpaImpl implements TellerWritePlatformSer
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult deleteTeller(Long tellerId) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
Teller teller = tellerRepositoryWrapper.findOneWithNotFoundDetection(tellerId);
|
||||
Set<Cashier> isTellerIdPresentInCashier = teller.getCashiers();
|
||||
|
||||
for (final Cashier tellerIdInCashier : isTellerIdPresentInCashier) {
|
||||
if (tellerIdInCashier.getTeller().getId().toString().equalsIgnoreCase(tellerId.toString())) {
|
||||
throw new CashierExistForTellerException(tellerId);
|
||||
}
|
||||
|
||||
}
|
||||
tellerRepositoryWrapper.delete(teller);
|
||||
teller.delete();
|
||||
tellerRepositoryWrapper.saveAndFlush(teller);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(teller.getId()) //
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.account.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import com.google.gson.JsonElement;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/accounts/{accountId}/terminate")
|
||||
@Component
|
||||
@Tag(name = "Account Terminate", description = "Unified terminate endpoint for Savings, Current, FD and RD accounts.")
|
||||
@RequiredArgsConstructor
|
||||
public class AccountTerminateApiResource {
|
||||
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Terminate an account", description = "Terminates a Savings, Current, Fixed Deposit or Recurring Deposit account. Pass accountType in the request body: SAVINGS | CURRENT | FD | RD.")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String terminate(@PathParam("accountId") final Long accountId,
|
||||
final String apiRequestBodyAsJson) {
|
||||
|
||||
final String jsonBody = StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson;
|
||||
|
||||
final JsonElement root = this.fromJsonHelper.parse(jsonBody);
|
||||
final String accountType = this.fromJsonHelper.extractStringNamed("accountType", root);
|
||||
String entityName = "SAVINGSACCOUNT";
|
||||
if ("CURRENT".equalsIgnoreCase(accountType)) {
|
||||
entityName = "CURRENTACCOUNT";
|
||||
} else if ("FD".equalsIgnoreCase(accountType)) {
|
||||
entityName = "FIXEDDEPOSITACCOUNT";
|
||||
} else if ("RD".equalsIgnoreCase(accountType)) {
|
||||
entityName = "RECURRINGDEPOSITACCOUNT";
|
||||
}
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.withJson(jsonBody)
|
||||
.terminateAccount(accountId)
|
||||
.withEntityName(entityName)
|
||||
.build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.account.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTerminateWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "ACCOUNT", action = "TERMINATE")
|
||||
public class AccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final AccountTerminateWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public AccountTerminateCommandHandler(final AccountTerminateWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.account.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface AccountTerminateWritePlatformService {
|
||||
|
||||
CommandProcessingResult terminate(Long accountId, JsonCommand command);
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.account.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Routes terminate to the correct account service based on the
|
||||
* "accountType" field in the JSON body:
|
||||
* SAVINGS → m_savings_account (depositType = savings)
|
||||
* FD → m_savings_account (depositType = 200)
|
||||
* RD → m_savings_account (depositType = 300)
|
||||
* CURRENT → m_current_account
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AccountTerminateWritePlatformServiceImpl implements AccountTerminateWritePlatformService {
|
||||
|
||||
private static final String PARAM_ACCOUNT_TYPE = "accountType";
|
||||
|
||||
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult terminate(final Long accountId, final JsonCommand command) {
|
||||
final String accountType = command.stringValueOfParameterNamed(PARAM_ACCOUNT_TYPE);
|
||||
|
||||
if (accountType == null || accountType.isBlank()) {
|
||||
throw new PlatformDataIntegrityException(
|
||||
"error.msg.account.terminate.accountType.required",
|
||||
"accountType is required: SAVINGS, CURRENT, FD or RD");
|
||||
}
|
||||
|
||||
return switch (accountType.toUpperCase()) {
|
||||
case "CURRENT" -> currentAccountWritePlatformService.terminateAccount(accountId, command);
|
||||
case "SAVINGS", "FD", "RD" -> savingsAccountWritePlatformService.terminate(accountId, command);
|
||||
default -> throw new PlatformDataIntegrityException(
|
||||
"error.msg.account.terminate.accountType.invalid",
|
||||
"Unknown accountType '" + accountType + "'. Expected SAVINGS, CURRENT, FD or RD.");
|
||||
};
|
||||
}
|
||||
}
|
||||
+112
@@ -37,20 +37,26 @@ import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.Collection;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.ResourceNotFoundException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.products.constants.ProductsApiConstants;
|
||||
import org.apache.fineract.portfolio.products.data.ProductData;
|
||||
import org.apache.fineract.portfolio.products.service.ShareProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.shareproducts.service.ShareProductWritePlatformService;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -61,11 +67,26 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class ProductsApiResource {
|
||||
|
||||
private static final String SHARE_PRODUCT_TYPE = "share";
|
||||
/**
|
||||
* These product types have their own dedicated endpoints (/savingsproducts/{id}, /currentproducts/{id},
|
||||
* /fixeddepositproducts/{id}, /recurringdepositproducts/{id}) which correctly route edits through
|
||||
* submitEditForApproval - the pending-edit row is the lock. This generic /v1/products/{type}/{productId} PUT
|
||||
* falls through to the native command-processing pipeline for any type not special-cased above, which would
|
||||
* bypass that lock entirely for these types. The Angular frontend never calls this generic path for them (only
|
||||
* for "share"), so blocking them here closes the bypass without removing any real functionality.
|
||||
*/
|
||||
private static final java.util.Set<String> PRODUCT_TYPES_WITH_DEDICATED_LOCKED_ENDPOINT = java.util.Set.of("savings", "current",
|
||||
"fixeddeposit", "recurringdeposit");
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final DefaultToApiJsonSerializer<ProductData> toApiJsonSerializer;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ShareProductWritePlatformService shareProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -171,9 +192,100 @@ public class ProductsApiResource {
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
|
||||
if (SHARE_PRODUCT_TYPE.equalsIgnoreCase(productType)) {
|
||||
// Bypasses the native command-source/maker-checker pipeline by design, same as Savings/Current
|
||||
// Products: edits are held in m_savings_product_temp (a pending row there is itself the lock) and
|
||||
// only applied to m_share_product on approval - see the pending/approve/reject endpoints below.
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = this.shareProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
if (PRODUCT_TYPES_WITH_DEDICATED_LOCKED_ENDPOINT.contains(productType.toLowerCase(java.util.Locale.ROOT))) {
|
||||
throw new ResourceNotFoundException("error.msg.product.generic.update.not.supported",
|
||||
"Updating a " + productType + " product through this generic endpoint is not supported - use its dedicated endpoint",
|
||||
new Object[] { productType });
|
||||
}
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateProduct(productType, productId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Share Products pending edit approval")
|
||||
public String retrieveAllPending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
try {
|
||||
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
|
||||
ShareProductReadPlatformService service = (ShareProductReadPlatformService) this.applicationContext.getBean(serviceName);
|
||||
final Collection<SavingsProductTempData> pendingEdits = service.retrieveAllPendingEdits();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
} catch (BeansException e) {
|
||||
throw new ResourceNotFoundException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Share Product")
|
||||
public String retrievePending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId, @Context final UriInfo uriInfo) {
|
||||
this.platformSecurityContext.authenticatedUser();
|
||||
try {
|
||||
String serviceName = productType + ProductsApiConstants.READPLATFORM_NAME;
|
||||
ShareProductReadPlatformService service = (ShareProductReadPlatformService) this.applicationContext.getBean(serviceName);
|
||||
final SavingsProductTempData pendingEdit = service.retrievePendingEdit(productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
} catch (BeansException e) {
|
||||
throw new ResourceNotFoundException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Share Product edit")
|
||||
public String approvePending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
if (this.platformSecurityContext.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_SHAREPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve share products");
|
||||
}
|
||||
final CommandProcessingResult result = this.shareProductWritePlatformService.approveEdit(productId);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Share Product edit")
|
||||
public String rejectPending(@PathParam("type") @Parameter(description = "type") final String productType,
|
||||
@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.platformSecurityContext.authenticatedUser().hasNotPermissionForAnyOf("REJECT_SHAREPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject share products");
|
||||
}
|
||||
String rejectReason = null;
|
||||
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||
}
|
||||
}
|
||||
final CommandProcessingResult result = this.shareProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -22,6 +22,7 @@ import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.portfolio.products.data.ProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
|
||||
public interface ShareProductReadPlatformService {
|
||||
|
||||
@@ -34,4 +35,8 @@ public interface ShareProductReadPlatformService {
|
||||
Set<String> getResponseDataParams();
|
||||
|
||||
Collection<ProductData> retrieveAllForLookup();
|
||||
|
||||
Collection<SavingsProductTempData> retrieveAllPendingEdits();
|
||||
|
||||
SavingsProductTempData retrievePendingEdit(Long productId);
|
||||
}
|
||||
|
||||
+28
-21
@@ -53,6 +53,9 @@ import org.apache.fineract.infrastructure.security.service.SqlValidator;
|
||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.CurrentAccountTemplateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -72,6 +75,7 @@ public class CurrentAccountsApiResource {
|
||||
private final SqlValidator sqlValidator;
|
||||
private final CurrentAccountWritePlatformService currentAccountWritePlatformService;
|
||||
private final CurrentAccountReadPlatformService currentAccountReadPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -125,10 +129,11 @@ public class CurrentAccountsApiResource {
|
||||
@Operation(summary = "Submit new current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String submitApplication(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.submitApplication(command);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.createCurrentAccount() //
|
||||
.withJson(apiRequestBodyAsJson) //
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -157,11 +162,11 @@ public class CurrentAccountsApiResource {
|
||||
public String update(@PathParam("accountId") final Long accountId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(apiRequestBodyAsJson != null ? apiRequestBodyAsJson : "{}"));
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.modifyApplication(accountId, command);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.updateCurrentAccount(accountId) //
|
||||
.withJson(apiRequestBodyAsJson) //
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -180,30 +185,29 @@ public class CurrentAccountsApiResource {
|
||||
jsonApiRequest = "{}";
|
||||
}
|
||||
|
||||
final org.apache.fineract.infrastructure.core.api.JsonCommand command =
|
||||
org.apache.fineract.infrastructure.core.api.JsonCommand.fromJsonElement(null,
|
||||
JsonParser.parseString(jsonApiRequest));
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonApiRequest);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
CommandWrapper commandRequest = null;
|
||||
if (is(commandParam, "reject")) {
|
||||
result = currentAccountWritePlatformService.rejectApplication(accountId, command);
|
||||
commandRequest = builder.rejectCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "withdrawnByApplicant")) {
|
||||
result = currentAccountWritePlatformService.withdrawApplication(accountId, command);
|
||||
commandRequest = builder.withdrawCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "approve")) {
|
||||
result = currentAccountWritePlatformService.approveApplication(accountId, command);
|
||||
commandRequest = builder.approveCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "undoapproval")) {
|
||||
result = currentAccountWritePlatformService.undoApproval(accountId, command);
|
||||
commandRequest = builder.undoCurrentAccountApplication(accountId).build();
|
||||
} else if (is(commandParam, "activate")) {
|
||||
result = currentAccountWritePlatformService.activate(accountId, command);
|
||||
commandRequest = builder.currentAccountActivation(accountId).build();
|
||||
} else if (is(commandParam, "close")) {
|
||||
result = currentAccountWritePlatformService.closeAccount(accountId, command);
|
||||
commandRequest = builder.closeCurrentAccountApplication(accountId).build();
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
if (commandRequest == null) {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "close" });
|
||||
}
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@@ -214,7 +218,10 @@ public class CurrentAccountsApiResource {
|
||||
@Operation(summary = "Delete a current account application")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String delete(@PathParam("accountId") final Long accountId) {
|
||||
final CommandProcessingResult result = currentAccountWritePlatformService.deleteApplication(accountId);
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.deleteCurrentAccount(accountId) //
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
|
||||
+74
-3
@@ -53,10 +53,13 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -71,7 +74,9 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.currentproduct.service.CurrentProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
@@ -101,6 +106,9 @@ public class CurrentProductsApiResource {
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final CurrentProductWritePlatformService currentProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
|
||||
@POST
|
||||
@@ -122,9 +130,72 @@ public class CurrentProductsApiResource {
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String update(@PathParam("productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateCurrentProduct(productId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_current_product on
|
||||
// approval - see /currentproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.currentProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Current Products pending edit approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.savingProductReadPlatformService.retrieveAllPendingEdits();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Current Product")
|
||||
public String retrievePending(@PathParam("productId") final Long productId, @Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_PRODUCT_RESOURCE_NAME);
|
||||
final SavingsProductTempData pendingEdit = this.savingProductReadPlatformService.retrievePendingEdit(productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Current Product edit")
|
||||
public String approvePending(@PathParam("productId") final Long productId) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_CURRENTPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve current products");
|
||||
}
|
||||
final CommandProcessingResult result = this.currentProductWritePlatformService.approveEdit(productId);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Current Product edit")
|
||||
public String rejectPending(@PathParam("productId") final Long productId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_CURRENTPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject current products");
|
||||
}
|
||||
String rejectReason = null;
|
||||
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||
}
|
||||
}
|
||||
final CommandProcessingResult result = this.currentProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
|
||||
+77
-3
@@ -53,14 +53,17 @@ import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import java.util.Locale;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -78,8 +81,10 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.FixedDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.FixedDepositProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
@@ -116,6 +121,9 @@ public class FixedDepositProductsApiResource {
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
|
||||
private final FixedDepositProductWritePlatformService fixedDepositProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -151,15 +159,81 @@ public class FixedDepositProductsApiResource {
|
||||
public String update(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateFixedDepositProduct(productId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_savings_product on
|
||||
// approval - see /fixeddepositproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.fixedDepositProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Fixed Deposit Products pending edit approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.depositProductReadPlatformService
|
||||
.retrieveAllPendingEdits(DepositAccountType.FIXED_DEPOSIT);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Fixed Deposit Product")
|
||||
public String retrievePending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final SavingsProductTempData pendingEdit = this.depositProductReadPlatformService
|
||||
.retrievePendingEdit(DepositAccountType.FIXED_DEPOSIT, productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Fixed Deposit Product edit")
|
||||
public String approvePending(@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_FIXEDDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve fixed deposit products");
|
||||
}
|
||||
final CommandProcessingResult result = this.fixedDepositProductWritePlatformService.approveEdit(productId);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Fixed Deposit Product edit")
|
||||
public String rejectPending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_FIXEDDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject fixed deposit products");
|
||||
}
|
||||
String rejectReason = null;
|
||||
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||
}
|
||||
}
|
||||
final CommandProcessingResult result = this.fixedDepositProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+78
-3
@@ -55,10 +55,13 @@ import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -85,8 +88,10 @@ import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.RecurringDepositProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
@@ -126,6 +131,9 @@ public class RecurringDepositProductsApiResource {
|
||||
.registerTypeAdapter(LocalDate.class,
|
||||
(JsonSerializer<LocalDate>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.toString()))
|
||||
.create();
|
||||
private final RecurringDepositProductWritePlatformService recurringDepositProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@@ -158,15 +166,82 @@ public class RecurringDepositProductsApiResource {
|
||||
public String update(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRecurringDepositProduct(productId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_savings_product on
|
||||
// approval - see /recurringdepositproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.recurringDepositProductWritePlatformService.submitEditForApproval(productId,
|
||||
command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Recurring Deposit Products pending edit approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.depositProductReadPlatformService
|
||||
.retrieveAllPendingEdits(DepositAccountType.RECURRING_DEPOSIT);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Recurring Deposit Product")
|
||||
public String retrievePending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final SavingsProductTempData pendingEdit = this.depositProductReadPlatformService
|
||||
.retrievePendingEdit(DepositAccountType.RECURRING_DEPOSIT, productId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Recurring Deposit Product edit")
|
||||
public String approvePending(@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_RECURRINGDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve recurring deposit products");
|
||||
}
|
||||
final CommandProcessingResult result = this.recurringDepositProductWritePlatformService.approveEdit(productId);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Recurring Deposit Product edit")
|
||||
public String rejectPending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_RECURRINGDEPOSITPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject recurring deposit products");
|
||||
}
|
||||
String rejectReason = null;
|
||||
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||
}
|
||||
}
|
||||
final CommandProcessingResult result = this.recurringDepositProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+88
-3
@@ -59,8 +59,11 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
@@ -74,9 +77,11 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductWritePlatformService;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -105,6 +110,9 @@ public class SavingsProductsApiResource {
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final SavingsProductWritePlatformService savingsProductWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final DefaultToApiJsonSerializer<SavingsProductTempData> tempDataApiJsonSerializer;
|
||||
|
||||
|
||||
@POST
|
||||
@@ -135,15 +143,92 @@ public class SavingsProductsApiResource {
|
||||
public String update(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateSavingProduct(productId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
// Bypasses the native command-source/maker-checker pipeline by design: edits are held in
|
||||
// m_savings_product_temp (a pending row there is itself the lock) and only applied to m_savings_product on
|
||||
// approval - see /savingsproducts/{productId}/approve and /reject below.
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
final JsonCommand command = JsonCommand.fromJsonElement(productId, this.fromApiJsonHelper.parse(apiRequestBodyAsJson),
|
||||
this.fromApiJsonHelper);
|
||||
|
||||
final CommandProcessingResult result = this.savingsProductWritePlatformService.submitEditForApproval(productId, command);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List Savings Products pending edit approval", description = "Lists all savings product edits currently awaiting checker approval")
|
||||
public String retrieveAllPending(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final Collection<SavingsProductTempData> pendingEdits = this.savingProductReadPlatformService.retrieveAllPendingEdits();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdits);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{productId}/pending")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve the pending edit for a Savings Product", description = "Retrieves the draft awaiting checker approval for this product, if any")
|
||||
public String retrievePending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
|
||||
final SavingsProductTempData pendingEdit = this.savingProductReadPlatformService.retrievePendingEdit(productId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.tempDataApiJsonSerializer.serialize(settings, pendingEdit);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/approve")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve a pending Savings Product edit", description = "Replays the pending edit and applies it to the Savings Product. The maker of the edit cannot also approve it.")
|
||||
public String approvePending(@PathParam("productId") @Parameter(description = "productId") final Long productId) {
|
||||
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("APPROVE_SAVINGSPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to approve savings products");
|
||||
}
|
||||
|
||||
final CommandProcessingResult result = this.savingsProductWritePlatformService.approveEdit(productId);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{productId}/reject")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Reject a pending Savings Product edit", description = "Discards the pending edit without applying it. The maker of the edit cannot also reject it.")
|
||||
public String rejectPending(@PathParam("productId") @Parameter(description = "productId") final Long productId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
if (this.context.authenticatedUser().hasNotPermissionForAnyOf("REJECT_SAVINGSPRODUCT")) {
|
||||
throw new NoAuthorizationException("User has no authority to reject savings products");
|
||||
}
|
||||
|
||||
String rejectReason = null;
|
||||
if (apiRequestBodyAsJson != null && !apiRequestBodyAsJson.isBlank()) {
|
||||
final var parsed = this.fromApiJsonHelper.parse(apiRequestBodyAsJson);
|
||||
if (parsed != null && parsed.isJsonObject() && parsed.getAsJsonObject().has("rejectReason")) {
|
||||
rejectReason = parsed.getAsJsonObject().get("rejectReason").getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
final CommandProcessingResult result = this.savingsProductWritePlatformService.rejectEdit(productId, rejectReason);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
|
||||
+11
@@ -25,6 +25,7 @@ import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.PostLoad;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
@@ -135,7 +136,17 @@ public class DepositAccountRecurringDetail extends AbstractPersistableCustom<Lon
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
public void postLoad() {
|
||||
if (this.recurringDetail == null) {
|
||||
this.recurringDetail = new DepositRecurringDetail();
|
||||
}
|
||||
}
|
||||
|
||||
public DepositRecurringDetail recurringDetail() {
|
||||
if (this.recurringDetail == null) {
|
||||
this.recurringDetail = new DepositRecurringDetail();
|
||||
}
|
||||
return this.recurringDetail;
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -42,6 +42,8 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.specialC
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.weakerSectionParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominationFacilityParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.autoDebitFacilityParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.introducerRequiredParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shareLinkingEnabledParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.isSeniorCitizenParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.seniorCitizenInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
|
||||
@@ -395,6 +397,16 @@ public class SavingsAccountAssembler {
|
||||
if (command.parameterExists(autoDebitFacilityParamName)) {
|
||||
account.setAutoDebitFacility(this.fromApiJsonHelper.extractBooleanNamed(autoDebitFacilityParamName, element));
|
||||
}
|
||||
if (command.parameterExists(introducerRequiredParamName)) {
|
||||
account.setIntroducerRequired(this.fromApiJsonHelper.extractBooleanNamed(introducerRequiredParamName, element));
|
||||
} else if (account.savingsProduct() != null) {
|
||||
account.setIntroducerRequired(account.savingsProduct().isIntroducerRequired());
|
||||
}
|
||||
if (command.parameterExists(shareLinkingEnabledParamName)) {
|
||||
account.setShareLinkingEnabled(this.fromApiJsonHelper.extractBooleanNamed(shareLinkingEnabledParamName, element));
|
||||
} else if (account.savingsProduct() != null) {
|
||||
account.setShareLinkingEnabled(account.savingsProduct().isShareLinkingEnabled());
|
||||
}
|
||||
|
||||
// Senior citizen rate — runs after validateDomainRules so it is the final persisted value
|
||||
if (command.parameterExists(isSeniorCitizenParamName)) {
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "TERMINATE")
|
||||
public class CurrentAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountTerminateCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminateAccount(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "FIXEDDEPOSITACCOUNT", action = "TERMINATE")
|
||||
public class FixedDepositAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public FixedDepositAccountTerminateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "RECURRINGDEPOSITACCOUNT", action = "TERMINATE")
|
||||
public class RecurringDepositAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public RecurringDepositAccountTerminateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SAVINGSACCOUNT", action = "TERMINATE")
|
||||
public class SavingsAccountTerminateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountTerminateCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.terminate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "ACTIVATE")
|
||||
public class ActivateCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ActivateCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public ActivateCurrentAccountCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.activate(command.getSavingsId(), command);
|
||||
return this.writePlatformService.activate(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "CLOSE")
|
||||
public class CloseSavingsCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CloseSavingsCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public CloseSavingsCurrentAccountCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.close(command.getSavingsId(), command);
|
||||
return this.writePlatformService.closeAccount(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "APPROVE")
|
||||
public class CurrentAccountApplicationApprovalCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationApprovalCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationApprovalCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.approveApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.approveApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "APPROVALUNDO")
|
||||
public class CurrentAccountApplicationApprovalUndoCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationApprovalUndoCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationApprovalUndoCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.undoApplicationApproval(command.getSavingsId(), command);
|
||||
return this.writePlatformService.undoApproval(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "DELETE")
|
||||
public class CurrentAccountApplicationDeletionCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationDeletionCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationDeletionCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.deleteApplication(command.getSavingsId());
|
||||
return this.writePlatformService.deleteApplication(command.entityId());
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "UPDATE")
|
||||
public class CurrentAccountApplicationModificationCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationModificationCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationModificationCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.modifyApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.modifyApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "REJECT")
|
||||
public class CurrentAccountApplicationRejectedCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationRejectedCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationRejectedCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.rejectApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.rejectApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "CREATE")
|
||||
public class CurrentAccountApplicationSubmittalCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationSubmittalCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationSubmittalCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.submitApplication(command);
|
||||
return this.writePlatformService.submitApplication(command);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,16 +31,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAW")
|
||||
public class CurrentAccountApplicationWithdrawnCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsApplicationProcessWritePlatformService applicationProcessService;
|
||||
private final CurrentAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountApplicationWithdrawnCommandHandler(final SavingsApplicationProcessWritePlatformService applicationProcessService) {
|
||||
this.applicationProcessService = applicationProcessService;
|
||||
public CurrentAccountApplicationWithdrawnCommandHandler(final CurrentAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.applicationProcessService.applicantWithdrawsFromApplication(command.getSavingsId(), command);
|
||||
return this.writePlatformService.withdrawApplication(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-4
@@ -297,12 +297,21 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
hasWhere = true;
|
||||
}
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
final Integer statusEnum = resolveStatusEnum(status);
|
||||
if (statusEnum != null) {
|
||||
if ("terminated".equalsIgnoreCase(status)) {
|
||||
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||
sqlBuilder.append("ca.status_enum = ?");
|
||||
params.add(statusEnum);
|
||||
sqlBuilder.append("ca.terminate_true = true");
|
||||
hasWhere = true;
|
||||
} else {
|
||||
final Integer statusEnum = resolveStatusEnum(status);
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||
sqlBuilder.append("ca.status_enum = ?");
|
||||
params.add(statusEnum);
|
||||
hasWhere = true;
|
||||
if (statusEnum == 100) {
|
||||
sqlBuilder.append(" AND (ca.terminate_true = false OR ca.terminate_true IS NULL)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fromDate != null) {
|
||||
@@ -356,6 +365,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
case "withdrawn" -> 400;
|
||||
case "rejected" -> 500;
|
||||
case "closed" -> 600;
|
||||
case "terminated" -> 900;
|
||||
default -> {
|
||||
try {
|
||||
yield Integer.parseInt(status);
|
||||
@@ -389,6 +399,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
// status
|
||||
int statusEnum = toInt(row.get("status_enum"), 100);
|
||||
out.put("status", buildStatus(statusEnum));
|
||||
out.put("terminateTrue", toBoolean(row.get("terminate_true")));
|
||||
|
||||
// subStatus
|
||||
int subStatusEnum = toInt(row.get("sub_status_enum"), 0);
|
||||
@@ -443,6 +454,7 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
out.put("msme", row.get("msme"));
|
||||
out.put("autoDebitFacility", row.get("auto_debit_facility"));
|
||||
out.put("balanceSwapOption", row.get("balance_swap_option"));
|
||||
out.put("shareLinkingEnabled", row.get("is_linking_enable"));
|
||||
out.put("establishmentDate", row.get("establishment_date"));
|
||||
out.put("startOperationDate", row.get("start_operation_date"));
|
||||
out.put("nonIndividualClient", row.get("is_non_individual_client"));
|
||||
@@ -501,6 +513,13 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", true);
|
||||
}
|
||||
case 900 -> {
|
||||
s.put("code", "savingsAccountStatusType.terminated");
|
||||
s.put("value", "Terminated");
|
||||
s.put("submittedAndPendingApproval", false);
|
||||
s.put("approved", false); s.put("active", false);
|
||||
s.put("rejected", false); s.put("withdrawnByApplicant", false); s.put("closed", false);
|
||||
}
|
||||
default -> {
|
||||
s.put("code", "savingsAccountStatusType.invalid");
|
||||
s.put("value", "Invalid");
|
||||
@@ -658,6 +677,14 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
try { return new BigDecimal(val.toString().trim()); } catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
private boolean toBoolean(Object val) {
|
||||
if (val == null) return false;
|
||||
if (val instanceof Boolean) return (Boolean) val;
|
||||
if (val instanceof Number) return ((Number) val).intValue() == 1;
|
||||
String s = val.toString().trim();
|
||||
return "true".equalsIgnoreCase(s) || "1".equals(s);
|
||||
}
|
||||
|
||||
private static class FlatRowMapper implements RowMapper<Map<String, Object>> {
|
||||
@Override
|
||||
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
+5
-5
@@ -30,7 +30,7 @@ import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
|
||||
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
|
||||
@@ -80,7 +80,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
|
||||
private final AccountNumberGenerator accountNumberGenerator;
|
||||
private final CurrentAccountNumberGenerator currentAccountNumberGenerator;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
|
||||
public CurrentAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
|
||||
@@ -91,7 +91,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
final ChargeReadPlatformService chargeReadPlatformService,
|
||||
final EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
final ColumnValidator columnValidator,
|
||||
final AccountNumberGenerator accountNumberGenerator,
|
||||
final CurrentAccountNumberGenerator currentAccountNumberGenerator,
|
||||
final CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
@@ -102,7 +102,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
|
||||
this.accountNumberGenerator = accountNumberGenerator;
|
||||
this.currentAccountNumberGenerator = currentAccountNumberGenerator;
|
||||
this.codeValueReadPlatformService = codeValueReadPlatformService;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ public class CurrentAccountTemplateReadPlatformServiceImpl implements CurrentAcc
|
||||
productData.getOdAccountClosedZeroBalanceInDayClosing() != null && productData.getOdAccountClosedZeroBalanceInDayClosing());
|
||||
// Generate preview account number using current product's own GL account ID
|
||||
try {
|
||||
final String preview = accountNumberGenerator.previewAccountNumberByGlAccountId(productData.getGlAccountId(), officeId);
|
||||
final String preview = currentAccountNumberGenerator.preview(productId, officeId);
|
||||
template.setPreviewAccountNo(preview);
|
||||
} catch (final Exception ex) {
|
||||
log.warn("Failed to generate preview account number for productId={}: {}", productId, ex.getMessage());
|
||||
|
||||
+64
-11
@@ -45,6 +45,7 @@ import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountWriteP
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||
import org.apache.fineract.accounting.common.AccountingRuleType;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
@@ -127,6 +128,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
boolean balanceSwapOption = json.has("balanceSwapOption") && !json.get("balanceSwapOption").isJsonNull()
|
||||
? json.get("balanceSwapOption").getAsBoolean() : product.isBalanceSwapOption();
|
||||
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = CurrentAccount.createNew(
|
||||
client, productId, office, null, "TEMP", submittedOnDate, accountTypeEnum,
|
||||
product.currency().getCode(), product.currency().getDigitsAfterDecimal(),
|
||||
@@ -139,6 +141,11 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
product.getCustomerSegment(), product.isAutoPost(),
|
||||
product.getInterestCalculateSelector(), product.getAveragePeriod(),
|
||||
product.getAvgBalanceCalcPeriod());
|
||||
account.setSubmittedOnByUser(user);
|
||||
|
||||
boolean shareLinkingEnabled = json.has("shareLinkingEnabled") && !json.get("shareLinkingEnabled").isJsonNull()
|
||||
? json.get("shareLinkingEnabled").getAsBoolean() : product.isShareLinkingEnabled();
|
||||
account.setShareLinkingEnabled(shareLinkingEnabled);
|
||||
|
||||
// Save CBS account type (descriptive name: "Standard Account", "Basic Account", etc.)
|
||||
if (cbsAccountType != null) account.setCbsAccountType(cbsAccountType);
|
||||
@@ -244,6 +251,9 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
throw e;
|
||||
}
|
||||
|
||||
log.info("Current Account created successfully: [id={}, accountNo={}, clientId={}, officeId={}, submittedOnDate={}]",
|
||||
account.getId(), account.getAccountNo(), clientId, office.getId(), submittedOnDate);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(account.getId())
|
||||
.withClientId(clientId)
|
||||
@@ -254,12 +264,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approveApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate approvedOnDate = parseDate(getString(json, "approvedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (approvedOnDate == null) approvedOnDate = LocalDate.now();
|
||||
account.approve(approvedOnDate);
|
||||
account.approve(approvedOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -271,6 +281,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
account.setStatusEnum(100);
|
||||
account.setApprovedOnDate(null);
|
||||
account.setApprovedOnByUser(null);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -278,7 +289,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult activate(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate activatedOnDate = parseDate(getString(json, "activatedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
@@ -299,7 +310,7 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
openingBalanceValue, minRequired)));
|
||||
}
|
||||
|
||||
account.activate(activatedOnDate);
|
||||
account.activate(activatedOnDate, user);
|
||||
applyFacilityChargesOnActivation(account);
|
||||
|
||||
java.math.BigDecimal currentBalance = java.math.BigDecimal.ZERO;
|
||||
@@ -356,12 +367,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult rejectApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate rejectedOnDate = parseDate(getString(json, "rejectedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (rejectedOnDate == null) rejectedOnDate = LocalDate.now();
|
||||
account.reject(rejectedOnDate);
|
||||
account.reject(rejectedOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -369,12 +380,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult withdrawApplication(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate withdrawnOnDate = parseDate(getString(json, "withdrawnOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (withdrawnOnDate == null) withdrawnOnDate = LocalDate.now();
|
||||
account.withdraw(withdrawnOnDate);
|
||||
account.withdraw(withdrawnOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -382,12 +393,12 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult closeAccount(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final AppUser user = context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
LocalDate closedOnDate = parseDate(getString(json, "closedOnDate"), getString(json, "dateFormat"), getString(json, "locale"));
|
||||
if (closedOnDate == null) closedOnDate = LocalDate.now();
|
||||
account.close(closedOnDate);
|
||||
account.close(closedOnDate, user);
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
@@ -399,6 +410,19 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
final JsonObject json = command.parsedJson().getAsJsonObject();
|
||||
|
||||
if (account.getStatusEnum() != null) {
|
||||
if (account.getStatusEnum() == 500) {
|
||||
account.setStatusEnum(100);
|
||||
account.setRejectedOnDate(null);
|
||||
account.setRejectedOnByUser(null);
|
||||
} else if (account.getStatusEnum() == 400) {
|
||||
account.setStatusEnum(100);
|
||||
account.setWithdrawnOnDate(null);
|
||||
account.setWithdrawnOnByUser(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (json.has("externalId") && !json.get("externalId").isJsonNull())
|
||||
account.setExternalId(json.get("externalId").getAsString());
|
||||
if (json.has("accountStatus") && !json.get("accountStatus").isJsonNull())
|
||||
@@ -422,6 +446,8 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
account.setBalanceSwapOption(json.get("balanceSwapOption").getAsBoolean());
|
||||
if (json.has("autoDebitFacility") && !json.get("autoDebitFacility").isJsonNull())
|
||||
account.setAutoDebitFacility(json.get("autoDebitFacility").getAsBoolean());
|
||||
if (json.has("shareLinkingEnabled") && !json.get("shareLinkingEnabled").isJsonNull())
|
||||
account.setShareLinkingEnabled(json.get("shareLinkingEnabled").getAsBoolean());
|
||||
if (json.has("introducerClientId") && !json.get("introducerClientId").isJsonNull())
|
||||
account.setIntroducerClientId(json.get("introducerClientId").getAsLong());
|
||||
if (json.has("introducerName") && !json.get("introducerName").isJsonNull())
|
||||
@@ -484,6 +510,30 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
return new CommandProcessingResultBuilder().withEntityId(accountId).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult terminateAccount(final Long accountId, final JsonCommand command) {
|
||||
context.authenticatedUser();
|
||||
final CurrentAccount account = findAccount(accountId);
|
||||
account.terminate();
|
||||
currentAccountRepository.saveAndFlush(account);
|
||||
|
||||
final java.util.Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("terminateTrue", true);
|
||||
changes.put("status", 900);
|
||||
|
||||
final Long officeId = account.getOffice() != null ? account.getOffice().getId() : null;
|
||||
final Long clientId = account.getClient() != null ? account.getClient().getId() : null;
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withEntityId(accountId)
|
||||
.withOfficeId(officeId)
|
||||
.withClientId(clientId)
|
||||
.withSavingsId(accountId)
|
||||
.with(changes)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
private CurrentAccount findAccount(Long accountId) {
|
||||
@@ -492,7 +542,10 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
||||
}
|
||||
|
||||
private CurrentAccountFacilityConfig assembleFacilityConfig(CurrentAccount account, JsonObject fcJson) {
|
||||
final CurrentAccountFacilityConfig cfg = CurrentAccountFacilityConfig.createFor(account);
|
||||
CurrentAccountFacilityConfig cfg = account.getFacilityConfig();
|
||||
if (cfg == null) {
|
||||
cfg = CurrentAccountFacilityConfig.createFor(account);
|
||||
}
|
||||
cfg.setAtmFacility(getBool(fcJson, "atmFacility"));
|
||||
cfg.setAtmWithdrawalFacility(getBool(fcJson, "atmWithdrawalFacility"));
|
||||
cfg.setUpiFacility(getBool(fcJson, "upiFacility"));
|
||||
|
||||
+87
-2
@@ -18,10 +18,13 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -34,11 +37,20 @@ import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.DepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.FixedDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.exception.FixedDepositProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNoPendingEditException;
|
||||
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 {
|
||||
@@ -49,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<DepositProductData> retrieveAll(final DepositAccountType depositAccountType) {
|
||||
@@ -124,6 +140,69 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private static String productPendingType(final DepositAccountType depositAccountType) {
|
||||
return depositAccountType.isFixedDeposit() ? "FD" : "RD";
|
||||
}
|
||||
|
||||
private RowMapper<SavingsProductTempData> 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<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> 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.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<SavingsProductTempData> retrieveAllPendingEdits(final DepositAccountType depositAccountType) {
|
||||
final String sql = "select " + pendingEditSchema(productPendingType(depositAccountType))
|
||||
+ "and spt.status = 'PENDING' order by spt.created_date desc";
|
||||
return this.jdbcTemplate.query(sql, getPendingEditRowMapper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavingsProductTempData retrievePendingEdit(final DepositAccountType depositAccountType, final Long productId) {
|
||||
final String sql = "select " + pendingEditSchema(productPendingType(depositAccountType))
|
||||
+ "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||
try {
|
||||
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SavingsProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class DepositProductMapper implements RowMapper<DepositProductData> {
|
||||
|
||||
private final String schemaSql;
|
||||
@@ -255,7 +334,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("sp.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
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("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
|
||||
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 ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
@@ -345,6 +426,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
result.setDeceasedSettlement(rs.getBoolean("deceasedSettlement"));
|
||||
result.setAllowDebitTxnAfterDueDate(rs.getBoolean("allowDebitTxnAfterDueDate"));
|
||||
result.setWithdrawalFeeForTransfers(rs.getBoolean("withdrawalFeeForTransfers"));
|
||||
result.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -392,7 +474,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("sp.no_penalty_nominee_heir_closure as noPenaltyNomineeHeirClosure, ");
|
||||
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("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
|
||||
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 ");
|
||||
sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sp.id=dprd.savings_product_id ");
|
||||
@@ -513,6 +597,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
result.setDeceasedSettlement(deceasedSettlement);
|
||||
result.setAllowDebitTxnAfterDueDate(allowDebitTxnAfterDueDate);
|
||||
result.setWithdrawalFeeForTransfers(withdrawalFeeForTransfers);
|
||||
result.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+217
-1
@@ -32,6 +32,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
@@ -39,6 +42,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler;
|
||||
@@ -48,8 +53,22 @@ import org.apache.fineract.portfolio.savings.data.DepositProductDataValidator;
|
||||
import org.apache.fineract.portfolio.savings.domain.DepositProductAssembler;
|
||||
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;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -57,12 +76,19 @@ 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;
|
||||
private final DepositProductAssembler depositProductAssembler;
|
||||
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;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -94,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));
|
||||
@@ -150,6 +182,190 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final FixedDepositProduct product = this.fixedDepositProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new FixedDepositProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_FD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new SavingsProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
this.fromApiJsonDataValidator.validateForFixedDepositUpdate(command.json());
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_FD, productId, maker);
|
||||
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
final java.util.Set<String> 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") //
|
||||
.entityName("FIXEDDEPOSITPRODUCT") //
|
||||
.resourceGetUrl("/fixeddepositproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> 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<ProductPendingEditGlMapping> 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();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_FD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> 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);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> 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(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_FD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+224
-1
@@ -32,6 +32,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
@@ -39,6 +42,8 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.interestratechart.service.InterestRateChartAssembler;
|
||||
@@ -46,10 +51,24 @@ import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
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;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -57,12 +76,19 @@ 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;
|
||||
private final DepositProductAssembler depositProductAssembler;
|
||||
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;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -94,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));
|
||||
@@ -150,6 +182,197 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final RecurringDepositProduct product = this.recurringDepositProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new RecurringDepositProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_RD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new SavingsProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
this.fromApiJsonDataValidator.validateForRecurringDepositUpdate(command.json());
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_RD, productId, maker);
|
||||
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
final java.util.Set<String> fdSubmitted = ProductPendingEditFieldMapper.populateFixedDepositFields(command, pendingEdit.getFixedDepositFields());
|
||||
final java.util.Set<String> 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") //
|
||||
.entityName("RECURRINGDEPOSITPRODUCT") //
|
||||
.resourceGetUrl("/recurringdepositproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> 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<ProductPendingEditGlMapping> 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();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_RD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> 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);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> 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(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_RD, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+19
-5
@@ -172,10 +172,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
if (searchParameters != null) {
|
||||
|
||||
if (StringUtils.isNotBlank(searchParameters.getStatus())) {
|
||||
final Integer statusEnum = resolveStatusEnum(searchParameters.getStatus());
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(" and sa.status_enum = ?");
|
||||
objectArray[arrayPos++] = statusEnum;
|
||||
if ("terminated".equalsIgnoreCase(searchParameters.getStatus())) {
|
||||
sqlBuilder.append(" and sa.terminate_true = true");
|
||||
} else {
|
||||
final Integer statusEnum = resolveStatusEnum(searchParameters.getStatus());
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(" and sa.status_enum = ?");
|
||||
objectArray[arrayPos++] = statusEnum;
|
||||
if (statusEnum == 100) {
|
||||
sqlBuilder.append(" and (sa.terminate_true = false or sa.terminate_true is null)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +255,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
case "withdrawn" -> 400;
|
||||
case "rejected" -> 500;
|
||||
case "closed" -> 600;
|
||||
case "terminated" -> 900;
|
||||
default -> {
|
||||
try {
|
||||
yield Integer.parseInt(status);
|
||||
@@ -863,6 +871,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sp.msme as msme, ");
|
||||
sqlBuilder.append("COALESCE(sa.nomination_facility, sp.nomination_facility) as nominationFacility, ");
|
||||
sqlBuilder.append("COALESCE(sa.auto_debit_facility, sp.auto_debit_facility) as autoDebitFacility, ");
|
||||
sqlBuilder.append("COALESCE(sa.introducer_required, sp.introducer_required) as introducerRequired, ");
|
||||
sqlBuilder.append("COALESCE(sa.share_linking_enabled, sp.share_linking_enabled) as shareLinkingEnabled, ");
|
||||
// Facility config fields
|
||||
sqlBuilder.append("safc.atm_facility as atmFacility, ");
|
||||
sqlBuilder.append("safc.atm_withdrawal_facility as atmWithdrawalFacility, ");
|
||||
@@ -879,7 +889,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("safc.inter_bank_total_cap_facility as interBankTotalCapFacility, ");
|
||||
sqlBuilder.append("safc.withdrawal_facility as withdrawalFacility, ");
|
||||
sqlBuilder.append("safc.deposit_facility_kyc as depositFacilityKyc, ");
|
||||
sqlBuilder.append("safc.virtual_debit_card as virtualDebitCard ");
|
||||
sqlBuilder.append("safc.virtual_debit_card as virtualDebitCard, ");
|
||||
sqlBuilder.append("sa.terminate_true as terminateTrue ");
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||
@@ -1118,6 +1129,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
accountData.setMsme(rs.getObject("msme") != null ? rs.getBoolean("msme") : null);
|
||||
accountData.setNominationFacility(rs.getObject("nominationFacility") != null ? rs.getBoolean("nominationFacility") : null);
|
||||
accountData.setAutoDebitFacility(rs.getObject("autoDebitFacility") != null ? rs.getBoolean("autoDebitFacility") : null);
|
||||
accountData.setIntroducerRequired(rs.getObject("introducerRequired") != null ? rs.getBoolean("introducerRequired") : null);
|
||||
accountData.setShareLinkingEnabled(rs.getObject("shareLinkingEnabled") != null ? rs.getBoolean("shareLinkingEnabled") : null);
|
||||
accountData.setTerminateTrue(rs.getObject("terminateTrue") != null ? rs.getBoolean("terminateTrue") : null);
|
||||
|
||||
// Nominee as parsed map
|
||||
final String nomineeDataStr = rs.getString("nomineeData");
|
||||
|
||||
+13
@@ -1770,6 +1770,19 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.withClientId(account.clientId()).withGroupId(account.groupId()).withSavingsId(savingsId).with(changes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult terminate(final Long savingsId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, false);
|
||||
final Map<String, Object> changes = account.terminate();
|
||||
if (!changes.isEmpty()) {
|
||||
this.savingAccountRepositoryWrapper.save(account);
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withEntityId(savingsId).withOfficeId(account.officeId())
|
||||
.withClientId(account.clientId()).withGroupId(account.groupId()).withSavingsId(savingsId).with(changes).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult unblockAccount(final Long savingsId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
+74
-1
@@ -38,17 +38,26 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimit;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc;
|
||||
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 {
|
||||
@@ -61,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<List<Map<String, Object>>>() {}.getType();
|
||||
private static final Type OTHER_GL_MAPPINGS_MAP_TYPE = new TypeToken<Map<String, List<Map<String, Object>>>>() {}.getType();
|
||||
@@ -170,6 +183,63 @@ 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.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 = 'SAVINGS' ";
|
||||
|
||||
private RowMapper<SavingsProductTempData> 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<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON);
|
||||
data.setProposedChanges(proposedChanges);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductTempData> retrieveAllPendingEdits() {
|
||||
this.context.authenticatedUser();
|
||||
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) {
|
||||
this.context.authenticatedUser();
|
||||
final String sql = "select " + PENDING_EDIT_SCHEMA + "and spt.status = 'PENDING' and spt.product_id = ?";
|
||||
try {
|
||||
return this.jdbcTemplate.queryForObject(sql, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SavingsProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SavingProductMapper implements RowMapper<SavingsProductData> {
|
||||
|
||||
private final String schemaSql;
|
||||
@@ -217,7 +287,9 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
sqlBuilder.append("sp.auto_post as autoPost, sp.frequency_fix_day as frequencyFixDay, sp.last_interest_date as lastInterestDate, ");
|
||||
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("sp.account_closed_zero_balance_in_day_closing as accountClosedZeroBalanceInDayClosing, ");
|
||||
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 ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
@@ -340,6 +412,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
productData.setEnableLockinPeriod(rs.getBoolean("enableLockinPeriod"));
|
||||
productData.setBalanceFrequency(rs.getString("balanceFrequency"));
|
||||
productData.setAccountClosedZeroBalanceInDayClosing(rs.getBoolean("accountClosedZeroBalanceInDayClosing"));
|
||||
productData.setIsPendingApproval(rs.getBoolean("isPendingApproval"));
|
||||
|
||||
return productData;
|
||||
}
|
||||
|
||||
+247
@@ -46,6 +46,11 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsApiConstants;
|
||||
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductDataValidator;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKyc;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
@@ -56,8 +61,24 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitR
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
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.ProductPendingEditSubmittedField;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditFieldMapper;
|
||||
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.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductSelfApprovalNotAllowedException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -66,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;
|
||||
@@ -75,6 +97,13 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final SavingsProductFacilityConfigRepository facilityConfigRepository;
|
||||
private final SavingsProductFacilityLimitRepository facilityLimitRepository;
|
||||
private final SavingsProductDepositLimitKycRepository depositLimitKycRepository;
|
||||
private final ProductPendingEditRepository productPendingEditRepository;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CommandSourceRepository commandSourceRepository;
|
||||
private final ProductPendingEditChargeRepository pendingEditChargeRepository;
|
||||
private final ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository;
|
||||
private final ProductPendingEditGlMappingRepository pendingEditGlMappingRepository;
|
||||
private final ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository;
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue is.
|
||||
@@ -279,6 +308,224 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final SavingsProduct product = this.savingProductRepository.findById(productId)
|
||||
.orElseThrow(() -> new SavingsProductNotFoundException(productId));
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SAVINGS, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new SavingsProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
// above. proposedPayloadJson is still populated for now (safe deprecation window) but is no
|
||||
// longer what approveEdit replays from - see approveEdit().
|
||||
final java.util.Set<String> submitted = ProductPendingEditFieldMapper.populateCoreFields(command, pendingEdit.getCoreFields());
|
||||
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 (facilityConfigSubmitted) {
|
||||
submitted.add("accountFacilityConfig");
|
||||
}
|
||||
if (depositLimitKycSubmitted) {
|
||||
submitted.add("depositLimitKyc");
|
||||
}
|
||||
if (glMappingSubmitted) {
|
||||
submitted.add("glAccountMapping");
|
||||
}
|
||||
|
||||
// Audit-only entry so this submission is visible in System -> Audit Trails even though it bypasses the
|
||||
// native command-processing/maker-checker pipeline (the real gate is m_savings_product_temp itself).
|
||||
final CommandSource auditEntry = CommandSource.builder() //
|
||||
.actionName("UPDATE") //
|
||||
.entityName("SAVINGSPRODUCT") //
|
||||
.resourceGetUrl("/savingsproducts/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> 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<ProductPendingEditFacilityLimit> facilityLimits = ProductPendingEditFieldMapper.extractFacilityLimits(command,
|
||||
pendingEdit);
|
||||
if (facilityLimits != null) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, "accountFacilityLimits"));
|
||||
this.pendingEditFacilityLimitRepository.saveAll(facilityLimits);
|
||||
}
|
||||
|
||||
final List<ProductPendingEditGlMapping> 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();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SAVINGS, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> 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 JsonCommand replayedCommand = JsonCommand.fromJsonElement(productId, root, this.fromApiJsonHelper);
|
||||
final CommandProcessingResult result = update(productId, replayedCommand);
|
||||
|
||||
pendingEdit.markApproved(checker);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> 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(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SAVINGS, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new SavingsProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new SavingsProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long productId) {
|
||||
|
||||
+55
-12
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.account.domain.AccountAssociationsRepositor
|
||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository;
|
||||
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountNumberGenerator;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
@@ -95,6 +96,11 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository;
|
||||
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;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
@@ -308,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
|
||||
@@ -323,9 +335,17 @@ public class SavingsConfiguration {
|
||||
public FixedDepositProductWritePlatformService fixedDepositProductWritePlatformService(PlatformSecurityContext context,
|
||||
FixedDepositProductRepository fixedDepositProductRepository, DepositProductDataValidator fromApiJsonDataValidator,
|
||||
DepositProductAssembler depositProductAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler) {
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new FixedDepositProductWritePlatformServiceJpaRepositoryImpl(context, fixedDepositProductRepository,
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler);
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository, pendingEditChargeRepository,
|
||||
pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -347,9 +367,17 @@ public class SavingsConfiguration {
|
||||
public RecurringDepositProductWritePlatformService recurringDepositProductWritePlatformService(PlatformSecurityContext context,
|
||||
RecurringDepositProductRepository recurringDepositProductRepository, DepositProductDataValidator fromApiJsonDataValidator,
|
||||
DepositProductAssembler depositProductAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler) {
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService, InterestRateChartAssembler chartAssembler,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new RecurringDepositProductWritePlatformServiceJpaRepositoryImpl(context, recurringDepositProductRepository,
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler);
|
||||
fromApiJsonDataValidator, depositProductAssembler, accountMappingWritePlatformService, chartAssembler,
|
||||
productPendingEditRepository, pendingEditSubmittedFieldRepository, pendingEditChargeRepository,
|
||||
pendingEditGlMappingRepository, fromApiJsonHelper, commandSourceRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -404,11 +432,11 @@ public class SavingsConfiguration {
|
||||
GroupReadPlatformService groupReadPlatformService, CurrentProductReadPlatformService currentProductReadPlatformService,
|
||||
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
|
||||
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
|
||||
ColumnValidator columnValidator, AccountNumberGenerator accountNumberGenerator,
|
||||
ColumnValidator columnValidator, CurrentAccountNumberGenerator currentAccountNumberGenerator,
|
||||
CodeValueReadPlatformService codeValueReadPlatformService) {
|
||||
return new CurrentAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
|
||||
currentProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
|
||||
entityDatatableChecksReadService, columnValidator, accountNumberGenerator, codeValueReadPlatformService);
|
||||
entityDatatableChecksReadService, columnValidator, currentAccountNumberGenerator, codeValueReadPlatformService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -472,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
|
||||
@@ -488,10 +522,19 @@ public class SavingsConfiguration {
|
||||
FineractEntityAccessUtil fineractEntityAccessUtil,
|
||||
SavingsProductFacilityConfigRepository facilityConfigRepository,
|
||||
SavingsProductFacilityLimitRepository facilityLimitRepository,
|
||||
SavingsProductDepositLimitKycRepository depositLimitKycRepository) {
|
||||
SavingsProductDepositLimitKycRepository depositLimitKycRepository,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
FromJsonHelper fromApiJsonHelper,
|
||||
CommandSourceRepository commandSourceRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditFacilityLimitRepository pendingEditFacilityLimitRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
org.apache.fineract.portfolio.savings.domain.ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository) {
|
||||
return new SavingsProductWritePlatformServiceJpaRepositoryImpl(context, savingProductRepository, fromApiJsonDataValidator,
|
||||
savingsProductAssembler, accountMappingWritePlatformService, fineractEntityAccessUtil, facilityConfigRepository,
|
||||
facilityLimitRepository, depositLimitKycRepository);
|
||||
facilityLimitRepository, depositLimitKycRepository, productPendingEditRepository, fromApiJsonHelper,
|
||||
commandSourceRepository, pendingEditChargeRepository, pendingEditFacilityLimitRepository, pendingEditGlMappingRepository,
|
||||
pendingEditSubmittedFieldRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.shareproducts.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ShareProductEditPendingApprovalException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ShareProductEditPendingApprovalException(final Long productId) {
|
||||
super("error.msg.shareproduct.edit.pending.approval",
|
||||
"Share product with identifier " + productId + " already has an edit pending approval", productId);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.shareproducts.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ShareProductNoPendingEditException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ShareProductNoPendingEditException(final Long productId) {
|
||||
super("error.msg.shareproduct.no.pending.edit", "Share product with identifier " + productId + " has no edit pending approval",
|
||||
productId);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.shareproducts.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class ShareProductSelfApprovalNotAllowedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public ShareProductSelfApprovalNotAllowedException(final Long productId) {
|
||||
super("error.msg.shareproduct.self.approval.not.allowed",
|
||||
"The maker of the pending edit for share product " + productId + " cannot also approve or reject it", productId);
|
||||
}
|
||||
}
|
||||
+71
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.shareproducts.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Date;
|
||||
import java.sql.ResultSet;
|
||||
@@ -46,12 +48,20 @@ import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.products.data.ProductData;
|
||||
import org.apache.fineract.portfolio.products.exception.ProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.products.service.ShareProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductTempData;
|
||||
import org.apache.fineract.portfolio.shareaccounts.service.SharesEnumerations;
|
||||
import org.apache.fineract.portfolio.shareproducts.data.ShareProductData;
|
||||
import org.apache.fineract.portfolio.shareproducts.data.ShareProductMarketPriceData;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.ShareProductNoPendingEditException;
|
||||
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 {
|
||||
@@ -62,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;
|
||||
|
||||
@@ -152,6 +166,63 @@ public class ShareProductReadPlatformServiceImpl implements ShareProductReadPlat
|
||||
return null;
|
||||
}
|
||||
|
||||
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.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.createdby_id "
|
||||
+ "left join m_appuser checker on checker.id = spt.lastmodifiedby_id "
|
||||
+ "where spt.product_type = 'SHARE' ";
|
||||
|
||||
private RowMapper<SavingsProductTempData> 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<ProductPendingEdit> opt = this.productPendingEditRepository.findById(tempId);
|
||||
if (opt.isPresent()) {
|
||||
final ProductPendingEdit pendingEdit = opt.get();
|
||||
final Set<String> submittedFields = new java.util.HashSet<>(
|
||||
this.pendingEditSubmittedFieldRepository.findFieldNamesByPendingEditId(pendingEdit.getId()));
|
||||
final Map<String, Object> proposedChanges = ProductPendingEditFieldMapper.reconstructProposedChangesMap(
|
||||
pendingEdit, submittedFields, this.pendingEditChargeRepository, this.pendingEditGlMappingRepository, null, GSON);
|
||||
data.setProposedChanges(proposedChanges);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SavingsProductTempData> retrieveAllPendingEdits() {
|
||||
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, getPendingEditRowMapper(), new Object[] { productId }); // NOSONAR
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ShareProductNoPendingEditException(productId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AllShareProductRowMapper implements RowMapper<ProductData> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
+17
@@ -32,4 +32,21 @@ public interface ShareProductWritePlatformService {
|
||||
CommandProcessingResult approveShareProductDividend(Long PayOutDetailId);
|
||||
|
||||
CommandProcessingResult deleteShareProductDividend(Long PayOutDetailId);
|
||||
|
||||
/**
|
||||
* Submits an edit for approval instead of applying it directly. Fails if a pending edit already exists for this
|
||||
* product (that pending row is itself the lock).
|
||||
*/
|
||||
CommandProcessingResult submitEditForApproval(Long productId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Replays the stored pending edit through the normal update path and marks it approved. Fails if the approving
|
||||
* user is the same user who submitted the edit.
|
||||
*/
|
||||
CommandProcessingResult approveEdit(Long productId);
|
||||
|
||||
/**
|
||||
* Discards the pending edit. Fails if the rejecting user is the same user who submitted the edit.
|
||||
*/
|
||||
CommandProcessingResult rejectEdit(Long productId, String rejectReason);
|
||||
}
|
||||
|
||||
+201
@@ -22,33 +22,56 @@ 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;
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.share.ShareProductDividentsCreateBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEdit;
|
||||
import org.apache.fineract.portfolio.savings.domain.ProductPendingEditRepository;
|
||||
import org.apache.fineract.portfolio.shareproducts.constants.ShareProductApiConstants;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProduct;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividendPayOutDetails;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.DividentProcessingException;
|
||||
import org.apache.fineract.portfolio.shareproducts.exception.ShareProductEditPendingApprovalException;
|
||||
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;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@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;
|
||||
@@ -56,6 +79,12 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP
|
||||
private final ShareProductDividendAssembler shareProductDividendAssembler;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
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
|
||||
public CommandProcessingResult createShareProduct(JsonCommand jsonCommand) {
|
||||
@@ -180,6 +209,178 @@ public class ShareProductWritePlatformServiceJpaRepositoryImpl implements ShareP
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult submitEditForApproval(final Long productId, final JsonCommand command) {
|
||||
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final ShareProduct product = this.repository.findOneWithNotFoundDetection(productId);
|
||||
|
||||
if (this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SHARE, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.isPresent()) {
|
||||
throw new ShareProductEditPendingApprovalException(productId);
|
||||
}
|
||||
|
||||
final ProductPendingEdit pendingEdit = new ProductPendingEdit(ProductPendingEdit.TYPE_SHARE, productId, maker);
|
||||
|
||||
final java.util.Set<String> 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") //
|
||||
.entityName("SHAREPRODUCT") //
|
||||
.resourceGetUrl("/products/share/" + productId) //
|
||||
.resourceId(productId) //
|
||||
.commandAsJson(command.json()) //
|
||||
.maker(maker) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
for (final String fieldName : submitted) {
|
||||
this.pendingEditSubmittedFieldRepository.save(new ProductPendingEditSubmittedField(pendingEdit, fieldName));
|
||||
}
|
||||
|
||||
final List<Long> 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<ProductPendingEditGlMapping> 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();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveEdit(final Long productId) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SHARE, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new ShareProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new ShareProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
final Set<String> 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);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsChecked(checker);
|
||||
auditEntry.updateForAudit(result);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandSource findOrCreateAuditEntry(final ProductPendingEdit pendingEdit, final Long productId) {
|
||||
if (pendingEdit.getCommandSourceId() != null) {
|
||||
final java.util.Optional<CommandSource> existing = this.commandSourceRepository.findById(pendingEdit.getCommandSourceId());
|
||||
if (existing.isPresent()) {
|
||||
return existing.get();
|
||||
}
|
||||
}
|
||||
final Set<String> 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(commandJson) //
|
||||
.maker(pendingEdit.getMaker()) //
|
||||
.madeOnDate(DateUtils.getAuditOffsetDateTime()) //
|
||||
.status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) //
|
||||
.idempotencyKey(java.util.UUID.randomUUID().toString()) //
|
||||
.sanitized(false) //
|
||||
.build();
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
pendingEdit.setCommandSourceId(auditEntry.getId());
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
return auditEntry;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectEdit(final Long productId, final String rejectReason) {
|
||||
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
final ProductPendingEdit pendingEdit = this.productPendingEditRepository
|
||||
.findByProductTypeAndProductIdAndStatus(ProductPendingEdit.TYPE_SHARE, productId, ProductPendingEdit.STATUS_PENDING)
|
||||
.orElseThrow(() -> new ShareProductNoPendingEditException(productId));
|
||||
|
||||
if (pendingEdit.getMaker().getId().equals(checker.getId()) && !checker.isCheckerSuperUser()) {
|
||||
throw new ShareProductSelfApprovalNotAllowedException(productId);
|
||||
}
|
||||
|
||||
pendingEdit.markRejected(checker, rejectReason);
|
||||
this.productPendingEditRepository.saveAndFlush(pendingEdit);
|
||||
|
||||
final CommandSource auditEntry = findOrCreateAuditEntry(pendingEdit, productId);
|
||||
auditEntry.markAsRejected(checker);
|
||||
this.commandSourceRepository.saveAndFlush(auditEntry);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(productId) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private void handleDataIntegrityIssues(final Exception e) {
|
||||
log.error("Unknown data integrity issue with resource", e);
|
||||
throw ErrorHandler.getMappable(e, "error.msg.shareproduct.unknown.data.integrity.issue",
|
||||
|
||||
+22
-3
@@ -24,11 +24,17 @@ import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToG
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
|
||||
import org.apache.fineract.organisation.monetary.service.CurrencyReadPlatformService;
|
||||
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;
|
||||
@@ -86,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);
|
||||
}
|
||||
|
||||
@@ -99,10 +111,17 @@ public class ShareProductsConfiguration {
|
||||
ShareProductDividentPayOutDetailsRepositoryWrapper shareProductDividentPayOutDetailsRepository,
|
||||
ShareProductDividendAssembler shareProductDividendAssembler,
|
||||
ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
BusinessEventNotifierService businessEventNotifierService) {
|
||||
BusinessEventNotifierService businessEventNotifierService,
|
||||
PlatformSecurityContext context,
|
||||
ProductPendingEditRepository productPendingEditRepository,
|
||||
ProductPendingEditSubmittedFieldRepository pendingEditSubmittedFieldRepository,
|
||||
ProductPendingEditChargeRepository pendingEditChargeRepository,
|
||||
ProductPendingEditGlMappingRepository pendingEditGlMappingRepository,
|
||||
CommandSourceRepository commandSourceRepository) {
|
||||
return new ShareProductWritePlatformServiceJpaRepositoryImpl(repository, serializer, fromApiJsonHelper,
|
||||
shareProductDividentPayOutDetailsRepository, shareProductDividendAssembler, accountMappingWritePlatformService,
|
||||
businessEventNotifierService);
|
||||
businessEventNotifierService, context, productPendingEditRepository, pendingEditSubmittedFieldRepository,
|
||||
pendingEditChargeRepository, pendingEditGlMappingRepository, commandSourceRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ fineract.security.oauth2.client.registrations.frontend-client.require-authorizat
|
||||
fineract.tenant.host=${FINERACT_DEFAULT_TENANTDB_HOSTNAME:localhost}
|
||||
fineract.tenant.port=${FINERACT_DEFAULT_TENANTDB_PORT:5432}
|
||||
fineract.tenant.username=${FINERACT_DEFAULT_TENANTDB_UID:postgres}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:root}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:Keshav@898966}
|
||||
fineract.tenant.parameters=${FINERACT_DEFAULT_TENANTDB_CONN_PARAMS:}
|
||||
fineract.tenant.timezone=${FINERACT_DEFAULT_TENANTDB_TIMEZONE:Asia/Kolkata}
|
||||
fineract.tenant.identifier=${FINERACT_DEFAULT_TENANTDB_IDENTIFIER:default}
|
||||
@@ -407,7 +407,7 @@ server.tomcat.mbeanregistry.enabled=${FINERACT_SERVER_TOMCAT_MBEANREGISTRY_ENABL
|
||||
spring.datasource.hikari.driverClassName=${FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME:org.postgresql.Driver}
|
||||
spring.datasource.hikari.jdbcUrl=${FINERACT_HIKARI_JDBC_URL:jdbc:postgresql://localhost:5432/fineract_tenants}
|
||||
spring.datasource.hikari.username=${FINERACT_HIKARI_USERNAME:postgres}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:Keshav@898966}
|
||||
spring.datasource.hikari.minimumIdle=${FINERACT_HIKARI_MINIMUM_IDLE:3}
|
||||
spring.datasource.hikari.maximumPoolSize=${FINERACT_HIKARI_MAXIMUM_POOL_SIZE:10}
|
||||
spring.datasource.hikari.idleTimeout=${FINERACT_HIKARI_IDLE_TIMEOUT:60000}
|
||||
|
||||
@@ -394,4 +394,7 @@
|
||||
<include file="parts/0364_catchup_current_product_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0366_catchup_clerk_withdrawal_token.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0367_catchup_missing_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0338_add_soft_delete_to_tellers.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0339_add_share_linking_enabled_to_current_account.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0340_add_terminate_true_to_savings_and_current_account.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0338_add_soft_delete_to_tellers" author="fineract">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<and>
|
||||
<tableExists tableName="m_tellers"/>
|
||||
<not><columnExists tableName="m_tellers" columnName="is_deleted"/></not>
|
||||
</and>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_tellers">
|
||||
<column name="is_deleted" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0339_add_share_linking_enabled_to_current_account" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<tableExists tableName="m_current_account"/>
|
||||
<not><columnExists tableName="m_current_account" columnName="is_linking_enable"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_current_account">
|
||||
<column name="is_linking_enable" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
|
||||
<changeSet id="0340_1" author="fineract">
|
||||
<addColumn tableName="m_savings_account">
|
||||
<column name="terminate_true" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0340_2" author="fineract">
|
||||
<addColumn tableName="m_current_account">
|
||||
<column name="terminate_true" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user