feat(chequebook): add cheque book module with Maker-Checker workflow
Adds cheque series, stock lot/transfer, cheque book issuance, stop-payment/lost/cancel-leaf actions, and pending-request approval APIs with Maker-Checker command handlers and liquibase changelogs.
This commit is contained in:
+152
@@ -71,6 +71,158 @@ public class CommandWrapperBuilder {
|
||||
this.sanitizeJsonKeys);
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createChequeSeries() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CHEQUESERIES";
|
||||
this.entityId = null;
|
||||
this.href = "/chequeseries";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateChequeSeries(final Long seriesId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "CHEQUESERIES";
|
||||
this.entityId = seriesId;
|
||||
this.href = "/chequeseries/" + seriesId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteChequeSeries(final Long seriesId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "CHEQUESERIES";
|
||||
this.entityId = seriesId;
|
||||
this.href = "/chequeseries/" + seriesId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createChequeStockLot() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CHEQUESTOCKLOT";
|
||||
this.entityId = null;
|
||||
this.href = "/chequestocklots";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder reportLostChequeStockLot(final Long lotId) {
|
||||
this.actionName = "REPORTLOST";
|
||||
this.entityName = "CHEQUESTOCKLOT";
|
||||
this.entityId = lotId;
|
||||
this.href = "/chequestocklots/" + lotId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createChequeStockTransfer() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CHEQUESTOCKTRANSFER";
|
||||
this.entityId = null;
|
||||
this.href = "/chequestocktransfers";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder receiveChequeStockTransfer(final Long transferId) {
|
||||
this.actionName = "RECEIVE";
|
||||
this.entityName = "CHEQUESTOCKTRANSFER";
|
||||
this.entityId = transferId;
|
||||
this.href = "/chequestocktransfers/" + transferId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder rejectChequeStockTransfer(final Long transferId) {
|
||||
this.actionName = "REJECT";
|
||||
this.entityName = "CHEQUESTOCKTRANSFER";
|
||||
this.entityId = transferId;
|
||||
this.href = "/chequestocktransfers/" + transferId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createChequeBook() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = null;
|
||||
this.href = "/chequebooks";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder issueChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "ISSUE";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder rejectChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "REJECT";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder withdrawChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "WITHDRAW";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder stopPaymentChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "STOPPAYMENT";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder revokeStopPaymentChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "REVOKESTOPPAYMENT";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder reportLostChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "REPORTLOST";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder cancelLeafChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "CANCELLEAF";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder markUsedChequeBook(final Long chequeBookId) {
|
||||
this.actionName = "MARKUSED";
|
||||
this.entityName = "CHEQUEBOOK";
|
||||
this.entityId = chequeBookId;
|
||||
this.href = "/chequebooks/" + chequeBookId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder approveChequeBookPendingRequest(final Long pendingRequestId) {
|
||||
this.actionName = "APPROVE";
|
||||
this.entityName = "CHEQUEBOOKPENDINGREQUEST";
|
||||
this.entityId = pendingRequestId;
|
||||
this.href = "/chequebook/pending-requests/" + pendingRequestId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder rejectChequeBookPendingRequest(final Long pendingRequestId) {
|
||||
this.actionName = "REJECT";
|
||||
this.entityName = "CHEQUEBOOKPENDINGREQUEST";
|
||||
this.entityId = pendingRequestId;
|
||||
this.href = "/chequebook/pending-requests/" + pendingRequestId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateCreditBureau() {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "ORGANISATIONCREDITBUREAU";
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.currentaccount.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Wrapper for {@link CurrentAccountRepository} that is responsible for checking if {@link CurrentAccount} is
|
||||
* returned when using <code>findOne</code> repository method and throwing an appropriate not found exception.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This is to avoid need for checking and throwing in multiple areas of code base where
|
||||
* {@link CurrentAccountRepository} is required.
|
||||
* </p>
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountRepositoryWrapper {
|
||||
|
||||
private final CurrentAccountRepository repository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public CurrentAccount findOneWithNotFoundDetection(final Long id) {
|
||||
return this.repository.findById(id).orElseThrow(() -> new CurrentAccountNotFoundException(id));
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
public final class ChequeBookApiConstants {
|
||||
|
||||
private ChequeBookApiConstants() {}
|
||||
|
||||
public static final String ENTITY_SERIES = "CHEQUESERIES";
|
||||
public static final String ENTITY_STOCK_LOT = "CHEQUESTOCKLOT";
|
||||
public static final String ENTITY_STOCK_TRANSFER = "CHEQUESTOCKTRANSFER";
|
||||
public static final String ENTITY_BOOK = "CHEQUEBOOK";
|
||||
public static final String ENTITY_PENDING_REQUEST = "CHEQUEBOOKPENDINGREQUEST";
|
||||
|
||||
public static final String RESOURCE_SERIES = "chequeseries";
|
||||
public static final String RESOURCE_STOCK_LOT = "chequestocklot";
|
||||
public static final String RESOURCE_STOCK_TRANSFER = "chequestocktransfer";
|
||||
public static final String RESOURCE_BOOK = "chequebook";
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum ChequeBookApiJsonInputParams {
|
||||
|
||||
// series create/update
|
||||
SERIES_CODE("seriesCode"), //
|
||||
NAME("name"), //
|
||||
LEAVES_PER_BOOK("leavesPerBook"), //
|
||||
APPLICABLE_ACCOUNT_TYPE("applicableAccountType"), //
|
||||
CTS_COMPLIANT("ctsCompliant"), //
|
||||
REORDER_LEVEL("reorderLevel"), //
|
||||
// stock lot receive
|
||||
SERIES_ID("seriesId"), //
|
||||
OFFICE_ID("officeId"), //
|
||||
RANGE_START("rangeStart"), //
|
||||
RANGE_END("rangeEnd"), //
|
||||
BATCH_REF("batchRef"), //
|
||||
// stock lot report-lost
|
||||
FROM_SERIAL_NO("fromSerialNo"), //
|
||||
REASON("reason"), //
|
||||
// stock transfer dispatch
|
||||
FROM_OFFICE_ID("fromOfficeId"), //
|
||||
TO_OFFICE_ID("toOfficeId"), //
|
||||
FROM_LOT_ID("fromLotId"), //
|
||||
NO_OF_LEAVES("noOfLeaves"), //
|
||||
// book request
|
||||
ACCOUNT_TYPE("accountType"), //
|
||||
ACCOUNT_ID("accountId"), //
|
||||
NO_OF_BOOKS("noOfBooks"), //
|
||||
// book issue
|
||||
COLLECTION_ACKNOWLEDGED("collectionAcknowledged"), //
|
||||
// book reject
|
||||
REJECTION_REASON("rejectionReason"), //
|
||||
// book leaf actions (stop-payment / revoke / report-lost / cancel-leaf)
|
||||
FROM_LEAF_SEQ("fromLeafSeq"), //
|
||||
TO_LEAF_SEQ("toLeafSeq"), //
|
||||
// common — the web client always sends these on command payloads
|
||||
LOCALE("locale"), //
|
||||
DATE_FORMAT("dateFormat"); //
|
||||
|
||||
private final String value;
|
||||
|
||||
ChequeBookApiJsonInputParams(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static Set<String> seriesCreateParams() {
|
||||
return toSet(SERIES_CODE, NAME, LEAVES_PER_BOOK, APPLICABLE_ACCOUNT_TYPE, CTS_COMPLIANT, REORDER_LEVEL, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> seriesUpdateParams() {
|
||||
return toSet(NAME, LEAVES_PER_BOOK, APPLICABLE_ACCOUNT_TYPE, CTS_COMPLIANT, REORDER_LEVEL, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> stockLotReceiveParams() {
|
||||
return toSet(SERIES_ID, OFFICE_ID, RANGE_START, RANGE_END, BATCH_REF, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> stockLotReportLostParams() {
|
||||
return toSet(FROM_SERIAL_NO, REASON, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> stockTransferDispatchParams() {
|
||||
return toSet(SERIES_ID, FROM_OFFICE_ID, TO_OFFICE_ID, FROM_LOT_ID, NO_OF_LEAVES, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> bookRequestParams() {
|
||||
return toSet(ACCOUNT_TYPE, ACCOUNT_ID, OFFICE_ID, SERIES_ID, NO_OF_BOOKS, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> bookIssueParams() {
|
||||
return toSet(COLLECTION_ACKNOWLEDGED, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> bookRejectParams() {
|
||||
return toSet(REJECTION_REASON, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
public static Set<String> bookLeafActionParams() {
|
||||
return toSet(FROM_LEAF_SEQ, TO_LEAF_SEQ, REASON, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
|
||||
private static Set<String> toSet(final ChequeBookApiJsonInputParams... params) {
|
||||
return Arrays.stream(params).map(ChequeBookApiJsonInputParams::getValue).collect(Collectors.toCollection(HashSet::new));
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
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.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeBookData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeLeafData;
|
||||
import org.apache.fineract.portfolio.chequebook.service.ChequeBookReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/chequebooks")
|
||||
@Component
|
||||
@Tag(name = "Cheque Books", description = "Requisition, issuance, stop-payment, hotlisting and cancellation of cheque books issued "
|
||||
+ "against a Savings or Current account.")
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeBookApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List cheque books")
|
||||
public Page<ChequeBookData> retrieveAll(@QueryParam("accountType") final String accountType,
|
||||
@QueryParam("accountId") final Long accountId, @QueryParam("officeId") final Long officeId,
|
||||
@QueryParam("status") final String status, @QueryParam("offset") @DefaultValue("0") final Integer offset,
|
||||
@QueryParam("limit") @DefaultValue("20") final Integer limit) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_BOOK);
|
||||
final int safeLimit = Math.min(limit, 200);
|
||||
return this.readPlatformService.retrieveAllBooks(accountType, accountId, officeId, status, offset, safeLimit);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a cheque book")
|
||||
public ChequeBookData retrieveOne(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_BOOK);
|
||||
return this.readPlatformService.retrieveBook(id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/leaves")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List the individual leaves of a cheque book")
|
||||
public List<ChequeLeafData> retrieveLeaves(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_BOOK);
|
||||
return this.readPlatformService.retrieveLeaves(id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Request a cheque book against an account")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createChequeBook().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{id}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Run a command on a cheque book", description = "Supported commands: issue, reject, withdraw, "
|
||||
+ "stoppayment, revokestoppayment, reportlost, cancelleaf, markused.")
|
||||
public CommandProcessingResult handleCommand(@PathParam("id") @Parameter(description = "id") final Long id,
|
||||
@QueryParam("command") @Parameter(description = "issue | reject | withdraw | stoppayment | revokestoppayment | "
|
||||
+ "reportlost | cancelleaf | markused") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
final CommandWrapper request;
|
||||
if ("issue".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.issueChequeBook(id).build();
|
||||
} else if ("reject".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.rejectChequeBook(id).build();
|
||||
} else if ("withdraw".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.withdrawChequeBook(id).build();
|
||||
} else if ("stoppayment".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.stopPaymentChequeBook(id).build();
|
||||
} else if ("revokestoppayment".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.revokeStopPaymentChequeBook(id).build();
|
||||
} else if ("reportlost".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.reportLostChequeBook(id).build();
|
||||
} else if ("cancelleaf".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.cancelLeafChequeBook(id).build();
|
||||
} else if ("markused".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.markUsedChequeBook(id).build();
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
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.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeBookPendingRequestData;
|
||||
import org.apache.fineract.portfolio.chequebook.service.ChequeBookReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Bespoke maker-checker queue for the three cheque-book creation actions (series master, stock receipt,
|
||||
* book requisition) — submitted via the normal {@code /chequeseries}, {@code /chequestocklots},
|
||||
* {@code /chequebooks} POST endpoints, which now hold the request here as PENDING instead of creating the
|
||||
* record immediately. A DIFFERENT user than the maker must approve or reject it from here.
|
||||
*/
|
||||
@Path("/v1/chequebook/pending-requests")
|
||||
@Component
|
||||
@Tag(name = "Cheque Book Pending Requests", description = "Maker-checker queue for cheque series, stock receipt, and book requisition "
|
||||
+ "submissions — approving replays the original request and creates the record; the maker can never approve their own request.")
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeBookPendingRequestApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List cheque book pending requests")
|
||||
public Page<ChequeBookPendingRequestData> retrieveAll(@QueryParam("status") final String status,
|
||||
@QueryParam("fromDate") final String fromDate, @QueryParam("toDate") final String toDate,
|
||||
@QueryParam("offset") @DefaultValue("0") final Integer offset, @QueryParam("limit") @DefaultValue("20") final Integer limit) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_PENDING_REQUEST);
|
||||
final int safeLimit = Math.min(limit, 200);
|
||||
return this.readPlatformService.retrievePendingRequests(status, fromDate, toDate, offset, safeLimit);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a cheque book pending request")
|
||||
public ChequeBookPendingRequestData retrieveOne(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_PENDING_REQUEST);
|
||||
return this.readPlatformService.retrievePendingRequest(id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{id}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Approve or reject a cheque book pending request", description = "Supported commands: approve, reject. "
|
||||
+ "The user submitting the original request can never approve/reject it themselves.")
|
||||
public CommandProcessingResult handleCommand(@PathParam("id") @Parameter(description = "id") final Long id,
|
||||
@QueryParam("command") @Parameter(description = "approve | reject") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
final CommandWrapper request;
|
||||
if ("approve".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.approveChequeBookPendingRequest(id).build();
|
||||
} else if ("reject".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.rejectChequeBookPendingRequest(id).build();
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeLeafData;
|
||||
import org.apache.fineract.portfolio.chequebook.service.ChequeBookReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/chequeleaves")
|
||||
@Component
|
||||
@Tag(name = "Cheque Leaves", description = "The teller/counter quick-lookup: given a bare cheque-leaf serial number, is it "
|
||||
+ "active, stopped, lost, or was it ever issued at all — one code path covers every case. A serial number is only "
|
||||
+ "unique within its own series, so more than one series can match.")
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeLeafApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookReadPlatformService readPlatformService;
|
||||
|
||||
@GET
|
||||
@Path("lookup")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Look up a cheque leaf by its serial number", description = "Returns every leaf across every series "
|
||||
+ "that carries this serial number — normally one, but more than one series can legitimately share a number.")
|
||||
public List<ChequeLeafData> lookup(@QueryParam("serialNo") @Parameter(description = "serialNo") final Long serialNo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_BOOK);
|
||||
return this.readPlatformService.retrieveLeafBySerialNo(serialNo);
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
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.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeSeriesData;
|
||||
import org.apache.fineract.portfolio.chequebook.service.ChequeBookReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/chequeseries")
|
||||
@Component
|
||||
@Tag(name = "Cheque Book Series", description = "Cheque-leaf stationery master — leaves-per-book, CTS-2010 compliance, low-stock reorder threshold.")
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeSeriesApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List cheque series")
|
||||
public Page<ChequeSeriesData> retrieveAll(@QueryParam("status") final String status,
|
||||
@QueryParam("offset") @DefaultValue("0") final Integer offset, @QueryParam("limit") @DefaultValue("20") final Integer limit) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_SERIES);
|
||||
final int safeLimit = Math.min(limit, 500);
|
||||
return this.readPlatformService.retrieveAllSeries(status, offset, safeLimit);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a cheque series")
|
||||
public ChequeSeriesData retrieveOne(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_SERIES);
|
||||
return this.readPlatformService.retrieveSeries(id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create a cheque series")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createChequeSeries().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Update a cheque series")
|
||||
public CommandProcessingResult update(@PathParam("id") @Parameter(description = "id") final Long id,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().updateChequeSeries(id).withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Deactivate a cheque series")
|
||||
public CommandProcessingResult delete(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().deleteChequeSeries(id).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
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.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeStockLotData;
|
||||
import org.apache.fineract.portfolio.chequebook.service.ChequeBookReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/chequestocklots")
|
||||
@Component
|
||||
@Tag(name = "Cheque Stock Lots", description = "Unified stock ledger — a contiguous leaf-serial range currently sitting at one office, "
|
||||
+ "with a drawdown cursor shared by both issuance-to-customer and transfer-to-office.")
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeStockLotApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List cheque stock lots")
|
||||
public Page<ChequeStockLotData> retrieveAll(@QueryParam("officeId") final Long officeId,
|
||||
@QueryParam("seriesId") final Long seriesId, @QueryParam("status") final String status,
|
||||
@QueryParam("offset") @DefaultValue("0") final Integer offset, @QueryParam("limit") @DefaultValue("20") final Integer limit) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_STOCK_LOT);
|
||||
final int safeLimit = Math.min(limit, 500);
|
||||
return this.readPlatformService.retrieveStockLots(officeId, seriesId, status, offset, safeLimit);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a cheque stock lot")
|
||||
public ChequeStockLotData retrieveOne(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_STOCK_LOT);
|
||||
return this.readPlatformService.retrieveStockLot(id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Receive a bulk cheque stock lot (from press/vendor)")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createChequeStockLot().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{id}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Run a command on a cheque stock lot", description = "Supported commands: reportlost.")
|
||||
public CommandProcessingResult handleCommand(@PathParam("id") @Parameter(description = "id") final Long id,
|
||||
@QueryParam("command") @Parameter(description = "reportlost") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
final CommandWrapper request;
|
||||
if ("reportlost".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.reportLostChequeStockLot(id).build();
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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.chequebook.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
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.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeStockTransferData;
|
||||
import org.apache.fineract.portfolio.chequebook.service.ChequeBookReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/chequestocktransfers")
|
||||
@Component
|
||||
@Tag(name = "Cheque Stock Transfers", description = "Office-to-office cheque stock movement: dispatch draws a sub-range down from the "
|
||||
+ "source lot, the destination office then acknowledges receipt (or rejects).")
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeStockTransferApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List cheque stock transfers")
|
||||
public Page<ChequeStockTransferData> retrieveAll(@QueryParam("status") final String status,
|
||||
@QueryParam("offset") @DefaultValue("0") final Integer offset, @QueryParam("limit") @DefaultValue("20") final Integer limit) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_STOCK_TRANSFER);
|
||||
final int safeLimit = Math.min(limit, 500);
|
||||
return this.readPlatformService.retrieveStockTransfers(status, offset, safeLimit);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a cheque stock transfer")
|
||||
public ChequeStockTransferData retrieveOne(@PathParam("id") @Parameter(description = "id") final Long id) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(ChequeBookApiConstants.ENTITY_STOCK_TRANSFER);
|
||||
return this.readPlatformService.retrieveStockTransfer(id);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Dispatch a cheque stock transfer to another office")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createChequeStockTransfer().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{id}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Run a command on a cheque stock transfer", description = "Supported commands: receive, reject.")
|
||||
public CommandProcessingResult handleCommand(@PathParam("id") @Parameter(description = "id") final Long id,
|
||||
@QueryParam("command") @Parameter(description = "receive | reject") final String commandParam,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
final CommandWrapper request;
|
||||
if ("receive".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.receiveChequeStockTransfer(id).build();
|
||||
} else if ("reject".equalsIgnoreCase(commandParam)) {
|
||||
request = builder.rejectChequeStockTransfer(id).build();
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.chequebook.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ChequeBookData {
|
||||
|
||||
private final Long id;
|
||||
private final String accountType;
|
||||
private final Long accountId;
|
||||
private final String accountNo;
|
||||
private final String clientName;
|
||||
private final Long officeId;
|
||||
private final String officeName;
|
||||
private final Long seriesId;
|
||||
private final String seriesCode;
|
||||
private final LocalDate requestedDate;
|
||||
private final Long requestedBy;
|
||||
private final Integer noOfBooks;
|
||||
private final Integer noOfLeavesRequested;
|
||||
private final String status;
|
||||
private final LocalDate issueDate;
|
||||
private final Long issuedBy;
|
||||
private final Long lotId;
|
||||
private final Long rangeStart;
|
||||
private final Long rangeEnd;
|
||||
private final Integer totalLeaves;
|
||||
private final String rejectionReason;
|
||||
private final boolean collectionAcknowledged;
|
||||
private final LocalDate collectedDate;
|
||||
private final Long collectedBy;
|
||||
}
|
||||
+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.chequebook.data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ChequeBookPendingRequestData {
|
||||
|
||||
private final Long id;
|
||||
private final String requestType;
|
||||
private final String payloadJson;
|
||||
private final String status;
|
||||
private final Long makerId;
|
||||
private final String makerName;
|
||||
private final String makerUsername;
|
||||
private final OffsetDateTime submittedOn;
|
||||
private final Long checkerId;
|
||||
private final String checkerName;
|
||||
private final String checkerUsername;
|
||||
private final OffsetDateTime checkedOn;
|
||||
private final String rejectionReason;
|
||||
private final Long createdResourceId;
|
||||
private final String activityLog;
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.chequebook.data;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* A single leaf. {@code chequeBookId}/{@code accountNo}/{@code clientName} are null when the row was
|
||||
* generated directly against a stock lot (stock reported lost/stolen before ever being issued) — the
|
||||
* quick-lookup-by-serial-number screen relies on this being the single source of truth for every case.
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ChequeLeafData {
|
||||
|
||||
private final Long id;
|
||||
private final Long chequeBookId;
|
||||
private final Long lotId;
|
||||
private final String seriesCode;
|
||||
private final Long leafSerialNo;
|
||||
private final Integer leafSeqNo;
|
||||
private final String status;
|
||||
private final String stopReason;
|
||||
private final Long stoppedBy;
|
||||
private final OffsetDateTime stoppedOn;
|
||||
private final String lostReason;
|
||||
private final Long lostReportedBy;
|
||||
private final OffsetDateTime lostReportedOn;
|
||||
private final String cancelledReason;
|
||||
private final Long cancelledBy;
|
||||
private final OffsetDateTime cancelledOn;
|
||||
private final String usedReason;
|
||||
private final Long usedBy;
|
||||
private final OffsetDateTime usedOn;
|
||||
private final String accountType;
|
||||
private final Long accountId;
|
||||
private final String accountNo;
|
||||
private final String clientName;
|
||||
private final String officeName;
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ChequeSeriesData {
|
||||
|
||||
private final Long id;
|
||||
private final String seriesCode;
|
||||
private final String name;
|
||||
private final Integer leavesPerBook;
|
||||
private final String applicableAccountType;
|
||||
private final boolean ctsCompliant;
|
||||
private final Integer reorderLevel;
|
||||
private final boolean active;
|
||||
}
|
||||
+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.chequebook.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ChequeStockLotData {
|
||||
|
||||
private final Long id;
|
||||
private final Long seriesId;
|
||||
private final String seriesCode;
|
||||
private final Integer leavesPerBook;
|
||||
private final Long officeId;
|
||||
private final String officeName;
|
||||
private final Long rangeStart;
|
||||
private final Long rangeEnd;
|
||||
private final Long nextSerialNo;
|
||||
private final Long remainingLeaves;
|
||||
private final Long remainingBooks;
|
||||
private final String source;
|
||||
private final Long sourceRefId;
|
||||
private final String batchRef;
|
||||
private final String status;
|
||||
private final boolean lowStock;
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.chequebook.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ChequeStockTransferData {
|
||||
|
||||
private final Long id;
|
||||
private final Long seriesId;
|
||||
private final String seriesCode;
|
||||
private final Long fromOfficeId;
|
||||
private final String fromOfficeName;
|
||||
private final Long toOfficeId;
|
||||
private final String toOfficeName;
|
||||
private final Long fromLotId;
|
||||
private final Long toLotId;
|
||||
private final Long rangeStart;
|
||||
private final Long rangeEnd;
|
||||
private final Integer totalLeaves;
|
||||
private final LocalDate dispatchDate;
|
||||
private final String status;
|
||||
private final LocalDate receivedDate;
|
||||
private final Long dispatchedBy;
|
||||
private final Long receivedBy;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/** Which account model a cheque book is issued against — the two are not interchangeable in this codebase. */
|
||||
public enum ChequeAccountType {
|
||||
|
||||
SAVINGS, //
|
||||
CURRENT; //
|
||||
|
||||
public static ChequeAccountType fromString(final String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
for (final ChequeAccountType type : values()) {
|
||||
if (type.name().equalsIgnoreCase(value.trim())) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Version;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
|
||||
/**
|
||||
* A cheque book requested and (once stock is drawn) issued against an account. {@code accountId} is a
|
||||
* plain column (no JPA relation) since the account may be either a {@code SavingsAccount} or a
|
||||
* {@code CurrentAccount} — two account models in this codebase that are not interchangeable — mirroring
|
||||
* the same "plain column to avoid cross-module entity dependency" precedent used by
|
||||
* {@code CurrentAccount.productId}.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_cheque_book")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class ChequeBook extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@Column(name = "account_type", nullable = false, length = 10)
|
||||
private String accountType;
|
||||
|
||||
@Column(name = "account_id", nullable = false)
|
||||
private Long accountId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "office_id", nullable = false)
|
||||
private Office office;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "series_id")
|
||||
private ChequeSeries series;
|
||||
|
||||
@Column(name = "requested_date", nullable = false)
|
||||
private LocalDate requestedDate;
|
||||
|
||||
@Column(name = "requested_by")
|
||||
private Long requestedBy;
|
||||
|
||||
/** How many whole books the customer requested; leaves handed over = noOfBooks x series.leavesPerBook. */
|
||||
@Column(name = "no_of_books", nullable = false)
|
||||
private Integer noOfBooks;
|
||||
|
||||
/** Legacy free-form leaf count; superseded by {@link #noOfBooks}. Kept nullable for historical rows. */
|
||||
@Column(name = "no_of_leaves_requested")
|
||||
private Integer noOfLeavesRequested;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "issue_date")
|
||||
private LocalDate issueDate;
|
||||
|
||||
@Column(name = "issued_by")
|
||||
private Long issuedBy;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "lot_id")
|
||||
private ChequeStockLot lot;
|
||||
|
||||
@Column(name = "range_start")
|
||||
private Long rangeStart;
|
||||
|
||||
@Column(name = "range_end")
|
||||
private Long rangeEnd;
|
||||
|
||||
@Column(name = "total_leaves")
|
||||
private Integer totalLeaves;
|
||||
|
||||
@Column(name = "rejection_reason", length = 500)
|
||||
private String rejectionReason;
|
||||
|
||||
@Column(name = "collection_acknowledged", nullable = false)
|
||||
private boolean collectionAcknowledged;
|
||||
|
||||
@Column(name = "collected_date")
|
||||
private LocalDate collectedDate;
|
||||
|
||||
@Column(name = "collected_by")
|
||||
private Long collectedBy;
|
||||
|
||||
@Column(name = "created_on")
|
||||
private OffsetDateTime createdOn;
|
||||
|
||||
@Version
|
||||
@Column(name = "version")
|
||||
private Integer version;
|
||||
|
||||
/**
|
||||
* The customer/branch picks the denomination (series) at requisition time — matching real cheque-book
|
||||
* requisition slips, which specify the book type up front rather than leaving it to the issuing teller
|
||||
* to guess. {@code series} must already be applicable to {@code accountType} (checked by the caller).
|
||||
*/
|
||||
public static ChequeBook requestNew(final ChequeAccountType accountType, final Long accountId, final Office office,
|
||||
final ChequeSeries series, final Integer noOfBooks, final Long requestedBy) {
|
||||
final ChequeBook book = new ChequeBook();
|
||||
book.accountType = accountType.name();
|
||||
book.accountId = accountId;
|
||||
book.office = office;
|
||||
book.series = series;
|
||||
book.noOfBooks = noOfBooks;
|
||||
book.requestedDate = DateUtils.getBusinessLocalDate();
|
||||
book.requestedBy = requestedBy;
|
||||
book.status = ChequeBookStatus.REQUESTED.name();
|
||||
book.collectionAcknowledged = false;
|
||||
book.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return book;
|
||||
}
|
||||
|
||||
private void assertRequested() {
|
||||
if (!ChequeBookStatus.REQUESTED.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.not.requested",
|
||||
"Cheque book " + this.getId() + " is not in REQUESTED status.", this.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/** The series was already fixed at request time; issuing only draws stock and materializes the leaves. */
|
||||
public void issue(final ChequeStockLot lot, final Long rangeStart, final Long rangeEnd, final Integer totalLeaves,
|
||||
final Long issuedBy) {
|
||||
assertRequested();
|
||||
this.lot = lot;
|
||||
this.rangeStart = rangeStart;
|
||||
this.rangeEnd = rangeEnd;
|
||||
this.totalLeaves = totalLeaves;
|
||||
this.issueDate = DateUtils.getBusinessLocalDate();
|
||||
this.issuedBy = issuedBy;
|
||||
this.status = ChequeBookStatus.ISSUED.name();
|
||||
}
|
||||
|
||||
public void reject(final String reason) {
|
||||
assertRequested();
|
||||
this.status = ChequeBookStatus.REJECTED.name();
|
||||
this.rejectionReason = reason;
|
||||
}
|
||||
|
||||
public void withdraw() {
|
||||
if (!ChequeBookStatus.ISSUED.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.not.issued",
|
||||
"Cheque book " + this.getId() + " is not in ISSUED status.", this.getId());
|
||||
}
|
||||
this.status = ChequeBookStatus.WITHDRAWN.name();
|
||||
}
|
||||
|
||||
public void acknowledgeCollection(final Long collectedBy) {
|
||||
this.collectionAcknowledged = true;
|
||||
this.collectedBy = collectedBy;
|
||||
this.collectedDate = DateUtils.getBusinessLocalDate();
|
||||
}
|
||||
|
||||
public boolean isIssued() {
|
||||
return ChequeBookStatus.ISSUED.name().equals(this.status);
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
|
||||
/**
|
||||
* Bespoke maker-checker for cheque-book creation actions (series master, stock receipt, book requisition) —
|
||||
* modelled on {@code m_clerk_disbursement_request} rather than Fineract's native command-approval engine,
|
||||
* because that engine's hold is bypassed by any role carrying ALL_FUNCTIONS (Super user, Clerk in this
|
||||
* tenant), which made it a no-op for this CBS's actual admin accounts. A request holds the raw submission
|
||||
* JSON; approval replays it through the same creation logic used elsewhere in this module. The maker can
|
||||
* never approve/reject their own request — enforced in the write service, not just by role.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_chequebook_pending_request")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class ChequeBookPendingRequest extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@Column(name = "request_type", nullable = false, length = 30)
|
||||
private String requestType;
|
||||
|
||||
@Column(name = "payload_json", nullable = false)
|
||||
private String payloadJson;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "maker_id", nullable = false)
|
||||
private Long makerId;
|
||||
|
||||
@Column(name = "maker_name", length = 150)
|
||||
private String makerName;
|
||||
|
||||
@Column(name = "maker_username", length = 100)
|
||||
private String makerUsername;
|
||||
|
||||
@Column(name = "submitted_on", nullable = false)
|
||||
private OffsetDateTime submittedOn;
|
||||
|
||||
@Column(name = "checker_id")
|
||||
private Long checkerId;
|
||||
|
||||
@Column(name = "checker_name", length = 150)
|
||||
private String checkerName;
|
||||
|
||||
@Column(name = "checker_username", length = 100)
|
||||
private String checkerUsername;
|
||||
|
||||
@Column(name = "checked_on")
|
||||
private OffsetDateTime checkedOn;
|
||||
|
||||
@Column(name = "rejection_reason", length = 500)
|
||||
private String rejectionReason;
|
||||
|
||||
@Column(name = "created_resource_id")
|
||||
private Long createdResourceId;
|
||||
|
||||
@Column(name = "activity_log")
|
||||
private String activityLog;
|
||||
|
||||
public static ChequeBookPendingRequest submit(final ChequeBookPendingRequestType requestType, final String payloadJson,
|
||||
final AppUser maker, final String activityLog) {
|
||||
final ChequeBookPendingRequest request = new ChequeBookPendingRequest();
|
||||
request.requestType = requestType.name();
|
||||
request.payloadJson = payloadJson;
|
||||
request.status = ChequeBookPendingRequestStatus.PENDING.name();
|
||||
request.makerId = maker.getId();
|
||||
request.makerName = maker.getDisplayName();
|
||||
request.makerUsername = maker.getUsername();
|
||||
request.submittedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
request.activityLog = activityLog;
|
||||
return request;
|
||||
}
|
||||
|
||||
public boolean isPending() {
|
||||
return ChequeBookPendingRequestStatus.PENDING.name().equals(this.status);
|
||||
}
|
||||
|
||||
/** The maker can never be their own checker — a distinct human must approve/reject a request. */
|
||||
public boolean isMadeBy(final Long userId) {
|
||||
return this.makerId != null && this.makerId.equals(userId);
|
||||
}
|
||||
|
||||
public void approve(final AppUser checker, final Long createdResourceId, final String activityLog) {
|
||||
this.status = ChequeBookPendingRequestStatus.APPROVED.name();
|
||||
this.checkerId = checker.getId();
|
||||
this.checkerName = checker.getDisplayName();
|
||||
this.checkerUsername = checker.getUsername();
|
||||
this.checkedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
this.createdResourceId = createdResourceId;
|
||||
this.activityLog = activityLog;
|
||||
}
|
||||
|
||||
public void reject(final AppUser checker, final String reason, final String activityLog) {
|
||||
this.status = ChequeBookPendingRequestStatus.REJECTED.name();
|
||||
this.checkerId = checker.getId();
|
||||
this.checkerName = checker.getDisplayName();
|
||||
this.checkerUsername = checker.getUsername();
|
||||
this.checkedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
this.rejectionReason = reason;
|
||||
this.activityLog = activityLog;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ChequeBookPendingRequestRepository extends JpaRepository<ChequeBookPendingRequest, Long> {
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeBookPendingRequestNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeBookPendingRequestRepositoryWrapper {
|
||||
|
||||
private final ChequeBookPendingRequestRepository repository;
|
||||
|
||||
public ChequeBookPendingRequest findOneWithNotFoundDetection(final Long id) {
|
||||
return this.repository.findById(id).orElseThrow(() -> new ChequeBookPendingRequestNotFoundException(id));
|
||||
}
|
||||
|
||||
public ChequeBookPendingRequest saveAndFlush(final ChequeBookPendingRequest request) {
|
||||
return this.repository.saveAndFlush(request);
|
||||
}
|
||||
}
|
||||
+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.chequebook.domain;
|
||||
|
||||
/** Lifecycle of a {@link ChequeBookPendingRequest}. Terminal once APPROVED or REJECTED. */
|
||||
public enum ChequeBookPendingRequestStatus {
|
||||
|
||||
PENDING, //
|
||||
APPROVED, //
|
||||
REJECTED; //
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
/** Which cheque-book action a {@link ChequeBookPendingRequest} holds. */
|
||||
public enum ChequeBookPendingRequestType {
|
||||
|
||||
CREATE_SERIES, //
|
||||
RECEIVE_STOCK, //
|
||||
REQUEST_BOOK, //
|
||||
DELETE_SERIES; //
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ChequeBookRepository extends JpaRepository<ChequeBook, Long> {
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeBookNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeBookRepositoryWrapper {
|
||||
|
||||
private final ChequeBookRepository repository;
|
||||
|
||||
public ChequeBook findOneWithNotFoundDetection(final Long id) {
|
||||
return this.repository.findById(id).orElseThrow(() -> new ChequeBookNotFoundException(id));
|
||||
}
|
||||
|
||||
public ChequeBook saveAndFlush(final ChequeBook book) {
|
||||
return this.repository.saveAndFlush(book);
|
||||
}
|
||||
}
|
||||
+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.chequebook.domain;
|
||||
|
||||
public enum ChequeBookStatus {
|
||||
|
||||
REQUESTED, //
|
||||
ISSUED, //
|
||||
REJECTED, //
|
||||
WITHDRAWN; //
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
|
||||
/**
|
||||
* One physical cheque leaf. {@code chequeBook} is nullable: a leaf reported lost/stolen while still
|
||||
* unissued stock (see {@link ChequeStockLot#reportRemainingLost(long)}) has no book yet, but still needs a
|
||||
* row so the serial-number quick-lookup screen has exactly one code path for every case — issued-active,
|
||||
* issued-stopped, issued-lost, and unissued-but-stolen.
|
||||
*
|
||||
* <p>
|
||||
* Legal transitions: (created)->ACTIVE (issued) or (created, no book)->LOST (unissued theft report);
|
||||
* ACTIVE<->STOP_PAYMENT (the one round-trip); ACTIVE->CANCELLED / LOST / WITHDRAWN / USED (all terminal).
|
||||
* A recovered "lost" cheque conventionally stays hotlisted rather than silently reactivating, since the
|
||||
* customer has typically already been issued a replacement book. USED is manually recorded by a branch,
|
||||
* never automatic — see {@link #markUsed(String, Long)}.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_cheque_leaf")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class ChequeLeaf extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "cheque_book_id")
|
||||
private ChequeBook chequeBook;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "lot_id", nullable = false)
|
||||
private ChequeStockLot lot;
|
||||
|
||||
@Column(name = "leaf_serial_no", nullable = false)
|
||||
private Long leafSerialNo;
|
||||
|
||||
@Column(name = "leaf_seq_no")
|
||||
private Integer leafSeqNo;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "stop_reason", length = 500)
|
||||
private String stopReason;
|
||||
|
||||
@Column(name = "stopped_by")
|
||||
private Long stoppedBy;
|
||||
|
||||
@Column(name = "stopped_on")
|
||||
private OffsetDateTime stoppedOn;
|
||||
|
||||
@Column(name = "lost_reason", length = 500)
|
||||
private String lostReason;
|
||||
|
||||
@Column(name = "lost_reported_by")
|
||||
private Long lostReportedBy;
|
||||
|
||||
@Column(name = "lost_reported_on")
|
||||
private OffsetDateTime lostReportedOn;
|
||||
|
||||
@Column(name = "cancelled_reason", length = 500)
|
||||
private String cancelledReason;
|
||||
|
||||
@Column(name = "cancelled_by")
|
||||
private Long cancelledBy;
|
||||
|
||||
@Column(name = "cancelled_on")
|
||||
private OffsetDateTime cancelledOn;
|
||||
|
||||
/** Manually recorded once the branch sees this leaf clear on the bank/clearing statement — never automatic. */
|
||||
@Column(name = "used_reason", length = 500)
|
||||
private String usedReason;
|
||||
|
||||
@Column(name = "used_by")
|
||||
private Long usedBy;
|
||||
|
||||
@Column(name = "used_on")
|
||||
private OffsetDateTime usedOn;
|
||||
|
||||
@Column(name = "created_on")
|
||||
private OffsetDateTime createdOn;
|
||||
|
||||
public static ChequeLeaf issueForBook(final ChequeBook chequeBook, final ChequeStockLot lot, final Long leafSerialNo,
|
||||
final Integer leafSeqNo) {
|
||||
final ChequeLeaf leaf = new ChequeLeaf();
|
||||
leaf.chequeBook = chequeBook;
|
||||
leaf.lot = lot;
|
||||
leaf.leafSerialNo = leafSerialNo;
|
||||
leaf.leafSeqNo = leafSeqNo;
|
||||
leaf.status = ChequeLeafStatus.ACTIVE.name();
|
||||
leaf.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return leaf;
|
||||
}
|
||||
|
||||
public static ChequeLeaf lostUnissued(final ChequeStockLot lot, final Long leafSerialNo, final String reason,
|
||||
final Long reportedBy) {
|
||||
final ChequeLeaf leaf = new ChequeLeaf();
|
||||
leaf.lot = lot;
|
||||
leaf.leafSerialNo = leafSerialNo;
|
||||
leaf.status = ChequeLeafStatus.LOST.name();
|
||||
leaf.lostReason = reason;
|
||||
leaf.lostReportedBy = reportedBy;
|
||||
leaf.lostReportedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
leaf.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return leaf;
|
||||
}
|
||||
|
||||
private void assertActive() {
|
||||
if (!ChequeLeafStatus.ACTIVE.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequeleaf.not.active",
|
||||
"Cheque leaf " + this.leafSerialNo + " is not ACTIVE.", this.leafSerialNo);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPayment(final String reason, final Long stoppedBy) {
|
||||
assertActive();
|
||||
this.status = ChequeLeafStatus.STOP_PAYMENT.name();
|
||||
this.stopReason = reason;
|
||||
this.stoppedBy = stoppedBy;
|
||||
this.stoppedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
}
|
||||
|
||||
public void revokeStopPayment() {
|
||||
if (!ChequeLeafStatus.STOP_PAYMENT.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequeleaf.not.stopped",
|
||||
"Cheque leaf " + this.leafSerialNo + " is not under stop-payment.", this.leafSerialNo);
|
||||
}
|
||||
this.status = ChequeLeafStatus.ACTIVE.name();
|
||||
this.stopReason = null;
|
||||
this.stoppedBy = null;
|
||||
this.stoppedOn = null;
|
||||
}
|
||||
|
||||
public void reportLost(final String reason, final Long reportedBy) {
|
||||
assertActive();
|
||||
this.status = ChequeLeafStatus.LOST.name();
|
||||
this.lostReason = reason;
|
||||
this.lostReportedBy = reportedBy;
|
||||
this.lostReportedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
}
|
||||
|
||||
public void cancel(final String reason, final Long cancelledBy) {
|
||||
assertActive();
|
||||
this.status = ChequeLeafStatus.CANCELLED.name();
|
||||
this.cancelledReason = reason;
|
||||
this.cancelledBy = cancelledBy;
|
||||
this.cancelledOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
}
|
||||
|
||||
public void withdraw() {
|
||||
assertActive();
|
||||
this.status = ChequeLeafStatus.WITHDRAWN.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually recorded once a branch sees this leaf clear on the bank/clearing statement. Never set
|
||||
* automatically — this CBS has no clearing/presentment integration to detect it on its own.
|
||||
*/
|
||||
public void markUsed(final String reason, final Long markedBy) {
|
||||
assertActive();
|
||||
this.status = ChequeLeafStatus.USED.name();
|
||||
this.usedReason = reason;
|
||||
this.usedBy = markedBy;
|
||||
this.usedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ChequeLeafRepository extends JpaRepository<ChequeLeaf, Long> {
|
||||
|
||||
Optional<ChequeLeaf> findByLeafSerialNo(Long leafSerialNo);
|
||||
|
||||
List<ChequeLeaf> findByChequeBookIdOrderByLeafSeqNoAsc(Long chequeBookId);
|
||||
|
||||
List<ChequeLeaf> findByChequeBookIdAndLeafSeqNoBetweenOrderByLeafSeqNoAsc(Long chequeBookId, Integer fromSeqNo,
|
||||
Integer toSeqNo);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeLeafNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeLeafRepositoryWrapper {
|
||||
|
||||
private final ChequeLeafRepository repository;
|
||||
|
||||
public ChequeLeaf findByLeafSerialNoWithNotFoundDetection(final Long leafSerialNo) {
|
||||
return this.repository.findByLeafSerialNo(leafSerialNo).orElseThrow(() -> new ChequeLeafNotFoundException(leafSerialNo));
|
||||
}
|
||||
|
||||
public ChequeLeaf saveAndFlush(final ChequeLeaf leaf) {
|
||||
return this.repository.saveAndFlush(leaf);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
/**
|
||||
* Legal transitions: (created)->ACTIVE (issued) or (created, no book)->LOST (unissued stock reported
|
||||
* stolen); ACTIVE<->STOP_PAYMENT (the one round-trip, via stop-payment/revoke); ACTIVE->CANCELLED
|
||||
* (spoiled leaf voided); ACTIVE->LOST (reported lost/stolen post-issuance); ACTIVE->WITHDRAWN (account
|
||||
* closed); ACTIVE->USED (manually recorded once the branch sees the leaf clear on the bank/clearing
|
||||
* statement — this CBS has no clearing/presentment integration, so USED can never be set automatically).
|
||||
* LOST, CANCELLED, WITHDRAWN and USED are all terminal — a recovered "lost" cheque conventionally stays
|
||||
* hotlisted since the customer has typically already been issued a replacement book.
|
||||
*/
|
||||
public enum ChequeLeafStatus {
|
||||
|
||||
ACTIVE, //
|
||||
STOP_PAYMENT, //
|
||||
CANCELLED, //
|
||||
LOST, //
|
||||
WITHDRAWN, //
|
||||
USED; //
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
|
||||
/** Cheque-leaf stationery master — leaves-per-book, CTS-2010 compliance, and a low-stock reorder threshold. */
|
||||
@Entity
|
||||
@Table(name = "m_cheque_series")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class ChequeSeries extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@Column(name = "series_code", nullable = false, length = 20)
|
||||
private String seriesCode;
|
||||
|
||||
@Column(name = "name", nullable = false, length = 100)
|
||||
private String name;
|
||||
|
||||
@Column(name = "leaves_per_book", nullable = false)
|
||||
private Integer leavesPerBook;
|
||||
|
||||
/** SAVINGS / CURRENT — which account scheme this stationery may be issued to. */
|
||||
@Column(name = "applicable_account_type", nullable = false, length = 10)
|
||||
private String applicableAccountType;
|
||||
|
||||
@Column(name = "cts_compliant", nullable = false)
|
||||
private boolean ctsCompliant;
|
||||
|
||||
@Column(name = "reorder_level")
|
||||
private Integer reorderLevel;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean active;
|
||||
|
||||
@Column(name = "created_by")
|
||||
private Long createdBy;
|
||||
|
||||
@Column(name = "created_on")
|
||||
private OffsetDateTime createdOn;
|
||||
|
||||
public static ChequeSeries createNew(final String seriesCode, final String name, final Integer leavesPerBook,
|
||||
final ChequeSeriesApplicability applicableAccountType, final boolean ctsCompliant, final Integer reorderLevel,
|
||||
final Long createdBy) {
|
||||
final ChequeSeries series = new ChequeSeries();
|
||||
series.seriesCode = seriesCode;
|
||||
series.name = name;
|
||||
series.leavesPerBook = leavesPerBook;
|
||||
series.applicableAccountType = applicableAccountType.name();
|
||||
series.ctsCompliant = ctsCompliant;
|
||||
series.reorderLevel = reorderLevel;
|
||||
series.active = true;
|
||||
series.createdBy = createdBy;
|
||||
series.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return series;
|
||||
}
|
||||
|
||||
public void updateDetails(final String name, final Integer leavesPerBook, final ChequeSeriesApplicability applicableAccountType,
|
||||
final Boolean ctsCompliant, final Integer reorderLevel) {
|
||||
if (name != null) {
|
||||
this.name = name;
|
||||
}
|
||||
if (leavesPerBook != null) {
|
||||
this.leavesPerBook = leavesPerBook;
|
||||
}
|
||||
if (applicableAccountType != null) {
|
||||
this.applicableAccountType = applicableAccountType.name();
|
||||
}
|
||||
if (ctsCompliant != null) {
|
||||
this.ctsCompliant = ctsCompliant;
|
||||
}
|
||||
if (reorderLevel != null) {
|
||||
this.reorderLevel = reorderLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
/** Whether this series' stationery may be issued to the given account type. */
|
||||
public boolean appliesTo(final ChequeAccountType accountType) {
|
||||
final ChequeSeriesApplicability applicability = ChequeSeriesApplicability.fromString(this.applicableAccountType);
|
||||
// a legacy row with an unrecognised/blank value (e.g. a pre-existing "BOTH") is treated permissively
|
||||
return applicability == null || applicability.appliesTo(accountType);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Which account scheme a cheque series' stationery is meant for. Real banks stock physically different
|
||||
* cheque books for Savings (SB) and Current (CA) accounts (different leaf count, design, MICR transaction
|
||||
* code), so every series is tagged SAVINGS-only or CURRENT-only — never both — and issuance is validated
|
||||
* against it.
|
||||
*/
|
||||
public enum ChequeSeriesApplicability {
|
||||
|
||||
SAVINGS, //
|
||||
CURRENT; //
|
||||
|
||||
public static ChequeSeriesApplicability fromString(final String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
for (final ChequeSeriesApplicability applicability : values()) {
|
||||
if (applicability.name().equalsIgnoreCase(value.trim())) {
|
||||
return applicability;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Whether a series with this applicability may be issued to the given account type. */
|
||||
public boolean appliesTo(final ChequeAccountType accountType) {
|
||||
if (accountType == null) {
|
||||
return false;
|
||||
}
|
||||
return this.name().equals(accountType.name());
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ChequeSeriesRepository extends JpaRepository<ChequeSeries, Long> {
|
||||
|
||||
boolean existsBySeriesCode(String seriesCode);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeSeriesNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeSeriesRepositoryWrapper {
|
||||
|
||||
private final ChequeSeriesRepository repository;
|
||||
|
||||
public ChequeSeries findOneWithNotFoundDetection(final Long id) {
|
||||
return this.repository.findById(id).orElseThrow(() -> new ChequeSeriesNotFoundException(id));
|
||||
}
|
||||
|
||||
public ChequeSeries saveAndFlush(final ChequeSeries series) {
|
||||
return this.repository.saveAndFlush(series);
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Version;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
|
||||
/**
|
||||
* A contiguous range of cheque-leaf serials currently sitting at one office, with a drawdown cursor
|
||||
* ({@link #nextSerialNo}) that advances every time a sub-range is issued to a customer or transferred to
|
||||
* another office — both are structurally the same "take N off the front of the remaining range" operation.
|
||||
* This single concept covers both an HO bulk receipt from the press and a branch's received-transfer-in
|
||||
* stock; a transfer-in is materially the same thing as a receipt, just sourced from another office.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_cheque_stock_lot")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class ChequeStockLot extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "series_id", nullable = false)
|
||||
private ChequeSeries series;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "office_id", nullable = false)
|
||||
private Office office;
|
||||
|
||||
@Column(name = "range_start", nullable = false)
|
||||
private Long rangeStart;
|
||||
|
||||
@Column(name = "range_end", nullable = false)
|
||||
private Long rangeEnd;
|
||||
|
||||
@Column(name = "next_serial_no", nullable = false)
|
||||
private Long nextSerialNo;
|
||||
|
||||
@Column(name = "source", nullable = false, length = 20)
|
||||
private String source;
|
||||
|
||||
@Column(name = "source_ref_id")
|
||||
private Long sourceRefId;
|
||||
|
||||
@Column(name = "batch_ref", length = 100)
|
||||
private String batchRef;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 25)
|
||||
private String status;
|
||||
|
||||
@Column(name = "created_by")
|
||||
private Long createdBy;
|
||||
|
||||
@Column(name = "created_on")
|
||||
private OffsetDateTime createdOn;
|
||||
|
||||
@Column(name = "received_by")
|
||||
private Long receivedBy;
|
||||
|
||||
@Column(name = "received_on")
|
||||
private OffsetDateTime receivedOn;
|
||||
|
||||
@Version
|
||||
@Column(name = "version")
|
||||
private Integer version;
|
||||
|
||||
public static ChequeStockLot createReceipt(final ChequeSeries series, final Office office, final Long rangeStart,
|
||||
final Long rangeEnd, final String batchRef, final Long createdBy) {
|
||||
final ChequeStockLot lot = new ChequeStockLot();
|
||||
lot.series = series;
|
||||
lot.office = office;
|
||||
lot.rangeStart = rangeStart;
|
||||
lot.rangeEnd = rangeEnd;
|
||||
lot.nextSerialNo = rangeStart;
|
||||
lot.source = ChequeStockLotSource.RECEIPT.name();
|
||||
lot.batchRef = batchRef;
|
||||
lot.status = ChequeStockLotStatus.AVAILABLE.name();
|
||||
lot.createdBy = createdBy;
|
||||
lot.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return lot;
|
||||
}
|
||||
|
||||
public static ChequeStockLot createTransferIn(final ChequeSeries series, final Office office, final Long rangeStart,
|
||||
final Long rangeEnd, final Long transferId) {
|
||||
final ChequeStockLot lot = new ChequeStockLot();
|
||||
lot.series = series;
|
||||
lot.office = office;
|
||||
lot.rangeStart = rangeStart;
|
||||
lot.rangeEnd = rangeEnd;
|
||||
lot.nextSerialNo = rangeStart;
|
||||
lot.source = ChequeStockLotSource.TRANSFER_IN.name();
|
||||
lot.sourceRefId = transferId;
|
||||
lot.status = ChequeStockLotStatus.DISPATCHED.name();
|
||||
lot.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return lot;
|
||||
}
|
||||
|
||||
/** Leaves still available to draw down, regardless of status. */
|
||||
public long remainingLeaves() {
|
||||
return this.rangeEnd - this.nextSerialNo + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the next {@code qty} leaf serials off the front of this lot's remaining range, advancing the
|
||||
* cursor and marking the lot EXHAUSTED if nothing remains. Used identically for issuing a book to a
|
||||
* customer and for dispatching a sub-range to another office.
|
||||
*/
|
||||
public long[] drawDown(final int qty) {
|
||||
if (!ChequeStockLotStatus.AVAILABLE.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.not.available",
|
||||
"Cheque stock lot " + this.getId() + " is not available for drawdown.", this.getId());
|
||||
}
|
||||
if (remainingLeaves() < qty) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.insufficient.stock",
|
||||
"Cheque stock lot " + this.getId() + " does not have enough remaining leaves.", this.getId());
|
||||
}
|
||||
final long start = this.nextSerialNo;
|
||||
final long end = start + qty - 1;
|
||||
this.nextSerialNo = end + 1;
|
||||
if (this.nextSerialNo > this.rangeEnd) {
|
||||
this.status = ChequeStockLotStatus.EXHAUSTED.name();
|
||||
}
|
||||
return new long[] { start, end };
|
||||
}
|
||||
|
||||
/** Transfer-in receive-ack: the destination lot becomes usable at its new custodian office. */
|
||||
public void markAvailable(final Long receivedBy) {
|
||||
if (!ChequeStockLotStatus.DISPATCHED.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.not.dispatched",
|
||||
"Cheque stock lot " + this.getId() + " is not awaiting receipt.", this.getId());
|
||||
}
|
||||
this.status = ChequeStockLotStatus.AVAILABLE.name();
|
||||
this.receivedBy = receivedBy;
|
||||
this.receivedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
}
|
||||
|
||||
/** Transfer rejected in transit: the destination lot never becomes usable. */
|
||||
public void cancel() {
|
||||
this.status = ChequeStockLotStatus.CANCELLED.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the remaining undrawn stock from {@code fromSerialNo} onward as lost/stolen. Only a suffix of
|
||||
* the remaining range can be reported (an arbitrary internal sub-range would imply a custody failure
|
||||
* severe enough that manual reconciliation, not an automatic system action, is the honest response).
|
||||
* Returns the pre-mutation range end so the caller can generate LOST leaf rows for
|
||||
* [fromSerialNo, returned value].
|
||||
*/
|
||||
public long reportRemainingLost(final long fromSerialNo) {
|
||||
if (fromSerialNo < this.nextSerialNo || fromSerialNo > this.rangeEnd) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.invalid.report.range",
|
||||
"The reported range must be a suffix of the lot's remaining undrawn stock.", this.getId());
|
||||
}
|
||||
final long originalRangeEnd = this.rangeEnd;
|
||||
if (fromSerialNo == this.nextSerialNo) {
|
||||
this.nextSerialNo = this.rangeEnd + 1;
|
||||
this.status = ChequeStockLotStatus.CANCELLED.name();
|
||||
} else {
|
||||
this.rangeEnd = fromSerialNo - 1;
|
||||
this.status = ChequeStockLotStatus.PARTIALLY_COMPROMISED.name();
|
||||
}
|
||||
return originalRangeEnd;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface ChequeStockLotRepository extends JpaRepository<ChequeStockLot, Long> {
|
||||
|
||||
List<ChequeStockLot> findBySeriesIdAndOfficeIdAndStatusOrderByIdAsc(Long seriesId, Long officeId, String status);
|
||||
|
||||
/**
|
||||
* Anti-duplication control: a newly received range must not overlap any existing lot's range within the SAME
|
||||
* series. Each series is its own stationery batch from the press (different leaf count/paper stock) and is
|
||||
* numbered independently — two different series legitimately reusing the same numeric range is normal in
|
||||
* real-world cheque stock, matching how the press assigns a fresh serial block per print order.
|
||||
*/
|
||||
@Query("SELECT COUNT(l) FROM ChequeStockLot l WHERE l.series.id = :seriesId AND l.rangeStart <= :rangeEnd AND l.rangeEnd >= :rangeStart")
|
||||
long countOverlappingInSeries(@Param("seriesId") long seriesId, @Param("rangeStart") long rangeStart,
|
||||
@Param("rangeEnd") long rangeEnd);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeStockLotNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeStockLotRepositoryWrapper {
|
||||
|
||||
private final ChequeStockLotRepository repository;
|
||||
|
||||
public ChequeStockLot findOneWithNotFoundDetection(final Long id) {
|
||||
return this.repository.findById(id).orElseThrow(() -> new ChequeStockLotNotFoundException(id));
|
||||
}
|
||||
|
||||
public ChequeStockLot saveAndFlush(final ChequeStockLot lot) {
|
||||
return this.repository.saveAndFlush(lot);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
/** How a {@link ChequeStockLot} came to exist at its current custodian office. */
|
||||
public enum ChequeStockLotSource {
|
||||
|
||||
RECEIPT, //
|
||||
TRANSFER_IN; //
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
/**
|
||||
* Lifecycle of a {@link ChequeStockLot}. DISPATCHED = in transit to another office, awaiting receive-ack.
|
||||
* AVAILABLE = usable at this office for issuance or further transfer-out. EXHAUSTED = fully drawn down.
|
||||
* PARTIALLY_COMPROMISED = a suffix of the remaining range was reported lost/stolen, but a front portion is
|
||||
* still issuable. CANCELLED = fully voided (rejected transfer, or entirely reported lost/stolen).
|
||||
*/
|
||||
public enum ChequeStockLotStatus {
|
||||
|
||||
DISPATCHED, //
|
||||
AVAILABLE, //
|
||||
EXHAUSTED, //
|
||||
PARTIALLY_COMPROMISED, //
|
||||
CANCELLED; //
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.office.domain.Office;
|
||||
|
||||
/**
|
||||
* Audit/workflow record of a stock movement between offices. The actual range drawdown at the source and
|
||||
* the destination lot creation happen as part of dispatch (see write-service); this row tracks the
|
||||
* dispatch → receive-ack (or reject) lifecycle.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_cheque_stock_transfer")
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
public class ChequeStockTransfer extends AbstractPersistableCustom<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "series_id", nullable = false)
|
||||
private ChequeSeries series;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "from_office_id", nullable = false)
|
||||
private Office fromOffice;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "to_office_id", nullable = false)
|
||||
private Office toOffice;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "from_lot_id", nullable = false)
|
||||
private ChequeStockLot fromLot;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "to_lot_id", nullable = false)
|
||||
private ChequeStockLot toLot;
|
||||
|
||||
@Column(name = "range_start", nullable = false)
|
||||
private Long rangeStart;
|
||||
|
||||
@Column(name = "range_end", nullable = false)
|
||||
private Long rangeEnd;
|
||||
|
||||
@Column(name = "total_leaves", nullable = false)
|
||||
private Integer totalLeaves;
|
||||
|
||||
@Column(name = "dispatch_date", nullable = false)
|
||||
private LocalDate dispatchDate;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "received_date")
|
||||
private LocalDate receivedDate;
|
||||
|
||||
@Column(name = "dispatched_by")
|
||||
private Long dispatchedBy;
|
||||
|
||||
@Column(name = "received_by")
|
||||
private Long receivedBy;
|
||||
|
||||
@Column(name = "created_on")
|
||||
private OffsetDateTime createdOn;
|
||||
|
||||
public static ChequeStockTransfer createNew(final ChequeSeries series, final Office fromOffice, final Office toOffice,
|
||||
final ChequeStockLot fromLot, final ChequeStockLot toLot, final Long rangeStart, final Long rangeEnd,
|
||||
final Integer totalLeaves, final LocalDate dispatchDate, final Long dispatchedBy) {
|
||||
final ChequeStockTransfer transfer = new ChequeStockTransfer();
|
||||
transfer.series = series;
|
||||
transfer.fromOffice = fromOffice;
|
||||
transfer.toOffice = toOffice;
|
||||
transfer.fromLot = fromLot;
|
||||
transfer.toLot = toLot;
|
||||
transfer.rangeStart = rangeStart;
|
||||
transfer.rangeEnd = rangeEnd;
|
||||
transfer.totalLeaves = totalLeaves;
|
||||
transfer.dispatchDate = dispatchDate;
|
||||
transfer.status = ChequeStockTransferStatus.DISPATCHED.name();
|
||||
transfer.dispatchedBy = dispatchedBy;
|
||||
transfer.createdOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
return transfer;
|
||||
}
|
||||
|
||||
public void markReceived(final Long receivedBy, final LocalDate receivedDate) {
|
||||
if (!ChequeStockTransferStatus.DISPATCHED.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocktransfer.not.dispatched",
|
||||
"Transfer " + this.getId() + " is not awaiting receipt.", this.getId());
|
||||
}
|
||||
this.status = ChequeStockTransferStatus.RECEIVED.name();
|
||||
this.receivedBy = receivedBy;
|
||||
this.receivedDate = receivedDate;
|
||||
}
|
||||
|
||||
public void reject(final Long rejectedBy, final LocalDate rejectedDate) {
|
||||
if (!ChequeStockTransferStatus.DISPATCHED.name().equals(this.status)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocktransfer.not.dispatched",
|
||||
"Transfer " + this.getId() + " is not awaiting receipt.", this.getId());
|
||||
}
|
||||
this.status = ChequeStockTransferStatus.REJECTED.name();
|
||||
this.receivedBy = rejectedBy;
|
||||
this.receivedDate = rejectedDate;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ChequeStockTransferRepository extends JpaRepository<ChequeStockTransfer, Long> {
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeStockTransferNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeStockTransferRepositoryWrapper {
|
||||
|
||||
private final ChequeStockTransferRepository repository;
|
||||
|
||||
public ChequeStockTransfer findOneWithNotFoundDetection(final Long id) {
|
||||
return this.repository.findById(id).orElseThrow(() -> new ChequeStockTransferNotFoundException(id));
|
||||
}
|
||||
|
||||
public ChequeStockTransfer saveAndFlush(final ChequeStockTransfer transfer) {
|
||||
return this.repository.saveAndFlush(transfer);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.chequebook.domain;
|
||||
|
||||
public enum ChequeStockTransferStatus {
|
||||
|
||||
DISPATCHED, //
|
||||
RECEIVED, //
|
||||
REJECTED; //
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.chequebook.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ChequeBookNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ChequeBookNotFoundException(final Long id) {
|
||||
super("error.msg.chequebook.id.invalid", "Cheque book with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+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.chequebook.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ChequeBookPendingRequestNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ChequeBookPendingRequestNotFoundException(final Long id) {
|
||||
super("error.msg.chequebookpendingrequest.id.invalid", "Cheque book pending request with identifier " + id + " does not exist",
|
||||
id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.chequebook.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ChequeLeafNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ChequeLeafNotFoundException(final Long leafSerialNo) {
|
||||
super("error.msg.chequeleaf.serialno.invalid", "No cheque leaf found with serial number " + leafSerialNo, leafSerialNo);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.chequebook.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ChequeSeriesNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ChequeSeriesNotFoundException(final Long id) {
|
||||
super("error.msg.chequeseries.id.invalid", "Cheque series with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.chequebook.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ChequeStockLotNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ChequeStockLotNotFoundException(final Long id) {
|
||||
super("error.msg.chequestocklot.id.invalid", "Cheque stock lot with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.chequebook.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class ChequeStockTransferNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public ChequeStockTransferNotFoundException(final Long id) {
|
||||
super("error.msg.chequestocktransfer.id.invalid", "Cheque stock transfer with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOKPENDINGREQUEST", action = "APPROVE")
|
||||
@RequiredArgsConstructor
|
||||
public class ApproveChequeBookPendingRequestCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.approvePendingRequest(command.entityId());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "CANCELLEAF")
|
||||
@RequiredArgsConstructor
|
||||
public class CancelLeafChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.cancelLeaf(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.requestChequeBook(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESERIES", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateChequeSeriesCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createSeries(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESTOCKLOT", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateChequeStockLotCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.receiveStock(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESTOCKTRANSFER", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateChequeStockTransferCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.dispatchStockTransfer(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESERIES", action = "DELETE")
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteChequeSeriesCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.deleteSeries(command.entityId());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "ISSUE")
|
||||
@RequiredArgsConstructor
|
||||
public class IssueChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.issueChequeBook(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "MARKUSED")
|
||||
@RequiredArgsConstructor
|
||||
public class MarkUsedChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.markLeafUsed(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESTOCKTRANSFER", action = "RECEIVE")
|
||||
@RequiredArgsConstructor
|
||||
public class ReceiveChequeStockTransferCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.receiveStockTransfer(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "REJECT")
|
||||
@RequiredArgsConstructor
|
||||
public class RejectChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.rejectChequeBook(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOKPENDINGREQUEST", action = "REJECT")
|
||||
@RequiredArgsConstructor
|
||||
public class RejectChequeBookPendingRequestCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.rejectPendingRequest(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESTOCKTRANSFER", action = "REJECT")
|
||||
@RequiredArgsConstructor
|
||||
public class RejectChequeStockTransferCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.rejectStockTransfer(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "REPORTLOST")
|
||||
@RequiredArgsConstructor
|
||||
public class ReportLostChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.reportChequeBookLost(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESTOCKLOT", action = "REPORTLOST")
|
||||
@RequiredArgsConstructor
|
||||
public class ReportLostChequeStockLotCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.reportStockLotLost(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "REVOKESTOPPAYMENT")
|
||||
@RequiredArgsConstructor
|
||||
public class RevokeStopPaymentChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.revokeStopPayment(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "STOPPAYMENT")
|
||||
@RequiredArgsConstructor
|
||||
public class StopPaymentChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.stopPayment(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUESERIES", action = "UPDATE")
|
||||
@RequiredArgsConstructor
|
||||
public class UpdateChequeSeriesCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.updateSeries(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.chequebook.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.chequebook.service.ChequeBookWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CHEQUEBOOK", action = "WITHDRAW")
|
||||
@RequiredArgsConstructor
|
||||
public class WithdrawChequeBookCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ChequeBookWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.withdrawChequeBook(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
/**
|
||||
* 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.chequebook.serialization;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.chequebook.api.ChequeBookApiConstants;
|
||||
import org.apache.fineract.portfolio.chequebook.api.ChequeBookApiJsonInputParams;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeAccountType;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class ChequeBookCommandFromApiJsonDeserializer {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
public void validateForCreateSeries(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.seriesCreateParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_SERIES);
|
||||
|
||||
final String seriesCode = this.fromApiJsonHelper.extractStringNamed(ChequeBookApiJsonInputParams.SERIES_CODE.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.SERIES_CODE.getValue()).value(seriesCode).notBlank().notExceedingLengthOf(20);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(ChequeBookApiJsonInputParams.NAME.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.NAME.getValue()).value(name).notBlank().notExceedingLengthOf(100);
|
||||
|
||||
final Integer leavesPerBook = this.fromApiJsonHelper
|
||||
.extractIntegerSansLocaleNamed(ChequeBookApiJsonInputParams.LEAVES_PER_BOOK.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.LEAVES_PER_BOOK.getValue()).value(leavesPerBook).notNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
final Integer reorderLevel = this.fromApiJsonHelper
|
||||
.extractIntegerSansLocaleNamed(ChequeBookApiJsonInputParams.REORDER_LEVEL.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.REORDER_LEVEL.getValue()).value(reorderLevel).ignoreIfNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
validateApplicableAccountType(element, v, true);
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
public void validateForUpdateSeries(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.seriesUpdateParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_SERIES);
|
||||
|
||||
final Integer leavesPerBook = this.fromApiJsonHelper
|
||||
.extractIntegerSansLocaleNamed(ChequeBookApiJsonInputParams.LEAVES_PER_BOOK.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.LEAVES_PER_BOOK.getValue()).value(leavesPerBook).ignoreIfNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
final Integer reorderLevel = this.fromApiJsonHelper
|
||||
.extractIntegerSansLocaleNamed(ChequeBookApiJsonInputParams.REORDER_LEVEL.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.REORDER_LEVEL.getValue()).value(reorderLevel).ignoreIfNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
validateApplicableAccountType(element, v, false);
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
private void validateApplicableAccountType(final JsonElement element, final DataValidatorBuilder v, final boolean required) {
|
||||
final String applicableAccountType = this.fromApiJsonHelper
|
||||
.extractStringNamed(ChequeBookApiJsonInputParams.APPLICABLE_ACCOUNT_TYPE.getValue(), element);
|
||||
// NB: isOneOfTheseStringValues lowercases the input before comparing, so the expected set must be lowercase
|
||||
final DataValidatorBuilder builder = v.reset().parameter(ChequeBookApiJsonInputParams.APPLICABLE_ACCOUNT_TYPE.getValue())
|
||||
.value(applicableAccountType);
|
||||
if (required) {
|
||||
builder.notBlank();
|
||||
} else {
|
||||
builder.ignoreIfNull();
|
||||
}
|
||||
builder.isOneOfTheseStringValues("savings", "current");
|
||||
}
|
||||
|
||||
public void validateForStockLotReceive(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.stockLotReceiveParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_STOCK_LOT);
|
||||
|
||||
final Long seriesId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.SERIES_ID.getValue()).value(seriesId).notNull().longGreaterThanZero();
|
||||
|
||||
final Long officeId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.OFFICE_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.OFFICE_ID.getValue()).value(officeId).notNull().longGreaterThanZero();
|
||||
|
||||
final Long rangeStart = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.RANGE_START.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.RANGE_START.getValue()).value(rangeStart).notNull().longGreaterThanZero();
|
||||
|
||||
final Long rangeEnd = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.RANGE_END.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.RANGE_END.getValue()).value(rangeEnd).notNull().longGreaterThanZero();
|
||||
|
||||
if (rangeStart != null && rangeEnd != null && rangeEnd <= rangeStart) {
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.RANGE_END.getValue()).value(rangeEnd)
|
||||
.failWithCode("must.be.greater.than.range.start", "rangeEnd must be greater than rangeStart");
|
||||
}
|
||||
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
public void validateForStockLotReportLost(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.stockLotReportLostParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_STOCK_LOT);
|
||||
|
||||
final Long fromSerialNo = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.FROM_SERIAL_NO.getValue(),
|
||||
element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.FROM_SERIAL_NO.getValue()).value(fromSerialNo).notNull().longGreaterThanZero();
|
||||
|
||||
final String reason = this.fromApiJsonHelper.extractStringNamed(ChequeBookApiJsonInputParams.REASON.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.REASON.getValue()).value(reason).notBlank().notExceedingLengthOf(500);
|
||||
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
public void validateForStockTransferDispatch(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.stockTransferDispatchParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_STOCK_TRANSFER);
|
||||
|
||||
final Long seriesId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.SERIES_ID.getValue()).value(seriesId).notNull().longGreaterThanZero();
|
||||
|
||||
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.FROM_OFFICE_ID.getValue(),
|
||||
element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.FROM_OFFICE_ID.getValue()).value(fromOfficeId).notNull().longGreaterThanZero();
|
||||
|
||||
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.TO_OFFICE_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.TO_OFFICE_ID.getValue()).value(toOfficeId).notNull().longGreaterThanZero();
|
||||
|
||||
if (fromOfficeId != null && fromOfficeId.equals(toOfficeId)) {
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.TO_OFFICE_ID.getValue()).value(toOfficeId)
|
||||
.failWithCode("must.differ.from.from.office", "toOfficeId must differ from fromOfficeId");
|
||||
}
|
||||
|
||||
final Long fromLotId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.FROM_LOT_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.FROM_LOT_ID.getValue()).value(fromLotId).notNull().longGreaterThanZero();
|
||||
|
||||
final Integer noOfLeaves = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
|
||||
ChequeBookApiJsonInputParams.NO_OF_LEAVES.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.NO_OF_LEAVES.getValue()).value(noOfLeaves).notNull().integerGreaterThanZero();
|
||||
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
public void validateForBookRequest(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.bookRequestParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_BOOK);
|
||||
|
||||
final String accountType = this.fromApiJsonHelper.extractStringNamed(ChequeBookApiJsonInputParams.ACCOUNT_TYPE.getValue(),
|
||||
element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.ACCOUNT_TYPE.getValue()).value(accountType).notBlank();
|
||||
if (StringUtils.isNotBlank(accountType) && ChequeAccountType.fromString(accountType) == null) {
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.ACCOUNT_TYPE.getValue()).value(accountType)
|
||||
.failWithCode("not.one.of.expected.enumerations", "SAVINGS, CURRENT");
|
||||
}
|
||||
|
||||
final Long accountId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.ACCOUNT_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.ACCOUNT_ID.getValue()).value(accountId).notNull().longGreaterThanZero();
|
||||
|
||||
final Long officeId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.OFFICE_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.OFFICE_ID.getValue()).value(officeId).notNull().longGreaterThanZero();
|
||||
|
||||
final Integer noOfBooks = this.fromApiJsonHelper
|
||||
.extractIntegerSansLocaleNamed(ChequeBookApiJsonInputParams.NO_OF_BOOKS.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.NO_OF_BOOKS.getValue()).value(noOfBooks).notNull().integerGreaterThanZero();
|
||||
|
||||
// the denomination is picked at requisition time, matching a real cheque-book request slip
|
||||
final Long seriesId = this.fromApiJsonHelper.extractLongNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.SERIES_ID.getValue()).value(seriesId).notNull().longGreaterThanZero();
|
||||
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
/** Issue no longer takes a series — it was already fixed at request time; only optional collection-ack. */
|
||||
public void validateForBookIssue(final String json) {
|
||||
parse(json, ChequeBookApiJsonInputParams.bookIssueParams());
|
||||
}
|
||||
|
||||
public void validateForBookReject(final String json) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.bookRejectParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_BOOK);
|
||||
|
||||
final String reason = this.fromApiJsonHelper.extractStringNamed(ChequeBookApiJsonInputParams.REJECTION_REASON.getValue(),
|
||||
element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.REJECTION_REASON.getValue()).value(reason).notBlank().notExceedingLengthOf(500);
|
||||
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
/** Shared by stop-payment / revoke-stop-payment / report-lost / cancel-leaf on a book. */
|
||||
public void validateForBookLeafAction(final String json, final boolean requireReason) {
|
||||
final JsonElement element = parse(json, ChequeBookApiJsonInputParams.bookLeafActionParams());
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ChequeBookApiConstants.RESOURCE_BOOK);
|
||||
|
||||
final Integer fromLeafSeq = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
|
||||
ChequeBookApiJsonInputParams.FROM_LEAF_SEQ.getValue(), element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.FROM_LEAF_SEQ.getValue()).value(fromLeafSeq).ignoreIfNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
final Integer toLeafSeq = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(ChequeBookApiJsonInputParams.TO_LEAF_SEQ.getValue(),
|
||||
element);
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.TO_LEAF_SEQ.getValue()).value(toLeafSeq).ignoreIfNull()
|
||||
.integerGreaterThanZero();
|
||||
|
||||
if (fromLeafSeq != null && toLeafSeq != null && toLeafSeq < fromLeafSeq) {
|
||||
v.reset().parameter(ChequeBookApiJsonInputParams.TO_LEAF_SEQ.getValue()).value(toLeafSeq)
|
||||
.failWithCode("must.be.greater.than.or.equal.to.from.leaf.seq", "toLeafSeq must not be less than fromLeafSeq");
|
||||
}
|
||||
|
||||
final String reason = this.fromApiJsonHelper.extractStringNamed(ChequeBookApiJsonInputParams.REASON.getValue(), element);
|
||||
final DataValidatorBuilder reasonValidator = v.reset().parameter(ChequeBookApiJsonInputParams.REASON.getValue()).value(reason);
|
||||
if (requireReason) {
|
||||
reasonValidator.notBlank().notExceedingLengthOf(500);
|
||||
} else {
|
||||
reasonValidator.ignoreIfNull().notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
private JsonElement parse(final String json, final Set<String> supportedParams) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParams);
|
||||
return this.fromApiJsonHelper.parse(json);
|
||||
}
|
||||
|
||||
private void throwIfErrors(final List<ApiParameterError> errors) {
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.chequebook.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeBookData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeBookPendingRequestData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeLeafData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeSeriesData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeStockLotData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeStockTransferData;
|
||||
|
||||
public interface ChequeBookReadPlatformService {
|
||||
|
||||
/** {@code status}: "ACTIVE" / "INACTIVE" — maps to the series' is_active flag. */
|
||||
Page<ChequeSeriesData> retrieveAllSeries(String status, Integer offset, Integer limit);
|
||||
|
||||
ChequeSeriesData retrieveSeries(Long id);
|
||||
|
||||
Page<ChequeStockLotData> retrieveStockLots(Long officeId, Long seriesId, String status, Integer offset, Integer limit);
|
||||
|
||||
ChequeStockLotData retrieveStockLot(Long id);
|
||||
|
||||
Page<ChequeStockTransferData> retrieveStockTransfers(String status, Integer offset, Integer limit);
|
||||
|
||||
ChequeStockTransferData retrieveStockTransfer(Long id);
|
||||
|
||||
Page<ChequeBookData> retrieveAllBooks(String accountType, Long accountId, Long officeId, String status, Integer offset,
|
||||
Integer limit);
|
||||
|
||||
ChequeBookData retrieveBook(Long id);
|
||||
|
||||
List<ChequeLeafData> retrieveLeaves(Long chequeBookId);
|
||||
|
||||
/**
|
||||
* Quick-lookup — the single teller/counter operation that answers "is this cheque number safe to honour".
|
||||
* A bare serial number is only unique within its own series, so more than one series can legitimately match.
|
||||
*/
|
||||
List<ChequeLeafData> retrieveLeafBySerialNo(Long leafSerialNo);
|
||||
|
||||
/** Bespoke maker-checker queue for series/stock/book creation requests — see {@code m_chequebook_pending_request}. */
|
||||
Page<ChequeBookPendingRequestData> retrievePendingRequests(String status, String fromDate, String toDate, Integer offset,
|
||||
Integer limit);
|
||||
|
||||
ChequeBookPendingRequestData retrievePendingRequest(Long id);
|
||||
}
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
/**
|
||||
* 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.chequebook.service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeBookData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeBookPendingRequestData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeLeafData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeSeriesData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeStockLotData;
|
||||
import org.apache.fineract.portfolio.chequebook.data.ChequeStockTransferData;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeAccountType;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeBookNotFoundException;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeBookPendingRequestNotFoundException;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeLeafNotFoundException;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeSeriesNotFoundException;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeStockLotNotFoundException;
|
||||
import org.apache.fineract.portfolio.chequebook.exception.ChequeStockTransferNotFoundException;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ChequeBookReadPlatformServiceImpl implements ChequeBookReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final SavingsAccountRepository savingsAccountRepository;
|
||||
private final CurrentAccountRepository currentAccountRepository;
|
||||
private final SeriesMapper seriesMapper = new SeriesMapper();
|
||||
private final StockLotMapper stockLotMapper = new StockLotMapper();
|
||||
private final StockTransferMapper stockTransferMapper = new StockTransferMapper();
|
||||
private final BookMapper bookMapper = new BookMapper();
|
||||
private final LeafMapper leafMapper = new LeafMapper();
|
||||
private final PendingRequestMapper pendingRequestMapper = new PendingRequestMapper();
|
||||
|
||||
@Override
|
||||
public Page<ChequeSeriesData> retrieveAllSeries(final String status, final Integer offset, final Integer limit) {
|
||||
final StringBuilder where = new StringBuilder(" where 1=1");
|
||||
final List<Object> args = new ArrayList<>();
|
||||
if ("ACTIVE".equalsIgnoreCase(status)) {
|
||||
where.append(" and is_active = true");
|
||||
} else if ("INACTIVE".equalsIgnoreCase(status)) {
|
||||
where.append(" and is_active = false");
|
||||
}
|
||||
|
||||
final String countSql = "select count(*) from (select " + this.seriesMapper.schema() + where + ") x";
|
||||
final Long totalFilteredRecords = this.jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
|
||||
|
||||
final StringBuilder pageSql = new StringBuilder("select ").append(this.seriesMapper.schema()).append(where)
|
||||
.append(" order by id desc limit ? offset ?");
|
||||
final List<Object> pageArgs = new ArrayList<>(args);
|
||||
pageArgs.add(limit);
|
||||
pageArgs.add(offset);
|
||||
final List<ChequeSeriesData> series = this.jdbcTemplate.query(pageSql.toString(), this.seriesMapper, pageArgs.toArray());
|
||||
return new Page<>(series, totalFilteredRecords == null ? 0 : totalFilteredRecords.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeSeriesData retrieveSeries(final Long id) {
|
||||
try {
|
||||
final String sql = "select " + this.seriesMapper.schema() + " where id = ?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.seriesMapper, id);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ChequeSeriesNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ChequeStockLotData> retrieveStockLots(final Long officeId, final Long seriesId, final String status,
|
||||
final Integer offset, final Integer limit) {
|
||||
final StringBuilder where = new StringBuilder(" where 1=1");
|
||||
final List<Object> args = new ArrayList<>();
|
||||
if (officeId != null) {
|
||||
where.append(" and l.office_id = ?");
|
||||
args.add(officeId);
|
||||
}
|
||||
if (seriesId != null) {
|
||||
where.append(" and l.series_id = ?");
|
||||
args.add(seriesId);
|
||||
}
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
where.append(" and l.status = ?");
|
||||
args.add(status.trim().toUpperCase());
|
||||
}
|
||||
|
||||
final String countSql = "select count(*) from (select " + this.stockLotMapper.schema() + where + ") x";
|
||||
final Long totalFilteredRecords = this.jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
|
||||
|
||||
final StringBuilder pageSql = new StringBuilder("select ").append(this.stockLotMapper.schema()).append(where)
|
||||
.append(" order by l.id desc limit ? offset ?");
|
||||
final List<Object> pageArgs = new ArrayList<>(args);
|
||||
pageArgs.add(limit);
|
||||
pageArgs.add(offset);
|
||||
final List<ChequeStockLotData> lots = this.jdbcTemplate.query(pageSql.toString(), this.stockLotMapper, pageArgs.toArray());
|
||||
return new Page<>(lots, totalFilteredRecords == null ? 0 : totalFilteredRecords.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeStockLotData retrieveStockLot(final Long id) {
|
||||
try {
|
||||
final String sql = "select " + this.stockLotMapper.schema() + " where l.id = ?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.stockLotMapper, id);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ChequeStockLotNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ChequeStockTransferData> retrieveStockTransfers(final String status, final Integer offset, final Integer limit) {
|
||||
final StringBuilder where = new StringBuilder(" where 1=1");
|
||||
final List<Object> args = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
where.append(" and t.status = ?");
|
||||
args.add(status.trim().toUpperCase());
|
||||
}
|
||||
|
||||
final String countSql = "select count(*) from (select " + this.stockTransferMapper.schema() + where + ") x";
|
||||
final Long totalFilteredRecords = this.jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
|
||||
|
||||
final StringBuilder pageSql = new StringBuilder("select ").append(this.stockTransferMapper.schema()).append(where)
|
||||
.append(" order by t.id desc limit ? offset ?");
|
||||
final List<Object> pageArgs = new ArrayList<>(args);
|
||||
pageArgs.add(limit);
|
||||
pageArgs.add(offset);
|
||||
final List<ChequeStockTransferData> transfers = this.jdbcTemplate.query(pageSql.toString(), this.stockTransferMapper,
|
||||
pageArgs.toArray());
|
||||
return new Page<>(transfers, totalFilteredRecords == null ? 0 : totalFilteredRecords.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeStockTransferData retrieveStockTransfer(final Long id) {
|
||||
try {
|
||||
final String sql = "select " + this.stockTransferMapper.schema() + " where t.id = ?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.stockTransferMapper, id);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ChequeStockTransferNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ChequeBookData> retrieveAllBooks(final String accountType, final Long accountId, final Long officeId,
|
||||
final String status, final Integer offset, final Integer limit) {
|
||||
final StringBuilder where = new StringBuilder(" where 1=1");
|
||||
final List<Object> args = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(accountType)) {
|
||||
where.append(" and b.account_type = ?");
|
||||
args.add(accountType.trim().toUpperCase());
|
||||
}
|
||||
if (accountId != null) {
|
||||
where.append(" and b.account_id = ?");
|
||||
args.add(accountId);
|
||||
}
|
||||
if (officeId != null) {
|
||||
where.append(" and b.office_id = ?");
|
||||
args.add(officeId);
|
||||
}
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
where.append(" and b.status = ?");
|
||||
args.add(status.trim().toUpperCase());
|
||||
}
|
||||
|
||||
// wrap the mapper's own select (joins to office/series) so the count always matches the page query 1:1
|
||||
final String countSql = "select count(*) from (select " + this.bookMapper.schema() + where + ") x";
|
||||
final Long totalFilteredRecords = this.jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
|
||||
|
||||
final StringBuilder pageSql = new StringBuilder("select ").append(this.bookMapper.schema()).append(where)
|
||||
.append(" order by b.id desc limit ? offset ?");
|
||||
final List<Object> pageArgs = new ArrayList<>(args);
|
||||
pageArgs.add(limit);
|
||||
pageArgs.add(offset);
|
||||
final List<ChequeBookData> books = this.jdbcTemplate.query(pageSql.toString(), this.bookMapper, pageArgs.toArray());
|
||||
final List<ChequeBookData> withAccountInfo = books.stream().map(this::withAccountInfo).toList();
|
||||
return new Page<>(withAccountInfo, totalFilteredRecords == null ? 0 : totalFilteredRecords.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeBookData retrieveBook(final Long id) {
|
||||
try {
|
||||
final String sql = "select " + this.bookMapper.schema() + " where b.id = ?";
|
||||
final ChequeBookData book = this.jdbcTemplate.queryForObject(sql, this.bookMapper, id);
|
||||
return withAccountInfo(book);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ChequeBookNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChequeLeafData> retrieveLeaves(final Long chequeBookId) {
|
||||
final String sql = "select " + this.leafMapper.schema() + " where f.cheque_book_id = ? order by f.leaf_seq_no asc";
|
||||
final List<ChequeLeafData> leaves = this.jdbcTemplate.query(sql, this.leafMapper, chequeBookId);
|
||||
return leaves.stream().map(this::withAccountInfo).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ChequeLeafData> retrieveLeafBySerialNo(final Long leafSerialNo) {
|
||||
// a bare serial number is only unique WITHIN a series (see ChequeStockLotRepository.countOverlappingInSeries) —
|
||||
// two different series can legitimately share a number, so this can return more than one match
|
||||
final String sql = "select " + this.leafMapper.schema() + " where f.leaf_serial_no = ? order by s.series_code asc";
|
||||
final List<ChequeLeafData> leaves = this.jdbcTemplate.query(sql, this.leafMapper, leafSerialNo);
|
||||
if (leaves.isEmpty()) {
|
||||
throw new ChequeLeafNotFoundException(leafSerialNo);
|
||||
}
|
||||
return leaves.stream().map(this::withAccountInfo).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ChequeBookPendingRequestData> retrievePendingRequests(final String status, final String fromDate, final String toDate,
|
||||
final Integer offset, final Integer limit) {
|
||||
final StringBuilder where = new StringBuilder(" where 1=1");
|
||||
final List<Object> args = new ArrayList<>();
|
||||
if (StringUtils.isNotBlank(status)) {
|
||||
where.append(" and status = ?");
|
||||
args.add(status.trim().toUpperCase());
|
||||
}
|
||||
if (StringUtils.isNotBlank(fromDate)) {
|
||||
where.append(" and submitted_on::date >= ?::date");
|
||||
args.add(fromDate);
|
||||
}
|
||||
if (StringUtils.isNotBlank(toDate)) {
|
||||
where.append(" and submitted_on::date <= ?::date");
|
||||
args.add(toDate);
|
||||
}
|
||||
|
||||
final String countSql = "select count(*) from m_chequebook_pending_request" + where;
|
||||
final Long totalFilteredRecords = this.jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
|
||||
|
||||
final StringBuilder pageSql = new StringBuilder("select ").append(this.pendingRequestMapper.schema()).append(where)
|
||||
.append(" order by id desc limit ? offset ?");
|
||||
final List<Object> pageArgs = new ArrayList<>(args);
|
||||
pageArgs.add(limit);
|
||||
pageArgs.add(offset);
|
||||
final List<ChequeBookPendingRequestData> requests = this.jdbcTemplate.query(pageSql.toString(), this.pendingRequestMapper,
|
||||
pageArgs.toArray());
|
||||
return new Page<>(requests, totalFilteredRecords == null ? 0 : totalFilteredRecords.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeBookPendingRequestData retrievePendingRequest(final Long id) {
|
||||
try {
|
||||
final String sql = "select " + this.pendingRequestMapper.schema() + " where id = ?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.pendingRequestMapper, id);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new ChequeBookPendingRequestNotFoundException(id);
|
||||
}
|
||||
}
|
||||
|
||||
/** Best-effort account/client display-name resolution — a missing account never fails the whole read. */
|
||||
private ChequeBookData withAccountInfo(final ChequeBookData book) {
|
||||
final AccountDisplay display = resolveAccountDisplay(book.getAccountType(), book.getAccountId());
|
||||
return ChequeBookData.builder().id(book.getId()).accountType(book.getAccountType()).accountId(book.getAccountId())
|
||||
.accountNo(display.accountNo).clientName(display.clientName).officeId(book.getOfficeId())
|
||||
.officeName(book.getOfficeName()).seriesId(book.getSeriesId()).seriesCode(book.getSeriesCode())
|
||||
.requestedDate(book.getRequestedDate()).requestedBy(book.getRequestedBy()).noOfBooks(book.getNoOfBooks())
|
||||
.noOfLeavesRequested(book.getNoOfLeavesRequested()).status(book.getStatus()).issueDate(book.getIssueDate())
|
||||
.issuedBy(book.getIssuedBy()).lotId(book.getLotId()).rangeStart(book.getRangeStart()).rangeEnd(book.getRangeEnd())
|
||||
.totalLeaves(book.getTotalLeaves()).rejectionReason(book.getRejectionReason())
|
||||
.collectionAcknowledged(book.isCollectionAcknowledged()).collectedDate(book.getCollectedDate())
|
||||
.collectedBy(book.getCollectedBy()).build();
|
||||
}
|
||||
|
||||
private ChequeLeafData withAccountInfo(final ChequeLeafData leaf) {
|
||||
final AccountDisplay display = resolveAccountDisplay(leaf.getAccountType(), leaf.getAccountId());
|
||||
return ChequeLeafData.builder().id(leaf.getId()).chequeBookId(leaf.getChequeBookId()).lotId(leaf.getLotId())
|
||||
.seriesCode(leaf.getSeriesCode()).leafSerialNo(leaf.getLeafSerialNo()).leafSeqNo(leaf.getLeafSeqNo()).status(leaf.getStatus())
|
||||
.stopReason(leaf.getStopReason()).stoppedBy(leaf.getStoppedBy()).stoppedOn(leaf.getStoppedOn())
|
||||
.lostReason(leaf.getLostReason()).lostReportedBy(leaf.getLostReportedBy()).lostReportedOn(leaf.getLostReportedOn())
|
||||
.cancelledReason(leaf.getCancelledReason()).cancelledBy(leaf.getCancelledBy()).cancelledOn(leaf.getCancelledOn())
|
||||
.usedReason(leaf.getUsedReason()).usedBy(leaf.getUsedBy()).usedOn(leaf.getUsedOn())
|
||||
.accountType(leaf.getAccountType()).accountId(leaf.getAccountId()).accountNo(display.accountNo)
|
||||
.clientName(display.clientName).officeName(leaf.getOfficeName()).build();
|
||||
}
|
||||
|
||||
private AccountDisplay resolveAccountDisplay(final String accountType, final Long accountId) {
|
||||
if (accountType == null || accountId == null) {
|
||||
return new AccountDisplay(null, null);
|
||||
}
|
||||
final ChequeAccountType type = ChequeAccountType.fromString(accountType);
|
||||
if (type == ChequeAccountType.SAVINGS) {
|
||||
final SavingsAccount account = this.savingsAccountRepository.findById(accountId).orElse(null);
|
||||
if (account == null) {
|
||||
return new AccountDisplay(null, null);
|
||||
}
|
||||
final String clientName = account.getClient() == null ? null : account.getClient().getDisplayName();
|
||||
return new AccountDisplay(account.getAccountNumber(), clientName);
|
||||
}
|
||||
if (type == ChequeAccountType.CURRENT) {
|
||||
final CurrentAccount account = this.currentAccountRepository.findById(accountId).orElse(null);
|
||||
if (account == null) {
|
||||
return new AccountDisplay(null, null);
|
||||
}
|
||||
final String clientName = account.getClient() == null ? null : account.getClient().getDisplayName();
|
||||
return new AccountDisplay(account.getAccountNo(), clientName);
|
||||
}
|
||||
return new AccountDisplay(null, null);
|
||||
}
|
||||
|
||||
private static final class AccountDisplay {
|
||||
|
||||
private final String accountNo;
|
||||
private final String clientName;
|
||||
|
||||
private AccountDisplay(final String accountNo, final String clientName) {
|
||||
this.accountNo = accountNo;
|
||||
this.clientName = clientName;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SeriesMapper implements RowMapper<ChequeSeriesData> {
|
||||
|
||||
String schema() {
|
||||
return " id, series_code, name, leaves_per_book, applicable_account_type, cts_compliant, reorder_level, is_active "
|
||||
+ "from m_cheque_series";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeSeriesData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
return ChequeSeriesData.builder().id(rs.getLong("id")).seriesCode(rs.getString("series_code")).name(rs.getString("name"))
|
||||
.leavesPerBook((Integer) rs.getObject("leaves_per_book"))
|
||||
.applicableAccountType(rs.getString("applicable_account_type")).ctsCompliant(rs.getBoolean("cts_compliant"))
|
||||
.reorderLevel((Integer) rs.getObject("reorder_level")).active(rs.getBoolean("is_active")).build();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StockLotMapper implements RowMapper<ChequeStockLotData> {
|
||||
|
||||
String schema() {
|
||||
return " l.id as id, l.series_id as series_id, s.series_code as series_code, s.leaves_per_book as leaves_per_book, "
|
||||
+ "l.office_id as office_id, o.name as office_name, l.range_start as range_start, l.range_end as range_end, "
|
||||
+ "l.next_serial_no as next_serial_no, l.source as source, l.source_ref_id as source_ref_id, "
|
||||
+ "l.batch_ref as batch_ref, l.status as status, s.reorder_level as reorder_level "
|
||||
+ "from m_cheque_stock_lot l join m_cheque_series s on s.id = l.series_id join m_office o on o.id = l.office_id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeStockLotData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
final long rangeEnd = rs.getLong("range_end");
|
||||
final long nextSerialNo = rs.getLong("next_serial_no");
|
||||
final long remaining = rangeEnd - nextSerialNo + 1;
|
||||
final Integer leavesPerBook = (Integer) rs.getObject("leaves_per_book");
|
||||
// full issuable books of this denomination — a trailing partial book cannot be issued
|
||||
final Long remainingBooks = leavesPerBook != null && leavesPerBook > 0 ? remaining / leavesPerBook : null;
|
||||
final Integer reorderLevel = (Integer) rs.getObject("reorder_level");
|
||||
final boolean lowStock = reorderLevel != null && remaining <= reorderLevel;
|
||||
return ChequeStockLotData.builder().id(rs.getLong("id")).seriesId(rs.getLong("series_id"))
|
||||
.seriesCode(rs.getString("series_code")).leavesPerBook(leavesPerBook).officeId(rs.getLong("office_id"))
|
||||
.officeName(rs.getString("office_name")).rangeStart(rs.getLong("range_start")).rangeEnd(rangeEnd)
|
||||
.nextSerialNo(nextSerialNo).remainingLeaves(remaining).remainingBooks(remainingBooks)
|
||||
.source(rs.getString("source")).sourceRefId((Long) rs.getObject("source_ref_id"))
|
||||
.batchRef(rs.getString("batch_ref")).status(rs.getString("status")).lowStock(lowStock).build();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StockTransferMapper implements RowMapper<ChequeStockTransferData> {
|
||||
|
||||
String schema() {
|
||||
return " t.id as id, t.series_id as series_id, s.series_code as series_code, t.from_office_id as from_office_id, "
|
||||
+ "fo.name as from_office_name, t.to_office_id as to_office_id, tofc.name as to_office_name, "
|
||||
+ "t.from_lot_id as from_lot_id, t.to_lot_id as to_lot_id, t.range_start as range_start, "
|
||||
+ "t.range_end as range_end, t.total_leaves as total_leaves, t.dispatch_date as dispatch_date, "
|
||||
+ "t.status as status, t.received_date as received_date, t.dispatched_by as dispatched_by, "
|
||||
+ "t.received_by as received_by "
|
||||
+ "from m_cheque_stock_transfer t join m_cheque_series s on s.id = t.series_id "
|
||||
+ "join m_office fo on fo.id = t.from_office_id join m_office tofc on tofc.id = t.to_office_id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeStockTransferData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
return ChequeStockTransferData.builder().id(rs.getLong("id")).seriesId(rs.getLong("series_id"))
|
||||
.seriesCode(rs.getString("series_code")).fromOfficeId(rs.getLong("from_office_id"))
|
||||
.fromOfficeName(rs.getString("from_office_name")).toOfficeId(rs.getLong("to_office_id"))
|
||||
.toOfficeName(rs.getString("to_office_name")).fromLotId(rs.getLong("from_lot_id"))
|
||||
.toLotId(rs.getLong("to_lot_id")).rangeStart(rs.getLong("range_start")).rangeEnd(rs.getLong("range_end"))
|
||||
.totalLeaves((Integer) rs.getObject("total_leaves")).dispatchDate(rs.getObject("dispatch_date", LocalDate.class))
|
||||
.status(rs.getString("status")).receivedDate(rs.getObject("received_date", LocalDate.class))
|
||||
.dispatchedBy((Long) rs.getObject("dispatched_by")).receivedBy((Long) rs.getObject("received_by")).build();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class BookMapper implements RowMapper<ChequeBookData> {
|
||||
|
||||
String schema() {
|
||||
return " b.id as id, b.account_type as account_type, b.account_id as account_id, b.office_id as office_id, "
|
||||
+ "o.name as office_name, b.series_id as series_id, s.series_code as series_code, "
|
||||
+ "b.requested_date as requested_date, b.requested_by as requested_by, b.no_of_books as no_of_books, "
|
||||
+ "b.no_of_leaves_requested as no_of_leaves_requested, b.status as status, b.issue_date as issue_date, "
|
||||
+ "b.issued_by as issued_by, b.lot_id as lot_id, b.range_start as range_start, b.range_end as range_end, "
|
||||
+ "b.total_leaves as total_leaves, b.rejection_reason as rejection_reason, "
|
||||
+ "b.collection_acknowledged as collection_acknowledged, b.collected_date as collected_date, "
|
||||
+ "b.collected_by as collected_by "
|
||||
+ "from m_cheque_book b join m_office o on o.id = b.office_id left join m_cheque_series s on s.id = b.series_id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeBookData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
return ChequeBookData.builder().id(rs.getLong("id")).accountType(rs.getString("account_type"))
|
||||
.accountId(rs.getLong("account_id")).accountNo(null).clientName(null).officeId(rs.getLong("office_id"))
|
||||
.officeName(rs.getString("office_name")).seriesId((Long) rs.getObject("series_id"))
|
||||
.seriesCode(rs.getString("series_code")).requestedDate(rs.getObject("requested_date", LocalDate.class))
|
||||
.requestedBy((Long) rs.getObject("requested_by")).noOfBooks((Integer) rs.getObject("no_of_books"))
|
||||
.noOfLeavesRequested((Integer) rs.getObject("no_of_leaves_requested"))
|
||||
.status(rs.getString("status")).issueDate(rs.getObject("issue_date", LocalDate.class))
|
||||
.issuedBy((Long) rs.getObject("issued_by")).lotId((Long) rs.getObject("lot_id"))
|
||||
.rangeStart((Long) rs.getObject("range_start")).rangeEnd((Long) rs.getObject("range_end"))
|
||||
.totalLeaves((Integer) rs.getObject("total_leaves")).rejectionReason(rs.getString("rejection_reason"))
|
||||
.collectionAcknowledged(rs.getBoolean("collection_acknowledged"))
|
||||
.collectedDate(rs.getObject("collected_date", LocalDate.class)).collectedBy((Long) rs.getObject("collected_by"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LeafMapper implements RowMapper<ChequeLeafData> {
|
||||
|
||||
String schema() {
|
||||
return " f.id as id, f.cheque_book_id as cheque_book_id, f.lot_id as lot_id, s.series_code as series_code, "
|
||||
+ "f.leaf_serial_no as leaf_serial_no, "
|
||||
+ "f.leaf_seq_no as leaf_seq_no, f.status as status, f.stop_reason as stop_reason, f.stopped_by as stopped_by, "
|
||||
+ "f.stopped_on as stopped_on, f.lost_reason as lost_reason, f.lost_reported_by as lost_reported_by, "
|
||||
+ "f.lost_reported_on as lost_reported_on, f.cancelled_reason as cancelled_reason, "
|
||||
+ "f.cancelled_by as cancelled_by, f.cancelled_on as cancelled_on, "
|
||||
+ "f.used_reason as used_reason, f.used_by as used_by, f.used_on as used_on, "
|
||||
+ "b.account_type as account_type, b.account_id as account_id, bo.name as book_office_name, "
|
||||
+ "lo.name as lot_office_name "
|
||||
+ "from m_cheque_leaf f left join m_cheque_book b on b.id = f.cheque_book_id left join m_office bo on bo.id = b.office_id "
|
||||
+ "join m_cheque_stock_lot l on l.id = f.lot_id join m_office lo on lo.id = l.office_id "
|
||||
+ "join m_cheque_series s on s.id = l.series_id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeLeafData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
final String bookOfficeName = rs.getString("book_office_name");
|
||||
final String officeName = bookOfficeName != null ? bookOfficeName : rs.getString("lot_office_name");
|
||||
return ChequeLeafData.builder().id(rs.getLong("id")).chequeBookId((Long) rs.getObject("cheque_book_id"))
|
||||
.lotId(rs.getLong("lot_id")).seriesCode(rs.getString("series_code")).leafSerialNo(rs.getLong("leaf_serial_no"))
|
||||
.leafSeqNo((Integer) rs.getObject("leaf_seq_no")).status(rs.getString("status"))
|
||||
.stopReason(rs.getString("stop_reason")).stoppedBy((Long) rs.getObject("stopped_by"))
|
||||
.stoppedOn(rs.getObject("stopped_on", OffsetDateTime.class)).lostReason(rs.getString("lost_reason"))
|
||||
.lostReportedBy((Long) rs.getObject("lost_reported_by"))
|
||||
.lostReportedOn(rs.getObject("lost_reported_on", OffsetDateTime.class))
|
||||
.cancelledReason(rs.getString("cancelled_reason")).cancelledBy((Long) rs.getObject("cancelled_by"))
|
||||
.cancelledOn(rs.getObject("cancelled_on", OffsetDateTime.class)).usedReason(rs.getString("used_reason"))
|
||||
.usedBy((Long) rs.getObject("used_by")).usedOn(rs.getObject("used_on", OffsetDateTime.class))
|
||||
.accountType(rs.getString("account_type")).accountId((Long) rs.getObject("account_id")).accountNo(null)
|
||||
.clientName(null).officeName(officeName).build();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PendingRequestMapper implements RowMapper<ChequeBookPendingRequestData> {
|
||||
|
||||
String schema() {
|
||||
return " id, request_type as request_type, payload_json as payload_json, status as status, maker_id as maker_id, "
|
||||
+ "maker_name as maker_name, maker_username as maker_username, submitted_on as submitted_on, "
|
||||
+ "checker_id as checker_id, checker_name as checker_name, checker_username as checker_username, "
|
||||
+ "checked_on as checked_on, rejection_reason as rejection_reason, created_resource_id as created_resource_id, "
|
||||
+ "activity_log as activity_log from m_chequebook_pending_request";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChequeBookPendingRequestData mapRow(final ResultSet rs, final int rowNum) throws SQLException {
|
||||
return ChequeBookPendingRequestData.builder().id(rs.getLong("id")).requestType(rs.getString("request_type"))
|
||||
.payloadJson(rs.getString("payload_json")).status(rs.getString("status")).makerId(rs.getLong("maker_id"))
|
||||
.makerName(rs.getString("maker_name")).makerUsername(rs.getString("maker_username"))
|
||||
.submittedOn(rs.getObject("submitted_on", OffsetDateTime.class)).checkerId((Long) rs.getObject("checker_id"))
|
||||
.checkerName(rs.getString("checker_name")).checkerUsername(rs.getString("checker_username"))
|
||||
.checkedOn(rs.getObject("checked_on", OffsetDateTime.class)).rejectionReason(rs.getString("rejection_reason"))
|
||||
.createdResourceId((Long) rs.getObject("created_resource_id")).activityLog(rs.getString("activity_log")).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.chequebook.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface ChequeBookWritePlatformService {
|
||||
|
||||
CommandProcessingResult createSeries(JsonCommand command);
|
||||
|
||||
CommandProcessingResult updateSeries(Long id, JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteSeries(Long id);
|
||||
|
||||
/** HO/branch bulk receipt from the press — creates an AVAILABLE stock lot. */
|
||||
CommandProcessingResult receiveStock(JsonCommand command);
|
||||
|
||||
/** Reports the remaining undrawn suffix of a stock lot's range as lost/stolen before ever being issued. */
|
||||
CommandProcessingResult reportStockLotLost(Long lotId, JsonCommand command);
|
||||
|
||||
/** Dispatches a sub-range from an available lot to another office. */
|
||||
CommandProcessingResult dispatchStockTransfer(JsonCommand command);
|
||||
|
||||
CommandProcessingResult receiveStockTransfer(Long transferId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult rejectStockTransfer(Long transferId, JsonCommand command);
|
||||
|
||||
/** Customer/branch requisition — no stock drawn yet. */
|
||||
CommandProcessingResult requestChequeBook(JsonCommand command);
|
||||
|
||||
/** Draws a range from an available lot at the book's office and materializes the leaf rows. */
|
||||
CommandProcessingResult issueChequeBook(Long chequeBookId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult rejectChequeBook(Long chequeBookId, JsonCommand command);
|
||||
|
||||
/** Account closed — withdraws the book's remaining ACTIVE leaves. */
|
||||
CommandProcessingResult withdrawChequeBook(Long chequeBookId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult stopPayment(Long chequeBookId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult revokeStopPayment(Long chequeBookId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult reportChequeBookLost(Long chequeBookId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult cancelLeaf(Long chequeBookId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Manually records that leaves cleared, once a branch sees them on the bank/clearing statement — never
|
||||
* automatic, since this CBS has no clearing/presentment integration to detect it on its own.
|
||||
*/
|
||||
CommandProcessingResult markLeafUsed(Long chequeBookId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Checker approves a pending series/stock/book request — replays the maker's original submission and
|
||||
* actually creates the record. Fails if the checker is the same user who submitted it.
|
||||
*/
|
||||
CommandProcessingResult approvePendingRequest(Long pendingRequestId);
|
||||
|
||||
/** Checker rejects a pending series/stock/book request. Fails if the checker is the original maker. */
|
||||
CommandProcessingResult rejectPendingRequest(Long pendingRequestId, JsonCommand command);
|
||||
}
|
||||
+767
@@ -0,0 +1,767 @@
|
||||
/**
|
||||
* 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.chequebook.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.GeneralPlatformDomainRuleException;
|
||||
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.organisation.office.domain.Office;
|
||||
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.chequebook.api.ChequeBookApiJsonInputParams;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeAccountType;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeBook;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeBookPendingRequest;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeBookPendingRequestRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeBookPendingRequestType;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeBookRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeLeaf;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeLeafRepository;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeLeafStatus;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeSeries;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeSeriesApplicability;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeSeriesRepository;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeSeriesRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeStockLot;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeStockLotRepository;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeStockLotRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeStockLotStatus;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeStockTransfer;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeStockTransferRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.chequebook.serialization.ChequeBookCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountFacilityConfig;
|
||||
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfig;
|
||||
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfig;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountSubStatusEnum;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfig;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ChequeBookWritePlatformServiceImpl implements ChequeBookWritePlatformService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ChequeBookCommandFromApiJsonDeserializer validator;
|
||||
private final ChequeSeriesRepository chequeSeriesRepository;
|
||||
private final ChequeSeriesRepositoryWrapper chequeSeriesRepositoryWrapper;
|
||||
private final ChequeStockLotRepository chequeStockLotRepository;
|
||||
private final ChequeStockLotRepositoryWrapper chequeStockLotRepositoryWrapper;
|
||||
private final ChequeStockTransferRepositoryWrapper chequeStockTransferRepositoryWrapper;
|
||||
private final ChequeBookRepositoryWrapper chequeBookRepositoryWrapper;
|
||||
private final ChequeLeafRepository chequeLeafRepository;
|
||||
private final OfficeRepositoryWrapper officeRepositoryWrapper;
|
||||
private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper;
|
||||
private final CurrentAccountRepositoryWrapper currentAccountRepositoryWrapper;
|
||||
private final CurrentProductFacilityConfigRepository currentProductFacilityConfigRepository;
|
||||
private final ChequeBookPendingRequestRepositoryWrapper chequeBookPendingRequestRepositoryWrapper;
|
||||
private final ChequeSeriesHardDeleteAttempter chequeSeriesHardDeleteAttempter;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
// ============================= Series =============================
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult createSeries(final JsonCommand command) {
|
||||
this.validator.validateForCreateSeries(command.json());
|
||||
final String seriesCode = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_CODE.getValue());
|
||||
if (this.chequeSeriesRepository.existsBySeriesCode(seriesCode)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequeseries.duplicate.code",
|
||||
"A cheque series with code " + seriesCode + " already exists.", seriesCode);
|
||||
}
|
||||
final Long pendingId = submitPendingRequest(ChequeBookPendingRequestType.CREATE_SERIES, command);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(pendingId).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs only once a checker approves the pending request — replays the maker's original submission.
|
||||
* {@code makerId} is the original submitter, not whichever checker triggers this replay.
|
||||
*/
|
||||
private Long finalizeCreateSeries(final JsonCommand command, final Long makerId) {
|
||||
final String seriesCode = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_CODE.getValue());
|
||||
if (this.chequeSeriesRepository.existsBySeriesCode(seriesCode)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequeseries.duplicate.code",
|
||||
"A cheque series with code " + seriesCode + " already exists.", seriesCode);
|
||||
}
|
||||
final String name = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.NAME.getValue());
|
||||
final Integer leavesPerBook = command
|
||||
.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.LEAVES_PER_BOOK.getValue());
|
||||
final ChequeSeriesApplicability applicableAccountType = ChequeSeriesApplicability
|
||||
.fromString(command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.APPLICABLE_ACCOUNT_TYPE.getValue()));
|
||||
final Boolean ctsCompliant = command.booleanObjectValueOfParameterNamed(ChequeBookApiJsonInputParams.CTS_COMPLIANT.getValue());
|
||||
final Integer reorderLevel = command
|
||||
.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.REORDER_LEVEL.getValue());
|
||||
|
||||
final ChequeSeries series = ChequeSeries.createNew(seriesCode, name, leavesPerBook, applicableAccountType,
|
||||
ctsCompliant == null || ctsCompliant, reorderLevel, makerId);
|
||||
final ChequeSeries saved = this.chequeSeriesRepositoryWrapper.saveAndFlush(series);
|
||||
return saved.getId();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult updateSeries(final Long id, final JsonCommand command) {
|
||||
this.validator.validateForUpdateSeries(command.json());
|
||||
final ChequeSeries series = this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(id);
|
||||
final String name = command.stringValueOfParameterNamedAllowingNull(ChequeBookApiJsonInputParams.NAME.getValue());
|
||||
final Integer leavesPerBook = command
|
||||
.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.LEAVES_PER_BOOK.getValue());
|
||||
final ChequeSeriesApplicability applicableAccountType = ChequeSeriesApplicability
|
||||
.fromString(command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.APPLICABLE_ACCOUNT_TYPE.getValue()));
|
||||
final Boolean ctsCompliant = command.booleanObjectValueOfParameterNamed(ChequeBookApiJsonInputParams.CTS_COMPLIANT.getValue());
|
||||
final Integer reorderLevel = command
|
||||
.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.REORDER_LEVEL.getValue());
|
||||
series.updateDetails(name, leavesPerBook, applicableAccountType, ctsCompliant, reorderLevel);
|
||||
this.chequeSeriesRepositoryWrapper.saveAndFlush(series);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(id).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult deleteSeries(final Long id) {
|
||||
this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(id); // fail fast for the maker if it doesn't exist
|
||||
final JsonObject payload = new JsonObject();
|
||||
payload.addProperty(ChequeBookApiJsonInputParams.SERIES_ID.getValue(), id);
|
||||
final Long pendingId = insertPendingRequest(ChequeBookPendingRequestType.DELETE_SERIES, this.gson.toJson(payload));
|
||||
return new CommandProcessingResultBuilder().withEntityId(pendingId).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs only once a checker approves the pending request. Permanently removes the series if the
|
||||
* database's own foreign-key constraints allow it (nothing ever referenced it); otherwise falls back to
|
||||
* the same deactivate used previously, preserving every stock lot/book/leaf already tied to this series.
|
||||
*/
|
||||
private Long finalizeDeleteSeries(final Long seriesId) {
|
||||
final boolean hardDeleted = this.chequeSeriesHardDeleteAttempter.attemptDelete(seriesId);
|
||||
if (!hardDeleted) {
|
||||
final ChequeSeries series = this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(seriesId);
|
||||
series.deactivate();
|
||||
this.chequeSeriesRepositoryWrapper.saveAndFlush(series);
|
||||
}
|
||||
return seriesId;
|
||||
}
|
||||
|
||||
// ============================= Stock =============================
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult receiveStock(final JsonCommand command) {
|
||||
this.validator.validateForStockLotReceive(command.json());
|
||||
final Long seriesId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue());
|
||||
final Long rangeStart = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.RANGE_START.getValue());
|
||||
final Long rangeEnd = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.RANGE_END.getValue());
|
||||
// fail fast for the maker; re-checked authoritatively at approval time in case stock changed meanwhile
|
||||
if (this.chequeStockLotRepository.countOverlappingInSeries(seriesId, rangeStart, rangeEnd) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.range.overlap",
|
||||
"The given leaf-serial range overlaps with an existing stock lot for this series.", rangeStart);
|
||||
}
|
||||
final Long pendingId = submitPendingRequest(ChequeBookPendingRequestType.RECEIVE_STOCK, command);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(pendingId).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs only once a checker approves the pending request — replays the maker's original submission.
|
||||
* {@code makerId} is the original submitter, not whichever checker triggers this replay.
|
||||
*/
|
||||
private Long finalizeReceiveStock(final JsonCommand command, final Long makerId) {
|
||||
final Long seriesId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue());
|
||||
final Long officeId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.OFFICE_ID.getValue());
|
||||
final Long rangeStart = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.RANGE_START.getValue());
|
||||
final Long rangeEnd = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.RANGE_END.getValue());
|
||||
final String batchRef = command.stringValueOfParameterNamedAllowingNull(ChequeBookApiJsonInputParams.BATCH_REF.getValue());
|
||||
|
||||
if (this.chequeStockLotRepository.countOverlappingInSeries(seriesId, rangeStart, rangeEnd) > 0) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.range.overlap",
|
||||
"The given leaf-serial range overlaps with an existing stock lot for this series.", rangeStart);
|
||||
}
|
||||
|
||||
final ChequeSeries series = this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(seriesId);
|
||||
final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
||||
|
||||
final ChequeStockLot lot = ChequeStockLot.createReceipt(series, office, rangeStart, rangeEnd, batchRef, makerId);
|
||||
final ChequeStockLot saved = this.chequeStockLotRepositoryWrapper.saveAndFlush(lot);
|
||||
log.info("Received cheque stock lot {} ({} - {}) for series {} at office {}", saved.getId(), rangeStart, rangeEnd, seriesId,
|
||||
officeId);
|
||||
return saved.getId();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult reportStockLotLost(final Long lotId, final JsonCommand command) {
|
||||
this.validator.validateForStockLotReportLost(command.json());
|
||||
final ChequeStockLot lot = this.chequeStockLotRepositoryWrapper.findOneWithNotFoundDetection(lotId);
|
||||
final Long fromSerialNo = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.FROM_SERIAL_NO.getValue());
|
||||
final String reason = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.REASON.getValue());
|
||||
final Long reporterId = this.context.authenticatedUser().getId();
|
||||
|
||||
final long originalRangeEnd = lot.reportRemainingLost(fromSerialNo);
|
||||
this.chequeStockLotRepositoryWrapper.saveAndFlush(lot);
|
||||
|
||||
final List<ChequeLeaf> lostLeaves = new ArrayList<>();
|
||||
for (long serial = fromSerialNo; serial <= originalRangeEnd; serial++) {
|
||||
lostLeaves.add(ChequeLeaf.lostUnissued(lot, serial, reason, reporterId));
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(lostLeaves);
|
||||
log.info("Reported {} unissued cheque leaves lost/stolen from stock lot {}", lostLeaves.size(), lotId);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(lotId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult dispatchStockTransfer(final JsonCommand command) {
|
||||
this.validator.validateForStockTransferDispatch(command.json());
|
||||
final Long seriesId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue());
|
||||
final Long fromOfficeId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.FROM_OFFICE_ID.getValue());
|
||||
final Long toOfficeId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.TO_OFFICE_ID.getValue());
|
||||
final Long fromLotId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.FROM_LOT_ID.getValue());
|
||||
final Integer noOfLeaves = command.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.NO_OF_LEAVES.getValue());
|
||||
|
||||
final ChequeSeries series = this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(seriesId);
|
||||
final Office fromOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(fromOfficeId);
|
||||
final Office toOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(toOfficeId);
|
||||
final ChequeStockLot fromLot = this.chequeStockLotRepositoryWrapper.findOneWithNotFoundDetection(fromLotId);
|
||||
|
||||
if (!fromLot.getOffice().getId().equals(fromOfficeId)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.office.mismatch",
|
||||
"Stock lot " + fromLotId + " is not custodied at the given fromOfficeId.", fromLotId);
|
||||
}
|
||||
if (!fromLot.getSeries().getId().equals(seriesId)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequestocklot.series.mismatch",
|
||||
"Stock lot " + fromLotId + " does not belong to the given series.", fromLotId);
|
||||
}
|
||||
|
||||
final long[] range = fromLot.drawDown(noOfLeaves);
|
||||
this.chequeStockLotRepositoryWrapper.saveAndFlush(fromLot);
|
||||
|
||||
final ChequeStockLot toLot = ChequeStockLot.createTransferIn(series, toOffice, range[0], range[1], null);
|
||||
final ChequeStockLot savedToLot = this.chequeStockLotRepositoryWrapper.saveAndFlush(toLot);
|
||||
|
||||
final Long dispatchedBy = this.context.authenticatedUser().getId();
|
||||
final ChequeStockTransfer transfer = ChequeStockTransfer.createNew(series, fromOffice, toOffice, fromLot, savedToLot, range[0],
|
||||
range[1], noOfLeaves, DateUtils.getBusinessLocalDate(), dispatchedBy);
|
||||
final ChequeStockTransfer saved = this.chequeStockTransferRepositoryWrapper.saveAndFlush(transfer);
|
||||
log.info("Dispatched cheque stock transfer {} ({} leaves, {} - {}) from office {} to office {}", saved.getId(), noOfLeaves,
|
||||
range[0], range[1], fromOfficeId, toOfficeId);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId()).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult receiveStockTransfer(final Long transferId, final JsonCommand command) {
|
||||
final ChequeStockTransfer transfer = this.chequeStockTransferRepositoryWrapper.findOneWithNotFoundDetection(transferId);
|
||||
final Long receivedBy = this.context.authenticatedUser().getId();
|
||||
transfer.markReceived(receivedBy, DateUtils.getBusinessLocalDate());
|
||||
this.chequeStockTransferRepositoryWrapper.saveAndFlush(transfer);
|
||||
transfer.getToLot().markAvailable(receivedBy);
|
||||
this.chequeStockLotRepositoryWrapper.saveAndFlush(transfer.getToLot());
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(transferId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectStockTransfer(final Long transferId, final JsonCommand command) {
|
||||
final ChequeStockTransfer transfer = this.chequeStockTransferRepositoryWrapper.findOneWithNotFoundDetection(transferId);
|
||||
final Long rejectedBy = this.context.authenticatedUser().getId();
|
||||
transfer.reject(rejectedBy, DateUtils.getBusinessLocalDate());
|
||||
this.chequeStockTransferRepositoryWrapper.saveAndFlush(transfer);
|
||||
transfer.getToLot().cancel();
|
||||
this.chequeStockLotRepositoryWrapper.saveAndFlush(transfer.getToLot());
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(transferId).build();
|
||||
}
|
||||
|
||||
// ============================= Cheque books =============================
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult requestChequeBook(final JsonCommand command) {
|
||||
this.validator.validateForBookRequest(command.json());
|
||||
final ChequeAccountType accountType = ChequeAccountType
|
||||
.fromString(command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.ACCOUNT_TYPE.getValue()));
|
||||
final Long accountId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.ACCOUNT_ID.getValue());
|
||||
final Long seriesId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue());
|
||||
// fail fast for the maker; re-checked authoritatively at approval time in case anything changed meanwhile
|
||||
assertEligible(accountType, accountId);
|
||||
final ChequeSeries series = this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(seriesId);
|
||||
if (!series.appliesTo(accountType)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.series.account.type.mismatch",
|
||||
"Cheque series " + seriesId + " is not applicable to a " + accountType + " account.", seriesId);
|
||||
}
|
||||
final Long pendingId = submitPendingRequest(ChequeBookPendingRequestType.REQUEST_BOOK, command);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(pendingId).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs only once a checker approves the pending request — replays the maker's original submission.
|
||||
* {@code makerId} is the original submitter, not whichever checker triggers this replay.
|
||||
*
|
||||
* Requesting and issuing happen together here — the checker's approval IS the authorization to hand
|
||||
* out the book, so there is no separate "Issue" click left for anyone to do afterward. The book is
|
||||
* created and immediately drawn down from available stock in the same step.
|
||||
*/
|
||||
private Long finalizeRequestChequeBook(final JsonCommand command, final Long makerId) {
|
||||
final ChequeAccountType accountType = ChequeAccountType
|
||||
.fromString(command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.ACCOUNT_TYPE.getValue()));
|
||||
final Long accountId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.ACCOUNT_ID.getValue());
|
||||
final Long officeId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.OFFICE_ID.getValue());
|
||||
final Integer noOfBooks = command.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.NO_OF_BOOKS.getValue());
|
||||
final Long seriesId = command.longValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue());
|
||||
|
||||
assertEligible(accountType, accountId);
|
||||
final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
|
||||
final ChequeSeries series = this.chequeSeriesRepositoryWrapper.findOneWithNotFoundDetection(seriesId);
|
||||
// the denomination requested must be meant for this account's scheme (SB vs CA)
|
||||
if (!series.appliesTo(accountType)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.series.account.type.mismatch",
|
||||
"Cheque series " + seriesId + " is not applicable to a " + accountType + " account.", seriesId);
|
||||
}
|
||||
|
||||
final ChequeBook book = ChequeBook.requestNew(accountType, accountId, office, series, noOfBooks, makerId);
|
||||
final ChequeBook saved = this.chequeBookRepositoryWrapper.saveAndFlush(book);
|
||||
|
||||
// leaves actually handed to the customer = requested books x this series' fixed denomination
|
||||
final int totalLeaves = noOfBooks * series.getLeavesPerBook();
|
||||
final List<ChequeStockLot> candidateLots = this.chequeStockLotRepository
|
||||
.findBySeriesIdAndOfficeIdAndStatusOrderByIdAsc(seriesId, officeId, ChequeStockLotStatus.AVAILABLE.name());
|
||||
ChequeStockLot chosenLot = null;
|
||||
for (final ChequeStockLot candidate : candidateLots) {
|
||||
if (candidate.remainingLeaves() >= totalLeaves) {
|
||||
chosenLot = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (chosenLot == null) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.insufficient.stock",
|
||||
"No cheque stock lot at this office has enough remaining leaves (" + totalLeaves + " needed) for series "
|
||||
+ seriesId + ".",
|
||||
seriesId);
|
||||
}
|
||||
|
||||
final long[] range = chosenLot.drawDown(totalLeaves);
|
||||
this.chequeStockLotRepositoryWrapper.saveAndFlush(chosenLot);
|
||||
// the checker approving right now is the one authorizing the physical issue, not the original maker
|
||||
final Long checkerId = this.context.authenticatedUser().getId();
|
||||
saved.issue(chosenLot, range[0], range[1], totalLeaves, checkerId);
|
||||
this.chequeBookRepositoryWrapper.saveAndFlush(saved);
|
||||
|
||||
final List<ChequeLeaf> leaves = new ArrayList<>();
|
||||
int seqNo = 1;
|
||||
for (long serial = range[0]; serial <= range[1]; serial++) {
|
||||
leaves.add(ChequeLeaf.issueForBook(saved, chosenLot, serial, seqNo++));
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
log.info("Cheque book {} requested and issued together on approval ({} leaves, {} - {}) for {} account {}", saved.getId(),
|
||||
leaves.size(), range[0], range[1], accountType, accountId);
|
||||
return saved.getId();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult issueChequeBook(final Long chequeBookId, final JsonCommand command) {
|
||||
this.validator.validateForBookIssue(command.json());
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
// the denomination was already fixed at request time — matches a real requisition slip
|
||||
final ChequeSeries series = book.getSeries();
|
||||
if (series == null) {
|
||||
// a request created before series-selection-at-request-time was introduced has no series on file
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.series.not.set",
|
||||
"Cheque book " + chequeBookId + " has no series recorded against it. Reject this request and submit "
|
||||
+ "a new one, which will capture the series at requisition time.",
|
||||
chequeBookId);
|
||||
}
|
||||
final Long seriesId = series.getId();
|
||||
|
||||
// re-validate eligibility at issue time — time may have passed since the request was recorded
|
||||
final ChequeAccountType accountType = ChequeAccountType.fromString(book.getAccountType());
|
||||
assertEligible(accountType, book.getAccountId());
|
||||
|
||||
// leaves actually handed to the customer = requested books x this series' fixed denomination
|
||||
final int totalLeaves = book.getNoOfBooks() * series.getLeavesPerBook();
|
||||
|
||||
final List<ChequeStockLot> candidateLots = this.chequeStockLotRepository.findBySeriesIdAndOfficeIdAndStatusOrderByIdAsc(seriesId,
|
||||
book.getOffice().getId(), ChequeStockLotStatus.AVAILABLE.name());
|
||||
ChequeStockLot chosenLot = null;
|
||||
for (final ChequeStockLot candidate : candidateLots) {
|
||||
if (candidate.remainingLeaves() >= totalLeaves) {
|
||||
chosenLot = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (chosenLot == null) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.insufficient.stock",
|
||||
"No cheque stock lot at this office has enough remaining leaves (" + totalLeaves + " needed) for series "
|
||||
+ seriesId + ".",
|
||||
seriesId);
|
||||
}
|
||||
|
||||
final long[] range = chosenLot.drawDown(totalLeaves);
|
||||
this.chequeStockLotRepositoryWrapper.saveAndFlush(chosenLot);
|
||||
|
||||
final Long issuedBy = this.context.authenticatedUser().getId();
|
||||
book.issue(chosenLot, range[0], range[1], totalLeaves, issuedBy);
|
||||
|
||||
final Boolean collectionAcknowledged = command
|
||||
.booleanObjectValueOfParameterNamed(ChequeBookApiJsonInputParams.COLLECTION_ACKNOWLEDGED.getValue());
|
||||
if (Boolean.TRUE.equals(collectionAcknowledged)) {
|
||||
book.acknowledgeCollection(issuedBy);
|
||||
}
|
||||
this.chequeBookRepositoryWrapper.saveAndFlush(book);
|
||||
|
||||
final List<ChequeLeaf> leaves = new ArrayList<>();
|
||||
int seqNo = 1;
|
||||
for (long serial = range[0]; serial <= range[1]; serial++) {
|
||||
leaves.add(ChequeLeaf.issueForBook(book, chosenLot, serial, seqNo++));
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
log.info("Issued cheque book {} ({} leaves, {} - {}) against {} account {}", chequeBookId, leaves.size(), range[0], range[1],
|
||||
book.getAccountType(), book.getAccountId());
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectChequeBook(final Long chequeBookId, final JsonCommand command) {
|
||||
this.validator.validateForBookReject(command.json());
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
final String reason = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.REJECTION_REASON.getValue());
|
||||
book.reject(reason);
|
||||
this.chequeBookRepositoryWrapper.saveAndFlush(book);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult withdrawChequeBook(final Long chequeBookId, final JsonCommand command) {
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
book.withdraw();
|
||||
this.chequeBookRepositoryWrapper.saveAndFlush(book);
|
||||
|
||||
final List<ChequeLeaf> leaves = this.chequeLeafRepository.findByChequeBookIdOrderByLeafSeqNoAsc(chequeBookId);
|
||||
final List<ChequeLeaf> toWithdraw = new ArrayList<>();
|
||||
for (final ChequeLeaf leaf : leaves) {
|
||||
if (ChequeLeafStatus.ACTIVE.name().equals(leaf.getStatus())) {
|
||||
leaf.withdraw();
|
||||
toWithdraw.add(leaf);
|
||||
}
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(toWithdraw);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult stopPayment(final Long chequeBookId, final JsonCommand command) {
|
||||
this.validator.validateForBookLeafAction(command.json(), true);
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
final String reason = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.REASON.getValue());
|
||||
final Long actorId = this.context.authenticatedUser().getId();
|
||||
final List<ChequeLeaf> leaves = resolveLeavesInRange(book, command);
|
||||
for (final ChequeLeaf leaf : leaves) {
|
||||
leaf.stopPayment(reason, actorId);
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult revokeStopPayment(final Long chequeBookId, final JsonCommand command) {
|
||||
this.validator.validateForBookLeafAction(command.json(), false);
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
final List<ChequeLeaf> leaves = resolveLeavesInRange(book, command);
|
||||
for (final ChequeLeaf leaf : leaves) {
|
||||
leaf.revokeStopPayment();
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult reportChequeBookLost(final Long chequeBookId, final JsonCommand command) {
|
||||
this.validator.validateForBookLeafAction(command.json(), true);
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
final String reason = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.REASON.getValue());
|
||||
final Long reporterId = this.context.authenticatedUser().getId();
|
||||
final List<ChequeLeaf> leaves = resolveLeavesInRange(book, command);
|
||||
for (final ChequeLeaf leaf : leaves) {
|
||||
leaf.reportLost(reason, reporterId);
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult cancelLeaf(final Long chequeBookId, final JsonCommand command) {
|
||||
this.validator.validateForBookLeafAction(command.json(), true);
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
final String reason = command.stringValueOfParameterNamed(ChequeBookApiJsonInputParams.REASON.getValue());
|
||||
final Long actorId = this.context.authenticatedUser().getId();
|
||||
final List<ChequeLeaf> leaves = resolveLeavesInRange(book, command);
|
||||
for (final ChequeLeaf leaf : leaves) {
|
||||
leaf.cancel(reason, actorId);
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult markLeafUsed(final Long chequeBookId, final JsonCommand command) {
|
||||
// informational record-keeping, not a fraud control — a note is welcome but not required
|
||||
this.validator.validateForBookLeafAction(command.json(), false);
|
||||
final ChequeBook book = this.chequeBookRepositoryWrapper.findOneWithNotFoundDetection(chequeBookId);
|
||||
final String reason = command.stringValueOfParameterNamedAllowingNull(ChequeBookApiJsonInputParams.REASON.getValue());
|
||||
final Long actorId = this.context.authenticatedUser().getId();
|
||||
final List<ChequeLeaf> leaves = resolveLeavesInRange(book, command);
|
||||
for (final ChequeLeaf leaf : leaves) {
|
||||
leaf.markUsed(reason, actorId);
|
||||
}
|
||||
this.chequeLeafRepository.saveAll(leaves);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(chequeBookId).build();
|
||||
}
|
||||
|
||||
// ============================= Pending requests (bespoke maker-checker) =============================
|
||||
//
|
||||
// Fineract's native command-approval engine holds nothing for a user whose role carries ALL_FUNCTIONS
|
||||
// (Super user, Clerk in this tenant) - it bypasses maker-checker unconditionally for them, which made it
|
||||
// a no-op for this CBS's actual admin accounts. These three actions instead go through this bespoke
|
||||
// table (mirrors m_clerk_disbursement_request): submit stores the raw request JSON as PENDING; approval
|
||||
// replays that JSON through the same finalize* method that used to run immediately.
|
||||
|
||||
private Long submitPendingRequest(final ChequeBookPendingRequestType type, final JsonCommand command) {
|
||||
return insertPendingRequest(type, command.json());
|
||||
}
|
||||
|
||||
private Long insertPendingRequest(final ChequeBookPendingRequestType type, final String payloadJson) {
|
||||
final AppUser maker = this.context.authenticatedUser();
|
||||
final String activityLog = appendActivityLog(null, "SUBMITTED", maker, null);
|
||||
final ChequeBookPendingRequest request = ChequeBookPendingRequest.submit(type, payloadJson, maker, activityLog);
|
||||
final ChequeBookPendingRequest saved = this.chequeBookPendingRequestRepositoryWrapper.saveAndFlush(request);
|
||||
log.info("Cheque book pending request {} ({}) submitted by {}", saved.getId(), type, maker.getUsername());
|
||||
return saved.getId();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approvePendingRequest(final Long pendingRequestId) {
|
||||
final ChequeBookPendingRequest request = this.chequeBookPendingRequestRepositoryWrapper
|
||||
.findOneWithNotFoundDetection(pendingRequestId);
|
||||
if (!request.isPending()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebookpendingrequest.not.pending",
|
||||
"Cheque book pending request " + pendingRequestId + " has already been actioned.", pendingRequestId);
|
||||
}
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
if (request.isMadeBy(checker.getId())) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebookpendingrequest.self.approval",
|
||||
"You cannot approve a cheque book request you submitted yourself — a different user must approve it.",
|
||||
pendingRequestId);
|
||||
}
|
||||
|
||||
final JsonCommand replay = reconstructCommand(request.getPayloadJson());
|
||||
final ChequeBookPendingRequestType type = ChequeBookPendingRequestType.valueOf(request.getRequestType());
|
||||
final Long createdResourceId = switch (type) {
|
||||
case CREATE_SERIES -> finalizeCreateSeries(replay, request.getMakerId());
|
||||
case RECEIVE_STOCK -> finalizeReceiveStock(replay, request.getMakerId());
|
||||
case REQUEST_BOOK -> finalizeRequestChequeBook(replay, request.getMakerId());
|
||||
case DELETE_SERIES ->
|
||||
finalizeDeleteSeries(replay.longValueOfParameterNamed(ChequeBookApiJsonInputParams.SERIES_ID.getValue()));
|
||||
};
|
||||
|
||||
final String activityLog = appendActivityLog(request.getActivityLog(), "APPROVED", checker, null);
|
||||
request.approve(checker, createdResourceId, activityLog);
|
||||
this.chequeBookPendingRequestRepositoryWrapper.saveAndFlush(request);
|
||||
log.info("Cheque book pending request {} approved by {}, created resource {}", pendingRequestId, checker.getUsername(),
|
||||
createdResourceId);
|
||||
return new CommandProcessingResultBuilder().withEntityId(createdResourceId).build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectPendingRequest(final Long pendingRequestId, final JsonCommand command) {
|
||||
final ChequeBookPendingRequest request = this.chequeBookPendingRequestRepositoryWrapper
|
||||
.findOneWithNotFoundDetection(pendingRequestId);
|
||||
if (!request.isPending()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebookpendingrequest.not.pending",
|
||||
"Cheque book pending request " + pendingRequestId + " has already been actioned.", pendingRequestId);
|
||||
}
|
||||
final AppUser checker = this.context.authenticatedUser();
|
||||
if (request.isMadeBy(checker.getId())) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebookpendingrequest.self.approval",
|
||||
"You cannot reject a cheque book request you submitted yourself — a different user must action it.",
|
||||
pendingRequestId);
|
||||
}
|
||||
final String reason = command.stringValueOfParameterNamedAllowingNull(ChequeBookApiJsonInputParams.REASON.getValue());
|
||||
if (reason == null || reason.isBlank()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebookpendingrequest.reject.reason.required",
|
||||
"A reason is required to reject a cheque book pending request.", pendingRequestId);
|
||||
}
|
||||
|
||||
final String activityLog = appendActivityLog(request.getActivityLog(), "REJECTED", checker, reason);
|
||||
request.reject(checker, reason, activityLog);
|
||||
this.chequeBookPendingRequestRepositoryWrapper.saveAndFlush(request);
|
||||
log.info("Cheque book pending request {} rejected by {}", pendingRequestId, checker.getUsername());
|
||||
return new CommandProcessingResultBuilder().withEntityId(pendingRequestId).build();
|
||||
}
|
||||
|
||||
/** Reconstructs a usable {@link JsonCommand} from a pending request's stored raw JSON, for replay at approval. */
|
||||
private JsonCommand reconstructCommand(final String json) {
|
||||
// entityName, resourceId, subresourceId, groupId, clientId, loanId, savingsId, transactionId, url,
|
||||
// productId, creditBureauId, organisationCreditBureauId, jobName, loanExternalId - none apply here
|
||||
return JsonCommand.from(json, this.fromApiJsonHelper.parse(json), this.fromApiJsonHelper, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private String appendActivityLog(final String existingJson, final String action, final AppUser user, final String note) {
|
||||
JsonArray log = new JsonArray();
|
||||
if (existingJson != null && !existingJson.isBlank()) {
|
||||
try {
|
||||
log = this.fromApiJsonHelper.parse(existingJson).getAsJsonArray();
|
||||
} catch (final Exception e) {
|
||||
log = new JsonArray();
|
||||
}
|
||||
}
|
||||
final JsonObject entry = new JsonObject();
|
||||
entry.addProperty("timestamp", Instant.now().toString());
|
||||
entry.addProperty("action", action);
|
||||
entry.addProperty("user", user.getUsername());
|
||||
if (note != null && !note.isBlank()) {
|
||||
entry.addProperty("note", note);
|
||||
}
|
||||
log.add(entry);
|
||||
return this.gson.toJson(log);
|
||||
}
|
||||
|
||||
// ============================= Helpers =============================
|
||||
|
||||
private List<ChequeLeaf> resolveLeavesInRange(final ChequeBook book, final JsonCommand command) {
|
||||
if (!book.isIssued()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.not.issued",
|
||||
"Cheque book " + book.getId() + " has not been issued.", book.getId());
|
||||
}
|
||||
final Integer fromSeq = command.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.FROM_LEAF_SEQ.getValue());
|
||||
final Integer toSeq = command.integerValueSansLocaleOfParameterNamed(ChequeBookApiJsonInputParams.TO_LEAF_SEQ.getValue());
|
||||
final List<ChequeLeaf> leaves;
|
||||
if (fromSeq == null && toSeq == null) {
|
||||
leaves = this.chequeLeafRepository.findByChequeBookIdOrderByLeafSeqNoAsc(book.getId());
|
||||
} else {
|
||||
final int from = fromSeq != null ? fromSeq : 1;
|
||||
final int to = toSeq != null ? toSeq : book.getTotalLeaves();
|
||||
leaves = this.chequeLeafRepository.findByChequeBookIdAndLeafSeqNoBetweenOrderByLeafSeqNoAsc(book.getId(), from, to);
|
||||
}
|
||||
if (leaves.isEmpty()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.no.leaves.in.range",
|
||||
"No leaves found in the given range for cheque book " + book.getId() + ".", book.getId());
|
||||
}
|
||||
return leaves;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the account (Savings or Current — not interchangeable in this codebase), checks it is active
|
||||
* and not dormant/blocked, and checks the {@code chequeBook} facility flag (account-level config,
|
||||
* falling back to product-level config when the account-level config is absent).
|
||||
*/
|
||||
private void assertEligible(final ChequeAccountType accountType, final Long accountId) {
|
||||
if (accountType == ChequeAccountType.SAVINGS) {
|
||||
assertSavingsEligible(accountId);
|
||||
} else if (accountType == ChequeAccountType.CURRENT) {
|
||||
assertCurrentEligible(accountId);
|
||||
} else {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.account.type.invalid", "Unknown account type.",
|
||||
accountId);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSavingsEligible(final Long accountId) {
|
||||
final SavingsAccount account = this.savingsAccountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
|
||||
if (!account.isActive()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.account.not.active",
|
||||
"Savings account " + accountId + " is not active.", accountId);
|
||||
}
|
||||
final SavingsAccountSubStatusEnum subStatus = SavingsAccountSubStatusEnum.fromInt(account.getSubStatus());
|
||||
if (subStatus != null && (subStatus.isSubStatusDormant() || subStatus.isSubStatusAccountBlocked()
|
||||
|| subStatus.isSubStatusDebitBlocked())) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.account.blocked",
|
||||
"Savings account " + accountId + " is dormant or blocked.", accountId);
|
||||
}
|
||||
Boolean chequeBookFlag = null;
|
||||
final SavingsAccountFacilityConfig accountConfig = account.getFacilityConfig();
|
||||
if (accountConfig != null) {
|
||||
chequeBookFlag = accountConfig.getChequeBook();
|
||||
} else if (account.savingsProduct() != null) {
|
||||
final SavingsProductFacilityConfig productConfig = account.savingsProduct().getFacilityConfig();
|
||||
if (productConfig != null) {
|
||||
chequeBookFlag = productConfig.getChequeBook();
|
||||
}
|
||||
}
|
||||
if (!Boolean.TRUE.equals(chequeBookFlag)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.facility.not.enabled",
|
||||
"The cheque book facility is not enabled for savings account " + accountId + ".", accountId);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCurrentEligible(final Long accountId) {
|
||||
final CurrentAccount account = this.currentAccountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
|
||||
if (!account.isActive()) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.account.not.active",
|
||||
"Current account " + accountId + " is not active.", accountId);
|
||||
}
|
||||
final Integer subStatusEnum = account.getSubStatusEnum();
|
||||
// 200=Dormant, 300=Block
|
||||
if (subStatusEnum != null && (subStatusEnum == 200 || subStatusEnum == 300)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.account.blocked",
|
||||
"Current account " + accountId + " is dormant or blocked.", accountId);
|
||||
}
|
||||
Boolean chequeBookFlag = null;
|
||||
final CurrentAccountFacilityConfig accountConfig = account.getFacilityConfig();
|
||||
if (accountConfig != null) {
|
||||
chequeBookFlag = accountConfig.isChequeBook();
|
||||
} else {
|
||||
final CurrentProductFacilityConfig productConfig = this.currentProductFacilityConfigRepository
|
||||
.findByCurrentProductId(account.getProductId());
|
||||
if (productConfig != null) {
|
||||
chequeBookFlag = productConfig.getChequeBook();
|
||||
}
|
||||
}
|
||||
if (!Boolean.TRUE.equals(chequeBookFlag)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.chequebook.facility.not.enabled",
|
||||
"The cheque book facility is not enabled for current account " + accountId + ".", accountId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.chequebook.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.portfolio.chequebook.domain.ChequeSeriesRepository;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Whether a cheque series can be permanently removed is decided ATOMICALLY by the database's own foreign
|
||||
* key constraints (stock lots/transfers/books referencing it), not by a separate "count references first"
|
||||
* check — a count-then-act approach has a race: something could start referencing the series in the gap
|
||||
* between the check and the delete (e.g. a concurrently-approved "receive stock" request). Attempting the
|
||||
* real delete and catching a constraint violation makes the check and the action the same atomic step.
|
||||
*
|
||||
* This attempt runs in its own transaction (REQUIRES_NEW) so a rejection here never poisons the caller's
|
||||
* transaction — Postgres marks a transaction aborted after a constraint violation, so the caller's
|
||||
* subsequent deactivate-fallback would otherwise never be allowed to run.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ChequeSeriesHardDeleteAttempter {
|
||||
|
||||
private final ChequeSeriesRepository chequeSeriesRepository;
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public boolean attemptDelete(final Long seriesId) {
|
||||
try {
|
||||
this.chequeSeriesRepository.deleteById(seriesId);
|
||||
this.chequeSeriesRepository.flush();
|
||||
return true;
|
||||
} catch (final DataIntegrityViolationException e) {
|
||||
log.info("Cheque series {} still has stock lots/transfers/books referencing it — deactivating instead of deleting",
|
||||
seriesId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:Keshav@898966}
|
||||
fineract.tenant.password=${FINERACT_DEFAULT_TENANTDB_PWD:313863}
|
||||
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:Keshav@898966}
|
||||
spring.datasource.hikari.password=${FINERACT_HIKARI_PASSWORD:313863}
|
||||
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}
|
||||
@@ -689,4 +689,4 @@ fineract.email.from-name=${FINERACT_EMAIL_FROM_NAME:CBSnexora}
|
||||
fineract.email.support-address=${FINERACT_EMAIL_SUPPORT_ADDRESS:support@cbsnexora.com}
|
||||
|
||||
# Frontend base URL — used for email links (voting, etc.)
|
||||
cbs.frontend.base-url=${CBS_FRONTEND_BASE_URL:http://localhost:4200}
|
||||
cbs.frontend.base-url=${CBS_FRONTEND_BASE_URL:https://cbs.godemo.in}
|
||||
|
||||
@@ -399,4 +399,13 @@
|
||||
<include file="parts/0340_add_terminate_true_to_savings_and_current_account.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0368_catchup_savings_account_columns.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0369_catchup_saving_product_temp_table.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0379_chequebook_module.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0380_cheque_series_account_type.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0381_chequebook_no_of_books.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0382_chequeleaf_used_status.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0383_cheque_series_no_both.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0384_chequebook_maker_checker.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0385_chequebook_pending_requests.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0386_enable_global_maker_checker.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0387_chequeleaf_serial_unique_per_lot.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Cheque Book Inventory module: stationery master, a unified stock ledger (HO receipt and branch
|
||||
transfer-in are the same "range of leaf serials sitting at an office, with a drawdown cursor"
|
||||
concept), issuance to an account (Savings or Current — both exist in this codebase and are not
|
||||
interchangeable, so a discriminator column is used instead of a JPA relation), and per-leaf
|
||||
tracking (materialized at issuance, OR directly against a stock lot when unissued stock is
|
||||
reported lost/stolen — see m_cheque_leaf.cheque_book_id being nullable).
|
||||
|
||||
Scope is deliberately limited to inventory + stop-payment/hotlisting. It does not model RBI's
|
||||
Positive Pay System, "used/cleared" leaf status, or automatic cheque-validity staleness — all three
|
||||
require clearing/presentment integration this CBS does not have; see the design plan for why
|
||||
faking any of them would be actively wrong rather than merely incomplete.
|
||||
|
||||
Every changeSet is guarded so this migration is a safe no-op where already applied.
|
||||
-->
|
||||
|
||||
<changeSet id="0379-cheque-series" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cheque_series"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_cheque_series">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="series_code" type="VARCHAR(20)">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="uk_cheque_series_code"/>
|
||||
</column>
|
||||
<column name="name" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="leaves_per_book" type="INTEGER">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="cts_compliant" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="reorder_level" type="INTEGER"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0379-cheque-stock-lot" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cheque_stock_lot"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_cheque_stock_lot">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="series_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_lot_series" references="m_cheque_series(id)"/>
|
||||
</column>
|
||||
<column name="office_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_lot_office" references="m_office(id)"/>
|
||||
</column>
|
||||
<column name="range_start" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="range_end" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="next_serial_no" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="source" type="VARCHAR(20)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="source_ref_id" type="BIGINT"/>
|
||||
<column name="batch_ref" type="VARCHAR(100)"/>
|
||||
<column name="status" type="VARCHAR(25)" defaultValue="AVAILABLE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="received_by" type="BIGINT"/>
|
||||
<column name="received_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="version" type="INTEGER" defaultValueNumeric="0"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0379-cheque-stock-transfer" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cheque_stock_transfer"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_cheque_stock_transfer">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="series_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_transfer_series" references="m_cheque_series(id)"/>
|
||||
</column>
|
||||
<column name="from_office_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_transfer_from_office" references="m_office(id)"/>
|
||||
</column>
|
||||
<column name="to_office_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_transfer_to_office" references="m_office(id)"/>
|
||||
</column>
|
||||
<column name="from_lot_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_transfer_from_lot" references="m_cheque_stock_lot(id)"/>
|
||||
</column>
|
||||
<column name="to_lot_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_stock_transfer_to_lot" references="m_cheque_stock_lot(id)"/>
|
||||
</column>
|
||||
<column name="range_start" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="range_end" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="total_leaves" type="INTEGER">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="dispatch_date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="status" type="VARCHAR(20)" defaultValue="DISPATCHED">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="received_date" type="DATE"/>
|
||||
<column name="dispatched_by" type="BIGINT"/>
|
||||
<column name="received_by" type="BIGINT"/>
|
||||
<column name="created_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0379-cheque-book" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cheque_book"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_cheque_book">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="account_type" type="VARCHAR(10)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="account_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="office_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_book_office" references="m_office(id)"/>
|
||||
</column>
|
||||
<column name="series_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_cheque_book_series" references="m_cheque_series(id)"/>
|
||||
</column>
|
||||
<column name="requested_date" type="DATE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="requested_by" type="BIGINT"/>
|
||||
<column name="no_of_leaves_requested" type="INTEGER">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="status" type="VARCHAR(20)" defaultValue="REQUESTED">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="issue_date" type="DATE"/>
|
||||
<column name="issued_by" type="BIGINT"/>
|
||||
<column name="lot_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_cheque_book_lot" references="m_cheque_stock_lot(id)"/>
|
||||
</column>
|
||||
<column name="range_start" type="BIGINT"/>
|
||||
<column name="range_end" type="BIGINT"/>
|
||||
<column name="total_leaves" type="INTEGER"/>
|
||||
<column name="rejection_reason" type="VARCHAR(500)"/>
|
||||
<column name="collection_acknowledged" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="collected_date" type="DATE"/>
|
||||
<column name="collected_by" type="BIGINT"/>
|
||||
<column name="created_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="version" type="INTEGER" defaultValueNumeric="0"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0379-cheque-leaf" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cheque_leaf"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_cheque_leaf">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="cheque_book_id" type="BIGINT">
|
||||
<constraints nullable="true" foreignKeyName="fk_cheque_leaf_book" references="m_cheque_book(id)"/>
|
||||
</column>
|
||||
<column name="lot_id" type="BIGINT">
|
||||
<constraints nullable="false" foreignKeyName="fk_cheque_leaf_lot" references="m_cheque_stock_lot(id)"/>
|
||||
</column>
|
||||
<column name="leaf_serial_no" type="BIGINT">
|
||||
<constraints nullable="false" unique="true" uniqueConstraintName="uk_cheque_leaf_serial_no"/>
|
||||
</column>
|
||||
<column name="leaf_seq_no" type="INTEGER"/>
|
||||
<column name="status" type="VARCHAR(20)" defaultValue="ACTIVE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="stop_reason" type="VARCHAR(500)"/>
|
||||
<column name="stopped_by" type="BIGINT"/>
|
||||
<column name="stopped_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="lost_reason" type="VARCHAR(500)"/>
|
||||
<column name="lost_reported_by" type="BIGINT"/>
|
||||
<column name="lost_reported_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="cancelled_reason" type="VARCHAR(500)"/>
|
||||
<column name="cancelled_by" type="BIGINT"/>
|
||||
<column name="cancelled_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="created_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0379-permissions" author="CBS" runOnChange="false">
|
||||
<sql>
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'READ_CHEQUESERIES', 'CHEQUESERIES', 'READ', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'READ_CHEQUESERIES');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_CHEQUESERIES', 'CHEQUESERIES', 'CREATE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CHEQUESERIES');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'UPDATE_CHEQUESERIES', 'CHEQUESERIES', 'UPDATE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'UPDATE_CHEQUESERIES');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'DELETE_CHEQUESERIES', 'CHEQUESERIES', 'DELETE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'DELETE_CHEQUESERIES');
|
||||
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'READ_CHEQUESTOCKLOT', 'CHEQUESTOCKLOT', 'READ', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'READ_CHEQUESTOCKLOT');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_CHEQUESTOCKLOT', 'CHEQUESTOCKLOT', 'CREATE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CHEQUESTOCKLOT');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'REPORTLOST_CHEQUESTOCKLOT', 'CHEQUESTOCKLOT', 'REPORTLOST', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REPORTLOST_CHEQUESTOCKLOT');
|
||||
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_CHEQUESTOCKTRANSFER', 'CHEQUESTOCKTRANSFER', 'CREATE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CHEQUESTOCKTRANSFER');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'RECEIVE_CHEQUESTOCKTRANSFER', 'CHEQUESTOCKTRANSFER', 'RECEIVE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'RECEIVE_CHEQUESTOCKTRANSFER');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'REJECT_CHEQUESTOCKTRANSFER', 'CHEQUESTOCKTRANSFER', 'REJECT', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REJECT_CHEQUESTOCKTRANSFER');
|
||||
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'READ_CHEQUEBOOK', 'CHEQUEBOOK', 'READ', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'READ_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CREATE_CHEQUEBOOK', 'CHEQUEBOOK', 'CREATE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CREATE_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'ISSUE_CHEQUEBOOK', 'CHEQUEBOOK', 'ISSUE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'ISSUE_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'REJECT_CHEQUEBOOK', 'CHEQUEBOOK', 'REJECT', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REJECT_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'WITHDRAW_CHEQUEBOOK', 'CHEQUEBOOK', 'WITHDRAW', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'WITHDRAW_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'CANCELLEAF_CHEQUEBOOK', 'CHEQUEBOOK', 'CANCELLEAF', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'CANCELLEAF_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'STOPPAYMENT_CHEQUEBOOK', 'CHEQUEBOOK', 'STOPPAYMENT', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'STOPPAYMENT_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'REVOKESTOPPAYMENT_CHEQUEBOOK', 'CHEQUEBOOK', 'REVOKESTOPPAYMENT', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REVOKESTOPPAYMENT_CHEQUEBOOK');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'REPORTLOST_CHEQUEBOOK', 'CHEQUEBOOK', 'REPORTLOST', true
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REPORTLOST_CHEQUEBOOK');
|
||||
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'heading_menu', 'MENU_CHEQUEBOOK', 'MENU', 'CHEQUEBOOK', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'MENU_CHEQUEBOOK');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Segregate cheque stationery by account scheme (Savings vs Current). Real banks stock physically
|
||||
different cheque books for SB and CA accounts, so each series is tagged SAVINGS / CURRENT / BOTH.
|
||||
Existing rows default to BOTH (backward compatible: any series stays issuable to any account type
|
||||
until an operator narrows it). Issuance is validated against this at issue time.
|
||||
-->
|
||||
|
||||
<changeSet id="0380-cheque-series-applicable-account-type" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_cheque_series" columnName="applicable_account_type"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_cheque_series">
|
||||
<column name="applicable_account_type" type="VARCHAR(10)" defaultValue="BOTH">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Cheque books are requested and issued in whole BOOKS of a fixed denomination (25-leaf, 50-leaf, ...),
|
||||
not in an arbitrary leaf count. A requisition captures how many books the customer wants; the actual
|
||||
leaves handed over are derived at issue time as books x series.leavesPerBook, and that leaf count plus
|
||||
the per-leaf serials are persisted. no_of_leaves_requested becomes optional/legacy.
|
||||
-->
|
||||
|
||||
<changeSet id="0381-cheque-book-no-of-books" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_cheque_book" columnName="no_of_books"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_cheque_book">
|
||||
<column name="no_of_books" type="INTEGER" defaultValueNumeric="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0381-cheque-book-leaves-requested-optional" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<columnExists tableName="m_cheque_book" columnName="no_of_leaves_requested"/>
|
||||
</preConditions>
|
||||
<dropNotNullConstraint tableName="m_cheque_book" columnName="no_of_leaves_requested" columnDataType="INTEGER"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
A manually-recorded USED status for a cheque leaf. This CBS has no clearing/presentment integration,
|
||||
so the system cannot detect on its own when a specific leaf was honoured — a branch marks it USED
|
||||
after seeing it clear on the bank/clearing statement (the same manual-recording pattern already used
|
||||
for the settlement module's "actual bank settlement" entry, where automatic detection isn't feasible
|
||||
either). This is a record-keeping status, not a fraud control, so it is not maker-checker.
|
||||
-->
|
||||
|
||||
<changeSet id="0382-cheque-leaf-used-columns" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_cheque_leaf" columnName="used_reason"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_cheque_leaf">
|
||||
<column name="used_reason" type="VARCHAR(500)"/>
|
||||
<column name="used_by" type="BIGINT"/>
|
||||
<column name="used_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0382-permission" author="CBS" runOnChange="false">
|
||||
<sql>
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'MARKUSED_CHEQUEBOOK', 'CHEQUEBOOK', 'MARKUSED', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'MARKUSED_CHEQUEBOOK');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
"BOTH" is removed as a cheque series applicability: every series must now be tagged explicitly as
|
||||
SAVINGS or CURRENT (real banks stock physically different stationery per scheme, so "applies to
|
||||
everything" was never a genuine stationery category). Any pre-existing BOTH-tagged row is narrowed
|
||||
to SAVINGS — an operator can re-create a CURRENT-tagged series if that stock is also needed there.
|
||||
-->
|
||||
|
||||
<changeSet id="0383-cheque-series-no-both-data" author="CBS" runOnChange="false">
|
||||
<sql>
|
||||
UPDATE m_cheque_series SET applicable_account_type = 'SAVINGS' WHERE applicable_account_type = 'BOTH';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0383-cheque-series-no-both-default" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<columnExists tableName="m_cheque_series" columnName="applicable_account_type"/>
|
||||
</preConditions>
|
||||
<dropDefaultValue tableName="m_cheque_series" columnName="applicable_account_type"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Extend maker-checker to the three cheque-book creation actions (series master, stock receipt, book
|
||||
requisition) so every request now needs a second user's approval before it takes effect, matching
|
||||
the fraud-sensitive actions (stop-payment/report-lost/revoke) that already required it. No bespoke
|
||||
approval logic is needed - can_maker_checker=true on the permission row is all the generic
|
||||
command-processing pipeline needs; pending requests surface in the existing Checker Inbox screen.
|
||||
-->
|
||||
|
||||
<changeSet id="0384-chequebook-maker-checker" author="CBS" runOnChange="false">
|
||||
<sql>
|
||||
UPDATE m_permission SET can_maker_checker = true
|
||||
WHERE code IN ('CREATE_CHEQUESERIES', 'CREATE_CHEQUESTOCKLOT', 'CREATE_CHEQUEBOOK');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Bespoke maker-checker for the three cheque-book creation actions (series master, stock receipt, book
|
||||
requisition), mirroring the existing m_clerk_disbursement_request pattern rather than the native
|
||||
Fineract command-approval engine: the native engine's maker-checker hold is bypassed by any user
|
||||
whose role carries ALL_FUNCTIONS (Super user, Clerk in this tenant), which made it useless for this
|
||||
CBS's actual admin accounts. This table instead holds a request as PENDING with its raw request JSON;
|
||||
approval replays that JSON through the same creation logic used elsewhere in this module. The
|
||||
submitting user (maker) can never approve/reject their own request (enforced in code, not just by
|
||||
role) - any OTHER user with the same create permission can act as checker.
|
||||
-->
|
||||
|
||||
<changeSet id="0385-chequebook-pending-requests-table" author="CBS">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_chequebook_pending_request"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_chequebook_pending_request">
|
||||
<column name="id" type="BIGSERIAL">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="request_type" type="VARCHAR(30)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="payload_json" type="TEXT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="status" type="VARCHAR(20)" defaultValue="PENDING">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="maker_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="maker_name" type="VARCHAR(150)"/>
|
||||
<column name="maker_username" type="VARCHAR(100)"/>
|
||||
<column name="submitted_on" type="TIMESTAMP WITH TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="checker_id" type="BIGINT"/>
|
||||
<column name="checker_name" type="VARCHAR(150)"/>
|
||||
<column name="checker_username" type="VARCHAR(100)"/>
|
||||
<column name="checked_on" type="TIMESTAMP WITH TIME ZONE"/>
|
||||
<column name="rejection_reason" type="VARCHAR(500)"/>
|
||||
<column name="created_resource_id" type="BIGINT"/>
|
||||
<column name="activity_log" type="TEXT"/>
|
||||
</createTable>
|
||||
<sql>CREATE INDEX IF NOT EXISTS idx_cbpr_status ON m_chequebook_pending_request(status)</sql>
|
||||
<sql>CREATE INDEX IF NOT EXISTS idx_cbpr_maker_id ON m_chequebook_pending_request(maker_id)</sql>
|
||||
<sql>CREATE INDEX IF NOT EXISTS idx_cbpr_submitted_on ON m_chequebook_pending_request(submitted_on)</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0385-chequebook-revert-native-maker-checker" author="CBS" runOnChange="false">
|
||||
<!-- the native engine's hold is replaced by this table's bespoke workflow for these three actions -->
|
||||
<sql>
|
||||
UPDATE m_permission SET can_maker_checker = false
|
||||
WHERE code IN ('CREATE_CHEQUESERIES', 'CREATE_CHEQUESTOCKLOT', 'CREATE_CHEQUEBOOK');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0385-chequebook-pending-request-permission" author="CBS" runOnChange="false">
|
||||
<sql>
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'READ_CHEQUEBOOKPENDINGREQUEST', 'CHEQUEBOOKPENDINGREQUEST', 'READ', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'READ_CHEQUEBOOKPENDINGREQUEST');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'APPROVE_CHEQUEBOOKPENDINGREQUEST', 'CHEQUEBOOKPENDINGREQUEST', 'APPROVE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'APPROVE_CHEQUEBOOKPENDINGREQUEST');
|
||||
INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker)
|
||||
SELECT 'portfolio', 'REJECT_CHEQUEBOOKPENDINGREQUEST', 'CHEQUEBOOKPENDINGREQUEST', 'REJECT', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_permission WHERE code = 'REJECT_CHEQUEBOOKPENDINGREQUEST');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
Fineract holds a per-permission can_maker_checker flag AND a single global "maker-checker" switch in
|
||||
c_configuration - the global switch is checked FIRST (ConfigurationDomainServiceJpa.isMakerCheckerEnabledForTask),
|
||||
and if it is disabled, every maker-checker-eligible permission in the whole system is bypassed
|
||||
regardless of its own flag. It shipped disabled in this tenant, which meant the fraud-sensitive
|
||||
actions built earlier (stop-payment/report-lost/revoke) were never actually holding for approval
|
||||
either. Enabling it here turns on real maker-checker enforcement platform-wide, not just for the
|
||||
cheque-book module. NB: a user whose role carries ALL_FUNCTIONS or CHECKER_SUPER_USER (Super user,
|
||||
Clerk in this tenant) still bypasses the hold regardless of this switch - see AppUser.isCheckerSuperUser().
|
||||
-->
|
||||
|
||||
<changeSet id="0386-enable-global-maker-checker" author="CBS" runOnChange="false">
|
||||
<sql>
|
||||
UPDATE c_configuration SET enabled = true WHERE name = 'maker-checker';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?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">
|
||||
|
||||
<!--
|
||||
leaf_serial_no was originally unique bank-wide, which matched the OLD global stock-numbering
|
||||
design. Per-series numbering (0383) intentionally lets two different series share a serial number
|
||||
(each is a separate stationery order from the press, numbered independently) - so a bare
|
||||
leaf_serial_no can no longer be unique on its own once real leaves are materialized for two series
|
||||
that happen to overlap. Uniqueness now needs to be scoped to the stock lot a leaf was drawn from
|
||||
(every lot belongs to exactly one series, and lots within one series never overlap), not the whole
|
||||
table.
|
||||
-->
|
||||
|
||||
<changeSet id="0387-chequeleaf-serial-unique-per-lot" author="CBS" runOnChange="false">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<indexExists indexName="uk_cheque_leaf_serial_no"/>
|
||||
</preConditions>
|
||||
<dropUniqueConstraint tableName="m_cheque_leaf" constraintName="uk_cheque_leaf_serial_no"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0387-chequeleaf-serial-unique-per-lot-add" author="CBS" runOnChange="false">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<indexExists indexName="uk_cheque_leaf_lot_serial_no"/>
|
||||
</not>
|
||||
</preConditions>
|
||||
<addUniqueConstraint tableName="m_cheque_leaf" columnNames="lot_id, leaf_serial_no"
|
||||
constraintName="uk_cheque_leaf_lot_serial_no"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user