Add extended fields to ClientNonPersonDetails: authorizedSignatory, additionalInfo, dateOfRegistration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
90c21d40c1
commit
87e132f93d
+3
-1
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.infrastructure.core.service;
|
||||
|
||||
import java.util.Properties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData;
|
||||
import org.apache.fineract.infrastructure.configuration.service.ExternalServicesPropertiesReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.domain.EmailDetail;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class GmailBackedPlatformEmailService implements PlatformEmailService {
|
||||
|
||||
@@ -90,7 +92,7 @@ public class GmailBackedPlatformEmailService implements PlatformEmailService {
|
||||
mailSender.send(message);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new PlatformEmailSendException(e);
|
||||
log.warn("Email sending failed for address [{}]. Skipping. Error: {}", emailDetails.getAddress(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -22,6 +22,7 @@ import com.google.gson.JsonObject;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
@@ -33,6 +34,7 @@ import java.util.Set;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientAddress;
|
||||
|
||||
@Entity
|
||||
@@ -47,6 +49,14 @@ public class Address extends AbstractPersistableCustom<Long> {
|
||||
@OneToMany(mappedBy = "address", cascade = CascadeType.ALL)
|
||||
private Set<ClientAddress> clientaddress;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "client_id")
|
||||
private Client client;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "address_type_id")
|
||||
private CodeValue addressType;
|
||||
|
||||
@Column(name = "street")
|
||||
private String street;
|
||||
|
||||
@@ -377,4 +387,20 @@ public class Address extends AbstractPersistableCustom<Long> {
|
||||
this.updatedOn = updatedOn;
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
public void setClient(Client client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public CodeValue getAddressType() {
|
||||
return this.addressType;
|
||||
}
|
||||
|
||||
public void setAddressType(CodeValue addressType) {
|
||||
this.addressType = addressType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
@@ -63,6 +63,8 @@ public class AddressWritePlatformServiceImpl implements AddressWritePlatformServ
|
||||
final Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final Address address = createAddress(jsonObject);
|
||||
address.setClient(client);
|
||||
address.setAddressType(addressTypeIdCodeValue);
|
||||
addressRepository.save(address);
|
||||
|
||||
final ClientAddress clientAddress = createClientAddress(client, jsonObject, addressTypeIdCodeValue, address);
|
||||
@@ -86,6 +88,8 @@ public class AddressWritePlatformServiceImpl implements AddressWritePlatformServ
|
||||
final CodeValue addressTypeIdCodeValue = codeValueRepository.getReferenceById(addressTypeId);
|
||||
|
||||
final Address address = createAddress(jsonObject);
|
||||
address.setClient(client);
|
||||
address.setAddressType(addressTypeIdCodeValue);
|
||||
addressRepository.save(address);
|
||||
|
||||
clientAddress = createClientAddress(client, jsonObject, addressTypeIdCodeValue, address);
|
||||
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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.client.api;
|
||||
|
||||
import static org.apache.fineract.util.StreamResponseUtil.DISPOSITION_TYPE_ATTACHMENT;
|
||||
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
|
||||
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
|
||||
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.GET;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.command.core.CommandPipeline;
|
||||
import org.apache.fineract.infrastructure.contentstore.detector.ContentDetectorContext;
|
||||
import org.apache.fineract.infrastructure.contentstore.detector.ContentDetectorManager;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.api.FileUploadValidator;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.command.DocumentCreateCommand;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.command.DocumentDeleteCommand;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.data.DocumentCreateRequest;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.data.DocumentCreateResponse;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.data.DocumentData;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.data.DocumentDeleteRequest;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.data.DocumentDeleteResponse;
|
||||
import org.apache.fineract.infrastructure.documentmanagement.service.DocumentReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.util.StreamResponseUtil;
|
||||
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* REST API for managing documents attached to family members.
|
||||
* Delegates to Fineract's generic document infrastructure (m_document table)
|
||||
* using entityType = "family_members".
|
||||
*/
|
||||
@Path("/v1/clients/{clientId}/familymembers/{familyMemberId}/documents")
|
||||
@Component
|
||||
@Tag(name = "Client Family Member Documents", description = "Upload, list, download and delete documents for family members (e.g. marriage certificates)")
|
||||
@RequiredArgsConstructor
|
||||
public class ClientFamilyMemberDocumentApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "FAMILYMEMBERDOCUMENT";
|
||||
private static final String ENTITY_TYPE = "family_members";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final DocumentReadPlatformService documentReadPlatformService;
|
||||
private final FileUploadValidator fileUploadValidator;
|
||||
private final ContentDetectorManager contentDetectorManager;
|
||||
private final CommandPipeline commandPipeline;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List all documents for a family member")
|
||||
public List<DocumentData> retrieveAllDocuments(
|
||||
@PathParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@PathParam("familyMemberId") @Parameter(description = "familyMemberId") final Long familyMemberId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
return documentReadPlatformService.retrieveAllDocuments(ENTITY_TYPE, familyMemberId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{documentId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a specific document for a family member")
|
||||
public DocumentData getDocument(
|
||||
@PathParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@PathParam("familyMemberId") @Parameter(description = "familyMemberId") final Long familyMemberId,
|
||||
@PathParam("documentId") @Parameter(description = "documentId") final Long documentId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
return documentReadPlatformService.retrieveDocument(ENTITY_TYPE, familyMemberId, documentId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{documentId}/attachment")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_OCTET_STREAM })
|
||||
@Operation(summary = "Download the file associated with a family member document")
|
||||
public Response downloadFile(
|
||||
@PathParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@PathParam("familyMemberId") @Parameter(description = "familyMemberId") final Long familyMemberId,
|
||||
@PathParam("documentId") @Parameter(description = "documentId") final Long documentId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final var content = documentReadPlatformService.retrieveDocumentContent(ENTITY_TYPE, familyMemberId, documentId);
|
||||
|
||||
return StreamResponseUtil
|
||||
.ok(StreamResponseUtil.StreamResponseData.builder().type(content.getContentType()).fileName(content.getFileName())
|
||||
.dispositionType(DISPOSITION_TYPE_ATTACHMENT).stream(content.getStream()).size(content.getSize()).build());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Upload a document for a family member", description = """
|
||||
Upload a document (e.g. marriage certificate) for a family member using multipart form data.
|
||||
|
||||
Body parts:
|
||||
- name: name or summary of the document
|
||||
- description: description of the document
|
||||
- file: the file to be uploaded
|
||||
""")
|
||||
public DocumentCreateResponse createDocument(
|
||||
@PathParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@PathParam("familyMemberId") @Parameter(description = "familyMemberId") final Long familyMemberId,
|
||||
@HeaderParam(CONTENT_LENGTH) final Long fileSize,
|
||||
@FormDataParam("file") final InputStream is,
|
||||
@FormDataParam("file") final FormDataContentDisposition fileDetails,
|
||||
@FormDataParam("file") final FormDataBodyPart filePart,
|
||||
@FormDataParam("name") final String name,
|
||||
@FormDataParam("description") final String description) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
fileUploadValidator.validate(fileSize, is, fileDetails, filePart);
|
||||
|
||||
final var command = new DocumentCreateCommand();
|
||||
|
||||
var type = Optional.ofNullable(filePart.getMediaType()).map(MediaType::toString)
|
||||
.or(() -> Optional.of(contentDetectorManager
|
||||
.detect(ContentDetectorContext.builder().fileName(fileDetails.getFileName()).build()).getMimeType()))
|
||||
.orElse(APPLICATION_OCTET_STREAM_VALUE);
|
||||
|
||||
command.setPayload(DocumentCreateRequest.builder()
|
||||
.entityId(familyMemberId)
|
||||
.entityType(ENTITY_TYPE)
|
||||
.name(name)
|
||||
.description(description)
|
||||
.fileName(fileDetails.getFileName())
|
||||
.size(fileSize)
|
||||
.type(type)
|
||||
.stream(is)
|
||||
.build());
|
||||
|
||||
final Supplier<DocumentCreateResponse> response = commandPipeline.send(command);
|
||||
return response.get();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{documentId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete a family member document")
|
||||
public DocumentDeleteResponse deleteDocument(
|
||||
@PathParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@PathParam("familyMemberId") @Parameter(description = "familyMemberId") final Long familyMemberId,
|
||||
@PathParam("documentId") @Parameter(description = "documentId") final Long documentId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasDeletePermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
|
||||
final var command = new DocumentDeleteCommand();
|
||||
command.setPayload(DocumentDeleteRequest.builder()
|
||||
.id(documentId)
|
||||
.entityId(familyMemberId)
|
||||
.entityType(ENTITY_TYPE)
|
||||
.build());
|
||||
|
||||
final Supplier<DocumentDeleteResponse> response = commandPipeline.send(command);
|
||||
return response.get();
|
||||
}
|
||||
}
|
||||
+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.client.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.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.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.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.data.ClientForm60Data;
|
||||
import org.apache.fineract.portfolio.client.service.ClientForm60ReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/clients/{clientId}/form60")
|
||||
@Component
|
||||
@Tag(name = "Client Form 60", description = "Form 60 declaration as per Rule 114B of Income Tax Rules, 1962. "
|
||||
+ "Filed by individuals who do not have a PAN for specified financial transactions.")
|
||||
@RequiredArgsConstructor
|
||||
public class ClientForm60ApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME = "CLIENTFORM60";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ClientForm60ReadPlatformService readPlatformService;
|
||||
private final DefaultToApiJsonSerializer<ClientForm60Data> toApiJsonSerializer;
|
||||
private final DefaultToApiJsonSerializer<List> listSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Submit Form 60 for a Client",
|
||||
description = "Creates a new Form 60 declaration for the given client.")
|
||||
public String createForm60(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.createClientForm60(clientId)
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve all Form 60 declarations for a Client",
|
||||
description = "Returns all Form 60 records for the given client.")
|
||||
public String retrieveAllForm60(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
|
||||
final List<ClientForm60Data> data = readPlatformService.retrieveAllByClientId(clientId);
|
||||
return listSerializer.serialize(data);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{form60Id}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a specific Form 60 by ID",
|
||||
description = "Returns the Form 60 record identified by {form60Id} for the given client.")
|
||||
public String retrieveForm60(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@PathParam("form60Id") @Parameter(description = "Form 60 record identifier") final Long form60Id) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
|
||||
final ClientForm60Data data = readPlatformService.retrieveOne(clientId, form60Id);
|
||||
return toApiJsonSerializer.serialize(data);
|
||||
}
|
||||
}
|
||||
+138
-10
@@ -34,6 +34,10 @@ import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
@@ -43,10 +47,12 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.data.ClientKycApiConstants;
|
||||
import org.apache.fineract.portfolio.client.data.ClientKycData;
|
||||
import org.apache.fineract.portfolio.client.data.ClientKycRequest;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.service.ClientKycReadPlatformService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.fineract.portfolio.client.data.ClientKycRequest;
|
||||
import org.apache.fineract.portfolio.client.service.ClientKycReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -80,6 +86,7 @@ public class ClientKycApiResource {
|
||||
private final ClientKycReadPlatformService readPlatformService;
|
||||
private final DefaultToApiJsonSerializer<ClientKycData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// INITIATE KYC – POST /v1/clients/{clientId}/kyc
|
||||
@@ -103,16 +110,131 @@ public class ClientKycApiResource {
|
||||
@ApiResponse(responseCode = "409", description = "KYC already exists for this client")
|
||||
public CommandProcessingResult initiateKyc(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final ClientKycRequest clientKycRequest) {
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
LOG.info("KYC INITIATE received: clientId={}, requestBody={}", clientId, apiRequestBodyAsJson);
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.initiateClientKyc(clientId)
|
||||
.withJson(toApiJsonSerializer.serialize(clientKycRequest))
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// TEMPLATE – GET /v1/clients/{clientId}/kyc/template
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve KYC document template for a Client",
|
||||
description = "Returns the list of required KYC documents based on the client's type "
|
||||
+ "(clientTypeId code value). Used by the UI to populate the KYC document dropdown.")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveKycTemplate(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
|
||||
final Client client = clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final Map<String, Object> template = new LinkedHashMap<>();
|
||||
template.put("clientId", clientId);
|
||||
template.put("clientDisplayName", client.getDisplayName());
|
||||
|
||||
// Resolve client type label from code value
|
||||
final String clientTypeName = client.clientType() != null ? client.clientType().getLabel() : null;
|
||||
final Integer clientTypeId = client.clientType() != null ? client.clientType().getId().intValue() : null;
|
||||
final Integer legalFormId = client.getLegalForm();
|
||||
template.put("clientTypeId", clientTypeId);
|
||||
template.put("clientTypeName", clientTypeName);
|
||||
template.put("legalFormId", legalFormId);
|
||||
|
||||
template.put("requiredDocuments", getRequiredDocuments(clientTypeName, legalFormId));
|
||||
|
||||
return toApiJsonSerializer.serialize(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns required KYC documents based on client type (RBI Master Direction on KYC).
|
||||
*/
|
||||
private List<Map<String, Object>> getRequiredDocuments(final String clientTypeName, final Integer legalFormId) {
|
||||
final List<Map<String, Object>> docs = new ArrayList<>();
|
||||
|
||||
// Individual types (legalFormId=1)
|
||||
if (legalFormId != null && legalFormId == 1) {
|
||||
docs.add(doc("Aadhaar Card", "AADHAAR", true));
|
||||
docs.add(doc("PAN Card", "PAN", true));
|
||||
docs.add(doc("Voter ID", "VOTER_ID", false));
|
||||
docs.add(doc("Driving License", "DRIVING_LICENSE", false));
|
||||
docs.add(doc("Passport", "PASSPORT", false));
|
||||
docs.add(doc("NREGA Job Card", "NREGA", false));
|
||||
return docs;
|
||||
}
|
||||
|
||||
// Non-individual types (legalFormId=2) – based on clientTypeName
|
||||
if (clientTypeName == null) {
|
||||
return docs;
|
||||
}
|
||||
|
||||
final String type = clientTypeName.toUpperCase().trim();
|
||||
|
||||
if (type.contains("PROPRIETORSHIP")) {
|
||||
docs.add(doc("PAN Card of Proprietor", "PAN", true));
|
||||
docs.add(doc("GST Registration Certificate", "GST_CERT", true));
|
||||
docs.add(doc("Shop & Establishment License", "SHOP_LICENSE", false));
|
||||
docs.add(doc("Udyam Registration Certificate", "UDYAM", false));
|
||||
docs.add(doc("Proprietor KYC - Aadhaar", "AADHAAR", true));
|
||||
} else if (type.contains("PARTNERSHIP") || type.contains("LLP")) {
|
||||
docs.add(doc("Registration Certificate", "REG_CERT", true));
|
||||
docs.add(doc("Partnership Deed", "PARTNERSHIP_DEED", true));
|
||||
docs.add(doc("PAN Card of Firm", "PAN", true));
|
||||
docs.add(doc("KYC of Authorized Signatories", "SIGNATORY_KYC", true));
|
||||
} else if (type.contains("PRIVATE") || type.contains("PUBLIC") || type.contains("CORPORATE")) {
|
||||
docs.add(doc("Certificate of Incorporation", "COI", true));
|
||||
docs.add(doc("Memorandum of Association", "MOA", true));
|
||||
docs.add(doc("Articles of Association", "AOA", true));
|
||||
docs.add(doc("PAN Card of Company", "PAN", true));
|
||||
docs.add(doc("Board Resolution", "BOARD_RESOLUTION", true));
|
||||
docs.add(doc("KYC of Authorized Signatories", "SIGNATORY_KYC", true));
|
||||
} else if (type.contains("TRUST")) {
|
||||
docs.add(doc("Registration Certificate", "REG_CERT", true));
|
||||
docs.add(doc("Trust Deed", "TRUST_DEED", true));
|
||||
docs.add(doc("PAN Card of Trust", "PAN", true));
|
||||
docs.add(doc("KYC of Trustees / Authorized Signatories", "SIGNATORY_KYC", true));
|
||||
} else if (type.contains("SOCIETY")) {
|
||||
docs.add(doc("Registration Certificate", "REG_CERT", true));
|
||||
docs.add(doc("PAN Card of Society", "PAN", true));
|
||||
docs.add(doc("KYC of Authorized Signatories", "SIGNATORY_KYC", true));
|
||||
} else if (type.contains("GOVERNMENT")) {
|
||||
docs.add(doc("Government Order / Notification", "GOVT_ORDER", true));
|
||||
docs.add(doc("PAN Card", "PAN", true));
|
||||
docs.add(doc("KYC of Authorized Signatories", "SIGNATORY_KYC", true));
|
||||
} else if (type.contains("MSME")) {
|
||||
docs.add(doc("Udyam Registration Certificate", "UDYAM", true));
|
||||
docs.add(doc("PAN Card", "PAN", true));
|
||||
docs.add(doc("GST Registration Certificate", "GST_CERT", false));
|
||||
docs.add(doc("KYC of Authorized Signatories", "SIGNATORY_KYC", true));
|
||||
} else {
|
||||
// Default non-individual
|
||||
docs.add(doc("PAN Card", "PAN", true));
|
||||
docs.add(doc("Registration Certificate", "REG_CERT", false));
|
||||
docs.add(doc("KYC of Authorized Signatories", "SIGNATORY_KYC", false));
|
||||
}
|
||||
|
||||
return docs;
|
||||
}
|
||||
|
||||
private Map<String, Object> doc(final String name, final String code, final boolean mandatory) {
|
||||
final Map<String, Object> d = new LinkedHashMap<>();
|
||||
d.put("name", name);
|
||||
d.put("code", code);
|
||||
d.put("mandatory", mandatory);
|
||||
return d;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// GET KYC for client – GET /v1/clients/{clientId}/kyc
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
@@ -173,11 +295,11 @@ public class ClientKycApiResource {
|
||||
public CommandProcessingResult updateClientKyc(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@PathParam("kycId") @Parameter(description = "KYC record identifier") final Long kycId,
|
||||
@Parameter(hidden = true) final ClientKycRequest clientKycRequest) {
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.updateClientKyc(clientId, kycId)
|
||||
.withJson(toApiJsonSerializer.serialize(clientKycRequest))
|
||||
.withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
return commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
@@ -224,11 +346,11 @@ public class ClientKycApiResource {
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@PathParam("kycId") @Parameter(description = "KYC record identifier") final Long kycId,
|
||||
@QueryParam("command") @Parameter(
|
||||
description = "Command: aadhaar-generate-otp | verify-pan | verify-aadhaar | verify-gstin | verify | reject")
|
||||
description = "Command: aadhaar-generate-otp | verify-pan | verify-aadhaar | verify-gstin | verify-cin | verify-din | verify | reject")
|
||||
final String command,
|
||||
@Parameter(hidden = true) final ClientKycRequest clientKycRequest) {
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
final String json = toApiJsonSerializer.serialize(clientKycRequest);
|
||||
final String json = apiRequestBodyAsJson;
|
||||
|
||||
LOG.info("KYC command received: clientId={}, kycId={}, command={}, requestBody={}", clientId, kycId, command, json);
|
||||
|
||||
@@ -245,6 +367,12 @@ public class ClientKycApiResource {
|
||||
} else if ("verify-gstin".equalsIgnoreCase(command)) {
|
||||
commandRequest = new CommandWrapperBuilder()
|
||||
.verifyGstinClientKyc(clientId, kycId).withJson(json).build();
|
||||
} else if ("verify-cin".equalsIgnoreCase(command)) {
|
||||
commandRequest = new CommandWrapperBuilder()
|
||||
.verifyCinClientKyc(clientId, kycId).withJson(json).build();
|
||||
} else if ("verify-din".equalsIgnoreCase(command)) {
|
||||
commandRequest = new CommandWrapperBuilder()
|
||||
.verifyDinClientKyc(clientId, kycId).withJson(json).build();
|
||||
} else if ("verify".equalsIgnoreCase(command)) {
|
||||
commandRequest = new CommandWrapperBuilder().verifyClientKyc(clientId, kycId).withJson(json).build();
|
||||
} else if ("reject".equalsIgnoreCase(command)) {
|
||||
@@ -252,7 +380,7 @@ public class ClientKycApiResource {
|
||||
} else {
|
||||
throw new org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException(
|
||||
"command", command,
|
||||
"aadhaar-generate-otp", "verify-pan", "verify-aadhaar", "verify-gstin", "verify", "reject");
|
||||
"aadhaar-generate-otp", "verify-pan", "verify-aadhaar", "verify-gstin", "verify-cin", "verify-din", "verify", "reject");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 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.client.api;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
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.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 java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.data.ClientNomineeData;
|
||||
import org.apache.fineract.portfolio.client.service.ClientNomineeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.client.service.ClientNomineeWritePlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/clients/{clientId}/nominees")
|
||||
@Component
|
||||
@Tag(name = "Client Nominees", description = "Manage nominees for a client's accounts. "
|
||||
+ "Nominees are linked to specific accounts (SB/CB/FD/RD/LA), not to clients directly. "
|
||||
+ "Nominee feature is only available if the client has at least one account. "
|
||||
+ "Total percentage share per account cannot exceed 100%.")
|
||||
@RequiredArgsConstructor
|
||||
public class ClientNomineeApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME = "CLIENTNOMINEE";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ClientNomineeReadPlatformService readService;
|
||||
private final ClientNomineeWritePlatformService writeService;
|
||||
private final DefaultToApiJsonSerializer<ClientNomineeData> toApiJsonSerializer;
|
||||
private final DefaultToApiJsonSerializer<List> listSerializer;
|
||||
private final DefaultToApiJsonSerializer<CommandProcessingResult> resultSerializer;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve nominees for a client",
|
||||
description = "Use accountNo + accountType query params to filter by a specific account. "
|
||||
+ "Without these params, returns all nominees for the client.")
|
||||
public String retrieveAll(
|
||||
@PathParam("clientId") @Parameter(description = "Client ID") final Long clientId,
|
||||
@QueryParam("accountNo") @Parameter(description = "Account number to filter by") final String accountNo,
|
||||
@QueryParam("accountType") @Parameter(description = "Account type: SB, CB, FD, RD, LA") final String accountType) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
|
||||
|
||||
final List<ClientNomineeData> nominees;
|
||||
if (accountNo != null && accountType != null) {
|
||||
nominees = readService.retrieveNomineesByAccount(clientId, accountNo, accountType);
|
||||
} else {
|
||||
nominees = readService.retrieveAllNominees(clientId);
|
||||
}
|
||||
return listSerializer.serialize(nominees);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{nomineeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve a specific nominee")
|
||||
public String retrieveOne(
|
||||
@PathParam("clientId") @Parameter(description = "Client ID") final Long clientId,
|
||||
@PathParam("nomineeId") @Parameter(description = "Nominee ID") final Long nomineeId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
|
||||
final ClientNomineeData data = readService.retrieveNominee(clientId, nomineeId);
|
||||
return toApiJsonSerializer.serialize(data);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve nominee template with dropdown options",
|
||||
description = "Also returns hasAnyAccount flag — nominee feature is only available if the client has accounts.")
|
||||
public String retrieveTemplate(
|
||||
@PathParam("clientId") @Parameter(description = "Client ID") final Long clientId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
|
||||
final ClientNomineeData template = readService.retrieveTemplate(clientId);
|
||||
template.setHasAnyAccount(readService.clientHasAnyAccount(clientId));
|
||||
return toApiJsonSerializer.serialize(template);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Add a nominee to a client's account",
|
||||
description = "accountNo and accountType are mandatory. Nominee must be an existing client. "
|
||||
+ "Percentage share per account cannot exceed 100%.")
|
||||
public String addNominee(
|
||||
@PathParam("clientId") @Parameter(description = "Client ID") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
|
||||
// Gate: client must have at least one account
|
||||
if (!readService.clientHasAnyAccount(clientId)) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.no.accounts",
|
||||
"Cannot add nominees. Client " + clientId + " has no accounts (savings, current, FD, RD, or loan).",
|
||||
"clientId");
|
||||
}
|
||||
|
||||
final JsonElement parsedCommand = this.fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
final JsonCommand command = new JsonCommand(null, apiRequestBodyAsJson, parsedCommand,
|
||||
this.fromJsonHelper, "CLIENTNOMINEE", clientId, null, null, clientId,
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
final CommandProcessingResult result = writeService.addNominee(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{nomineeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Update a nominee")
|
||||
public String updateNominee(
|
||||
@PathParam("clientId") @Parameter(description = "Client ID") final Long clientId,
|
||||
@PathParam("nomineeId") @Parameter(description = "Nominee ID") final Long nomineeId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
this.context.authenticatedUser().validateHasUpdatePermission(RESOURCE_NAME);
|
||||
final JsonElement parsedCommand = this.fromJsonHelper.parse(apiRequestBodyAsJson);
|
||||
final JsonCommand command = new JsonCommand(null, apiRequestBodyAsJson, parsedCommand,
|
||||
this.fromJsonHelper, "CLIENTNOMINEE", clientId, null, null, clientId,
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
final CommandProcessingResult result = writeService.updateNominee(clientId, nomineeId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{nomineeId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete a nominee")
|
||||
public String deleteNominee(
|
||||
@PathParam("clientId") @Parameter(description = "Client ID") final Long clientId,
|
||||
@PathParam("nomineeId") @Parameter(description = "Nominee ID") final Long nomineeId) {
|
||||
this.context.authenticatedUser().validateHasDeletePermission(RESOURCE_NAME);
|
||||
final CommandProcessingResult result = writeService.deleteNominee(clientId, nomineeId);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
+31
@@ -44,7 +44,9 @@ import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.data.AuditData;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.AuditReadPlatformService;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookPopulatorService;
|
||||
@@ -56,6 +58,7 @@ import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
|
||||
import org.apache.fineract.infrastructure.core.service.CommandParameterUtil;
|
||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||
import org.apache.fineract.infrastructure.core.service.Page;
|
||||
@@ -74,6 +77,8 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/clients")
|
||||
@@ -83,6 +88,8 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class ClientsApiResource {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClientsApiResource.class);
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ClientReadPlatformService clientReadPlatformService;
|
||||
private final ClientTemplateReadPlatformService clientTemplateReadPlatformService;
|
||||
@@ -96,6 +103,7 @@ public class ClientsApiResource {
|
||||
private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService;
|
||||
private final GuarantorReadPlatformService guarantorReadPlatformService;
|
||||
private final SqlValidator sqlValidator;
|
||||
private final AuditReadPlatformService auditReadPlatformService;
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@@ -180,6 +188,8 @@ public class ClientsApiResource {
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsResponse.class)))
|
||||
public String create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
LOG.info("Client CREATE request body: {}", apiRequestBodyAsJson);
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder() //
|
||||
.createClient() //
|
||||
.withJson(apiRequestBodyAsJson) //
|
||||
@@ -588,4 +598,25 @@ public class ClientsApiResource {
|
||||
return toApiJsonSerializer.serialize(transferDate);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{clientId}/audit")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve Client Activity Logs", description = "Returns audit trail for a specific client including all actions performed on the client and related entities.\n"
|
||||
+ "\n" + "Example Requests:\n" + "\n" + "clients/1/audit\n" + "clients/1/audit?includeJson=true")
|
||||
@ApiResponse(responseCode = "200", description = "OK")
|
||||
public String retrieveClientAuditEntries(@PathParam("clientId") @Parameter(description = "clientId") final Long clientId,
|
||||
@Context final UriInfo uriInfo,
|
||||
@QueryParam("includeJson") @Parameter(description = "includeJson") final Boolean includeJson) {
|
||||
|
||||
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
|
||||
|
||||
final SQLBuilder extraCriteria = new SQLBuilder();
|
||||
extraCriteria.addCriteria("aud.client_id = ", clientId);
|
||||
|
||||
final boolean includeJsonFlag = Boolean.TRUE.equals(includeJson);
|
||||
final List<AuditData> auditEntries = auditReadPlatformService.retrieveAuditEntries(extraCriteria, includeJsonFlag);
|
||||
return toApiJsonSerializer.serialize(auditEntries);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-11
@@ -77,28 +77,39 @@ public class ClientKycCommand {
|
||||
v.reset().parameter(ClientKycApiConstants.kycTypeParam).value(kycType).notNull().inMinMaxRange(1, 2);
|
||||
v.reset().parameter(ClientKycApiConstants.riskCategoryParam).value(riskCategory).notNull().inMinMaxRange(1, 3);
|
||||
|
||||
// PAN is mandatory as per RBI KYC guidelines
|
||||
v.reset().parameter(ClientKycApiConstants.panNumberParam).value(panNumber).notBlank()
|
||||
.notExceedingLengthOf(10)
|
||||
.matchesRegularExpression(ClientKycApiConstants.PAN_REGEX,
|
||||
"PAN must be in format: 5 uppercase letters + 4 digits + 1 uppercase letter (e.g. ABCDE1234F)");
|
||||
// PAN – optional; validated only if provided
|
||||
if (panNumber != null && !panNumber.isBlank()) {
|
||||
v.reset().parameter(ClientKycApiConstants.panNumberParam).value(panNumber)
|
||||
.notExceedingLengthOf(10)
|
||||
.matchesRegularExpression(ClientKycApiConstants.PAN_REGEX,
|
||||
"PAN must be in format: 5 uppercase letters + 4 digits + 1 uppercase letter (e.g. ABCDE1234F)");
|
||||
}
|
||||
|
||||
// Aadhaar – mandatory for individual
|
||||
if (kycType != null && kycType == 1) {
|
||||
v.reset().parameter(ClientKycApiConstants.aadhaarNumberMaskedParam).value(aadhaarNumberMasked).notBlank()
|
||||
// Aadhaar – optional; validated only if provided (mandatory for individual at verification time)
|
||||
if (aadhaarNumberMasked != null && !aadhaarNumberMasked.isBlank()) {
|
||||
v.reset().parameter(ClientKycApiConstants.aadhaarNumberMaskedParam).value(aadhaarNumberMasked)
|
||||
.notExceedingLengthOf(12)
|
||||
.matchesRegularExpression(ClientKycApiConstants.AADHAAR_MASKED_REGEX,
|
||||
"Aadhaar masked number must be 12 characters (first 8 masked, last 4 digits visible)");
|
||||
}
|
||||
|
||||
// GSTIN – mandatory for non-individual
|
||||
if (kycType != null && kycType == 2) {
|
||||
v.reset().parameter(ClientKycApiConstants.gstinNumberParam).value(gstinNumber).notBlank()
|
||||
// GSTIN – optional; validated only if provided (mandatory for non-individual at verification time)
|
||||
if (gstinNumber != null && !gstinNumber.isBlank()) {
|
||||
v.reset().parameter(ClientKycApiConstants.gstinNumberParam).value(gstinNumber)
|
||||
.notExceedingLengthOf(15)
|
||||
.matchesRegularExpression(ClientKycApiConstants.GSTIN_REGEX,
|
||||
"GSTIN must be a valid 15-character GST Identification Number");
|
||||
}
|
||||
|
||||
// At least one ID document must be provided
|
||||
final boolean hasPan = panNumber != null && !panNumber.isBlank();
|
||||
final boolean hasAadhaar = aadhaarNumberMasked != null && !aadhaarNumberMasked.isBlank();
|
||||
final boolean hasGstin = gstinNumber != null && !gstinNumber.isBlank();
|
||||
if (!hasPan && !hasAadhaar && !hasGstin) {
|
||||
v.reset().parameter(ClientKycApiConstants.panNumberParam).value(panNumber)
|
||||
.notBlank();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(errors);
|
||||
}
|
||||
|
||||
@@ -184,6 +195,16 @@ public class ClientKycCommand {
|
||||
// GSTIN is already stored on the KYC entity; nothing to validate in the command body.
|
||||
}
|
||||
|
||||
/** verify-cin command: CIN is read from the existing KYC record – no required body fields. */
|
||||
public void validateForVerifyCin() {
|
||||
// CIN is already stored on the KYC entity; nothing to validate in the command body.
|
||||
}
|
||||
|
||||
/** verify-din command: DIN is read from the existing KYC record – no required body fields. */
|
||||
public void validateForVerifyDin() {
|
||||
// DIN is already stored on the KYC entity; nothing to validate in the command body.
|
||||
}
|
||||
|
||||
public void validateForReject() {
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(ClientKycApiConstants.RESOURCE_NAME);
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ public class ClientAddressRequest implements Serializable {
|
||||
private String addressLine2;
|
||||
private String addressLine3;
|
||||
private String townVillage;
|
||||
private String street;
|
||||
private String countyDistrict;
|
||||
private Long stateProvinceId;
|
||||
private BigDecimal latitude;
|
||||
|
||||
+20
-8
@@ -27,28 +27,40 @@ public class ClientApiCollectionConstants extends ClientApiConstants {
|
||||
|
||||
protected static final Set<String> CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(familyMembers, address,
|
||||
localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, cifNumberParamName, externalIdParamName, mobileNoParamName,
|
||||
emailAddressParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName,
|
||||
alternateMobileNoParamName, emailAddressParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName,
|
||||
activeParamName, activationDateParamName, staffIdParamName, submittedOnDateParamName, savingsProductIdParamName,
|
||||
dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName,
|
||||
occupationCategoryIdParamName, occupationIdParamName,
|
||||
dateOfBirthParamName, genderIdParamName, salutationIdParamName, clientTypeIdParamName, clientClassificationIdParamName,
|
||||
occupationCategoryIdParamName, occupationIdParamName, maritalStatusIdParamName, monthlyIncomeParamName, annualTurnoverParamName,
|
||||
ageYearsParamName, ageMonthsParamName, ageDaysParamName,
|
||||
nationalityIdParamName, isMinorParamName, guardianClientIdParamName, guardianRelationParamName,
|
||||
dipVerifiedParamName, dipLookupLogIdParamName, dipTimestampParamName,
|
||||
alternateDipVerifiedParamName, alternateDipLookupLogIdParamName, alternateDipTimestampParamName,
|
||||
nameVerifiedParamName, screeningLogIdParamName,
|
||||
clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, datatables, isStaffParamName, kycParamName));
|
||||
|
||||
protected static final Set<String> CLIENT_NON_PERSON_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList(familyMembers, address, localeParamName, dateFormatParamName, incorpNumberParamName, remarksParamName,
|
||||
incorpValidityTillParamName, constitutionIdParamName, mainBusinessLineIdParamName, datatables));
|
||||
incorpValidityTillParamName, constitutionIdParamName, mainBusinessLineIdParamName,
|
||||
authorizedSignatoryParamName, additionalInfoParamName, dateOfRegistrationParamName, datatables));
|
||||
|
||||
protected static final Set<String> CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, emailAddressParamName, firstnameParamName,
|
||||
dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, alternateMobileNoParamName, emailAddressParamName, firstnameParamName,
|
||||
middlenameParamName,
|
||||
|
||||
lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName,
|
||||
dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName,
|
||||
occupationCategoryIdParamName, occupationIdParamName,
|
||||
dateOfBirthParamName, genderIdParamName, salutationIdParamName, clientTypeIdParamName, clientClassificationIdParamName,
|
||||
occupationCategoryIdParamName, occupationIdParamName, maritalStatusIdParamName, monthlyIncomeParamName, annualTurnoverParamName,
|
||||
ageYearsParamName, ageMonthsParamName, ageDaysParamName,
|
||||
nationalityIdParamName, isMinorParamName, guardianClientIdParamName, guardianRelationParamName,
|
||||
dipVerifiedParamName, dipLookupLogIdParamName, dipTimestampParamName,
|
||||
alternateDipVerifiedParamName, alternateDipLookupLogIdParamName, alternateDipTimestampParamName,
|
||||
nameVerifiedParamName, screeningLogIdParamName,
|
||||
submittedOnDateParamName, clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, isStaffParamName));
|
||||
|
||||
protected static final Set<String> CLIENT_NON_PERSON_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList(localeParamName, dateFormatParamName, incorpNumberParamName, remarksParamName, incorpValidityTillParamName,
|
||||
constitutionIdParamName, mainBusinessLineIdParamName));
|
||||
constitutionIdParamName, mainBusinessLineIdParamName, authorizedSignatoryParamName, additionalInfoParamName,
|
||||
dateOfRegistrationParamName));
|
||||
|
||||
/**
|
||||
* These parameters will match the class level parameters of {@link ClientData}. Where possible, we try to get
|
||||
|
||||
+121
@@ -166,6 +166,12 @@ public final class ClientDataValidator {
|
||||
.notExceedingLengthOf(50);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.alternateMobileNoParamName, element)) {
|
||||
final String alternateMobileNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.alternateMobileNoParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.alternateMobileNoParamName).value(alternateMobileNo).ignoreIfNull()
|
||||
.notExceedingLengthOf(50);
|
||||
}
|
||||
|
||||
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
|
||||
if (active != null) {
|
||||
if (active.booleanValue()) {
|
||||
@@ -225,6 +231,26 @@ public final class ClientDataValidator {
|
||||
baseDataValidator.reset().parameter("isStaff").value(isStaffFlag).notNull();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.nationalityIdParamName, element)) {
|
||||
final String nationality = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.nationalityIdParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.nationalityIdParamName).value(nationality).notBlank();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.isMinorParamName, element)) {
|
||||
final Boolean isMinor = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.isMinorParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.isMinorParamName).value(isMinor).notNull();
|
||||
|
||||
if (Boolean.TRUE.equals(isMinor)) {
|
||||
final Long guardianClientId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.guardianClientIdParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.guardianClientIdParamName).value(guardianClientId).notNull()
|
||||
.longGreaterThanZero();
|
||||
|
||||
final String guardianRelation = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.guardianRelationParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.guardianRelationParamName).value(guardianRelation).notBlank();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.configurationReadPlatformService.retrieveGlobalConfiguration(GlobalConfigurationConstants.ENABLE_ADDRESS).isEnabled()) {
|
||||
final JsonArray address = this.fromApiJsonHelper.extractJsonArrayNamed(ClientApiConstants.address, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.address).value(address).ignoreIfNull().jsonArrayNotEmpty();
|
||||
@@ -276,6 +302,25 @@ public final class ClientDataValidator {
|
||||
.integerGreaterThanZero();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.authorizedSignatoryParamName, element)) {
|
||||
final String authorizedSignatory = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.authorizedSignatoryParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.authorizedSignatoryParamName).value(authorizedSignatory).ignoreIfNull()
|
||||
.notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.additionalInfoParamName, element)) {
|
||||
final String additionalInfo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.additionalInfoParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.additionalInfoParamName).value(additionalInfo).ignoreIfNull()
|
||||
.notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dateOfRegistrationParamName, element)) {
|
||||
final LocalDate dateOfRegistration = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed(ClientApiConstants.dateOfRegistrationParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.dateOfRegistrationParamName).value(dateOfRegistration).ignoreIfNull();
|
||||
}
|
||||
|
||||
return dataValidationErrors;
|
||||
}
|
||||
|
||||
@@ -443,6 +488,13 @@ public final class ClientDataValidator {
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.mobileNoParamName).value(mobileNo).notExceedingLengthOf(50);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.alternateMobileNoParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final String alternateMobileNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.alternateMobileNoParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.alternateMobileNoParamName).value(alternateMobileNo).ignoreIfNull()
|
||||
.notExceedingLengthOf(50);
|
||||
}
|
||||
|
||||
final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element);
|
||||
if (active != null) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
@@ -513,6 +565,53 @@ public final class ClientDataValidator {
|
||||
baseDataValidator.reset().parameter("isStaff").value(isStaffFlag).notNull();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.nationalityIdParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final String nationality = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.nationalityIdParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.nationalityIdParamName).value(nationality).notBlank();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.isMinorParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final Boolean isMinor = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.isMinorParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.isMinorParamName).value(isMinor).notNull();
|
||||
|
||||
if (Boolean.TRUE.equals(isMinor)) {
|
||||
final Long guardianClientId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.guardianClientIdParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.guardianClientIdParamName).value(guardianClientId).notNull()
|
||||
.longGreaterThanZero();
|
||||
|
||||
final String guardianRelation = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.guardianRelationParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.guardianRelationParamName).value(guardianRelation).notBlank();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dipVerifiedParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dipLookupLogIdParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dipTimestampParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.alternateDipVerifiedParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.alternateDipLookupLogIdParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.alternateDipTimestampParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.nameVerifiedParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.screeningLogIdParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
|
||||
Map<String, Object> parameterUpdateStatusDetails = getParameterUpdateStatusAndDataValidationErrorsForUpdateOnClientNonPerson(
|
||||
element.getAsJsonObject().get(ClientApiConstants.clientNonPersonDetailsParamName));
|
||||
boolean atLeastOneParameterPassedForClientNonPersonUpdate = (boolean) parameterUpdateStatusDetails.get("parameterUpdateStatus");
|
||||
@@ -573,6 +672,28 @@ public final class ClientDataValidator {
|
||||
.integerGreaterThanZero();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.authorizedSignatoryParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final String authorizedSignatory = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.authorizedSignatoryParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.authorizedSignatoryParamName).value(authorizedSignatory)
|
||||
.notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.additionalInfoParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final String additionalInfo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.additionalInfoParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.additionalInfoParamName).value(additionalInfo)
|
||||
.notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.dateOfRegistrationParamName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final LocalDate dateOfRegistration = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed(ClientApiConstants.dateOfRegistrationParamName, element);
|
||||
baseDataValidator.reset().parameter(ClientApiConstants.dateOfRegistrationParamName).value(dateOfRegistration);
|
||||
}
|
||||
|
||||
Map<String, Object> parameterUpdateStatusDetails = new HashMap<>();
|
||||
parameterUpdateStatusDetails.put("parameterUpdateStatus", atLeastOneParameterPassedForUpdate);
|
||||
parameterUpdateStatusDetails.put("dataValidationErrors", dataValidationErrors);
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.client.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public final class ClientForm60Data {
|
||||
|
||||
private final Long id;
|
||||
private final Long clientId;
|
||||
private final String clientDisplayName;
|
||||
private final String fullName;
|
||||
private final LocalDate dateOfBirth;
|
||||
private final String fatherName;
|
||||
private final String address;
|
||||
private final String transactionType;
|
||||
private final BigDecimal transactionAmount;
|
||||
private final BigDecimal estimatedTotalIncome;
|
||||
private final String reasonForNoPan;
|
||||
private final Boolean declaration;
|
||||
private final LocalDate submittedOn;
|
||||
}
|
||||
+50
-2
@@ -48,14 +48,50 @@ public final class ClientKycApiConstants {
|
||||
/** One-time password entered by the client – required for the verify-otp step. */
|
||||
public static final String aadhaarOtpParam = "aadhaarOtp";
|
||||
|
||||
// ── Extended KYC fields ──────────────────────────────────────────────────
|
||||
public static final String panAadhaarSeedingStatusParam = "panAadhaarSeedingStatus";
|
||||
public static final String enterprisePanParam = "enterprisePan";
|
||||
public static final String cinNumberParam = "cinNumber";
|
||||
public static final String dinNumberParam = "dinNumber";
|
||||
public static final String verifyCinParam = "verifyCin";
|
||||
public static final String verifyDinParam = "verifyDin";
|
||||
public static final String incorporationDateParam = "incorporationDate";
|
||||
public static final String kycDocumentNameParam = "kycDocumentName";
|
||||
public static final String kycDocumentNumberParam = "kycDocumentNumber";
|
||||
public static final String kycDocIssueDateParam = "kycDocIssueDate";
|
||||
public static final String kycDocExpiryDateParam = "kycDocExpiryDate";
|
||||
public static final String amlCheckStatusParam = "amlCheckStatus";
|
||||
public static final String bannedEntityMatchParam = "bannedEntityMatch";
|
||||
public static final String willfulDefaulterParam = "willfulDefaulter";
|
||||
public static final String ecgcSalFlagParam = "ecgcSalFlag";
|
||||
public static final String dormantFlagParam = "dormantFlag";
|
||||
public static final String accountStatusParam = "accountStatus";
|
||||
public static final String freezeReasonParam = "freezeReason";
|
||||
public static final String freezeAuthorityParam = "freezeAuthority";
|
||||
public static final String form60TypeParam = "form60Type";
|
||||
public static final String form60DateParam = "form60Date";
|
||||
public static final String form15ApplicableParam = "form15Applicable";
|
||||
public static final String form15TypeParam = "form15Type";
|
||||
public static final String form15FYParam = "form15FY";
|
||||
|
||||
// ── Supported parameters for initiate/update ──────────────────────────────
|
||||
public static final Set<String> INITIATE_REQUEST_PARAMS = new HashSet<>(Arrays.asList(
|
||||
kycTypeParam, riskCategoryParam, panNumberParam, aadhaarNumberMaskedParam,
|
||||
gstinNumberParam, remarksParam, "locale"));
|
||||
gstinNumberParam, remarksParam, "locale", "dateFormat",
|
||||
panAadhaarSeedingStatusParam, enterprisePanParam, cinNumberParam, dinNumberParam, incorporationDateParam,
|
||||
kycDocumentNameParam, kycDocumentNumberParam, kycDocIssueDateParam, kycDocExpiryDateParam,
|
||||
amlCheckStatusParam, bannedEntityMatchParam, willfulDefaulterParam, ecgcSalFlagParam,
|
||||
dormantFlagParam, accountStatusParam, freezeReasonParam, freezeAuthorityParam,
|
||||
form60TypeParam, form60DateParam, form15ApplicableParam, form15TypeParam, form15FYParam));
|
||||
|
||||
public static final Set<String> UPDATE_REQUEST_PARAMS = new HashSet<>(Arrays.asList(
|
||||
panNumberParam, aadhaarNumberMaskedParam, gstinNumberParam,
|
||||
riskCategoryParam, remarksParam, "locale"));
|
||||
riskCategoryParam, remarksParam, "locale", "dateFormat",
|
||||
panAadhaarSeedingStatusParam, enterprisePanParam, cinNumberParam, dinNumberParam, incorporationDateParam,
|
||||
kycDocumentNameParam, kycDocumentNumberParam, kycDocIssueDateParam, kycDocExpiryDateParam,
|
||||
amlCheckStatusParam, bannedEntityMatchParam, willfulDefaulterParam, ecgcSalFlagParam,
|
||||
dormantFlagParam, accountStatusParam, freezeReasonParam, freezeAuthorityParam,
|
||||
form60TypeParam, form60DateParam, form15ApplicableParam, form15TypeParam, form15FYParam));
|
||||
|
||||
/** verify command: existing flags + Aadhaar OTP fields for the verify-otp step. */
|
||||
public static final Set<String> VERIFY_REQUEST_PARAMS = new HashSet<>(Arrays.asList(
|
||||
@@ -76,6 +112,12 @@ public final class ClientKycApiConstants {
|
||||
/** verify-gstin command: GSTIN is already stored; no extra body params required. */
|
||||
public static final Set<String> VERIFY_GSTIN_REQUEST_PARAMS = new HashSet<>();
|
||||
|
||||
/** verify-cin command: CIN is already stored; no extra body params required. */
|
||||
public static final Set<String> VERIFY_CIN_REQUEST_PARAMS = new HashSet<>();
|
||||
|
||||
/** verify-din command: DIN is already stored; no extra body params required. */
|
||||
public static final Set<String> VERIFY_DIN_REQUEST_PARAMS = new HashSet<>();
|
||||
|
||||
public static final Set<String> REJECT_REQUEST_PARAMS = new HashSet<>(Arrays.asList(remarksParam));
|
||||
|
||||
// ── PAN regex: 5 uppercase letters + 4 digits + 1 uppercase letter ────────
|
||||
@@ -86,4 +128,10 @@ public final class ClientKycApiConstants {
|
||||
|
||||
// ── GSTIN: 2-digit state + 10-char PAN + 1 entity + Z + check digit ──────
|
||||
public static final String GSTIN_REGEX = "^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$";
|
||||
|
||||
// ── CIN: 1 char status + 5 digit NIC + 2 char state + 4 digit year + 3 char ownership + 6 digit reg ──
|
||||
public static final String CIN_REGEX = "^[A-Z]{1}[0-9]{5}[A-Z]{2}[0-9]{4}[A-Z]{3}[0-9]{6}$";
|
||||
|
||||
// ── DIN: 8-digit unique director identification number ──────────────────
|
||||
public static final String DIN_REGEX = "^[0-9]{8}$";
|
||||
}
|
||||
|
||||
+32
@@ -46,6 +46,8 @@ public final class ClientKycData {
|
||||
private final Boolean panVerified;
|
||||
private final LocalDate panVerifiedOn;
|
||||
private final String panFullName;
|
||||
private final String panFirstName;
|
||||
private final String panLastName;
|
||||
private final String panAadhaarSeedingStatus;
|
||||
private final String panCategory;
|
||||
private final String panStatus;
|
||||
@@ -60,6 +62,36 @@ public final class ClientKycData {
|
||||
private final Boolean gstinVerified;
|
||||
private final LocalDate gstinVerifiedOn;
|
||||
|
||||
// ── Enterprise / Non-Individual ──────────────────────────────────────────
|
||||
private final String enterprisePan;
|
||||
private final String cinNumber;
|
||||
private final LocalDate incorporationDate;
|
||||
|
||||
// ── KYC Document ─────────────────────────────────────────────────────────
|
||||
private final String kycDocumentName;
|
||||
private final String kycDocumentNumber;
|
||||
private final LocalDate kycDocIssueDate;
|
||||
private final LocalDate kycDocExpiryDate;
|
||||
|
||||
// ── AML / Compliance ─────────────────────────────────────────────────────
|
||||
private final String amlCheckStatus;
|
||||
private final String bannedEntityMatch;
|
||||
private final String willfulDefaulter;
|
||||
private final String ecgcSalFlag;
|
||||
private final String dormantFlag;
|
||||
|
||||
// ── Account status / Freeze ──────────────────────────────────────────────
|
||||
private final String accountStatus;
|
||||
private final String freezeReason;
|
||||
private final String freezeAuthority;
|
||||
|
||||
// ── Form 60 / Form 15G/15H ───────────────────────────────────────────────
|
||||
private final String form60Type;
|
||||
private final LocalDate form60Date;
|
||||
private final Boolean form15Applicable;
|
||||
private final String form15Type;
|
||||
private final String form15Fy;
|
||||
|
||||
// ── CKYCRR ────────────────────────────────────────────────────────────────
|
||||
private final String ckycNumber;
|
||||
|
||||
|
||||
+66
@@ -48,6 +48,72 @@ public class ClientKycRequest {
|
||||
@Schema(description = "15-character GSTIN – mandatory for NON_INDIVIDUAL clients", example = "27ABCDE1234F1Z5")
|
||||
private String gstinNumber;
|
||||
|
||||
@Schema(description = "PAN-Aadhaar seeding/linking status", example = "LINKED")
|
||||
private String panAadhaarSeedingStatus;
|
||||
|
||||
@Schema(description = "Enterprise PAN (for non-individual clients)", example = "ABCDE1234F")
|
||||
private String enterprisePan;
|
||||
|
||||
@Schema(description = "Corporate Identification Number (21 characters)", example = "U12345MH2020PTC123456")
|
||||
private String cinNumber;
|
||||
|
||||
@Schema(description = "Date of incorporation for non-individual clients", example = "01 April 2020")
|
||||
private String incorporationDate;
|
||||
|
||||
@Schema(description = "Name of KYC document used for identification", example = "Aadhaar Card")
|
||||
private String kycDocumentName;
|
||||
|
||||
@Schema(description = "KYC document number", example = "1234-5678-9012")
|
||||
private String kycDocumentNumber;
|
||||
|
||||
@Schema(description = "KYC document issue date", example = "15 January 2020")
|
||||
private String kycDocIssueDate;
|
||||
|
||||
@Schema(description = "KYC document expiry date (null if no expiry)", example = "null")
|
||||
private String kycDocExpiryDate;
|
||||
|
||||
@Schema(description = "AML check status", example = "CLEAR")
|
||||
private String amlCheckStatus;
|
||||
|
||||
@Schema(description = "Whether client matches banned entity list", example = "NO")
|
||||
private String bannedEntityMatch;
|
||||
|
||||
@Schema(description = "Willful defaulter flag (RBI list)", example = "NO")
|
||||
private String willfulDefaulter;
|
||||
|
||||
@Schema(description = "ECGC SAL (Specific Approval List) flag", example = "NO")
|
||||
private String ecgcSalFlag;
|
||||
|
||||
@Schema(description = "Dormant account flag", example = "ACTIVE")
|
||||
private String dormantFlag;
|
||||
|
||||
@Schema(description = "Account status", example = "ACTIVE")
|
||||
private String accountStatus;
|
||||
|
||||
@Schema(description = "Reason for account freeze (null if not frozen)")
|
||||
private String freezeReason;
|
||||
|
||||
@Schema(description = "Authority that ordered the freeze (null if not frozen)")
|
||||
private String freezeAuthority;
|
||||
|
||||
@Schema(description = "Form 60 type (FORM_60 for clients without PAN)", example = "FORM_60")
|
||||
private String form60Type;
|
||||
|
||||
@Schema(description = "Date of Form 60 submission")
|
||||
private String form60Date;
|
||||
|
||||
@Schema(description = "Whether Form 15G/15H is applicable", example = "false")
|
||||
private Boolean form15Applicable;
|
||||
|
||||
@Schema(description = "Form 15 type: 15G or 15H", example = "15G")
|
||||
private String form15Type;
|
||||
|
||||
@Schema(description = "Financial year for Form 15G/15H")
|
||||
private String form15FY;
|
||||
|
||||
@Schema(description = "Date format for date fields", example = "dd MMMM yyyy")
|
||||
private String dateFormat;
|
||||
|
||||
@Schema(description = "Optional remarks / notes", example = "Documents submitted at branch")
|
||||
private String remarks;
|
||||
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.client.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ClientNomineeData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
|
||||
// Nominee — linked existing client
|
||||
private Long nomineeClientId;
|
||||
private String nomineeDisplayName;
|
||||
private String nomineeCifNumber;
|
||||
private String nomineeAccountNo;
|
||||
private LocalDate nomineeDateOfBirth;
|
||||
private String nomineeMobileNo;
|
||||
|
||||
// Relationship
|
||||
private Long relationshipId;
|
||||
private String relationshipName;
|
||||
|
||||
// Share & status
|
||||
private BigDecimal percentageShare;
|
||||
private Boolean isActive;
|
||||
|
||||
// Minor nominee guardian
|
||||
private Boolean nomineeIsMinor;
|
||||
private Long guardianClientId;
|
||||
private String guardianDisplayName;
|
||||
private String guardianCifNumber;
|
||||
private Long guardianRelationshipId;
|
||||
private String guardianRelationshipName;
|
||||
|
||||
private LocalDate effectiveDate;
|
||||
private String remarks;
|
||||
|
||||
// Nominee identifier (auto-generated)
|
||||
private String nomineeIdentifier;
|
||||
private String accountNo;
|
||||
private String accountType;
|
||||
|
||||
// Template options
|
||||
private Collection<CodeValueData> relationshipOptions;
|
||||
|
||||
/** Set on template response — true if client has at least one account (SB/CB/FD/RD/LA) */
|
||||
private Boolean hasAnyAccount;
|
||||
}
|
||||
+44
@@ -86,6 +86,29 @@ public class ClientFamilyMembers extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "remarks")
|
||||
private String remarks;
|
||||
|
||||
// ── CBS extended fields ──────────────────────────────────────────────
|
||||
@Column(name = "aadhaar_number", length = 12)
|
||||
private String aadhaarNumber;
|
||||
|
||||
@Column(name = "pan_number", length = 10)
|
||||
private String panNumber;
|
||||
|
||||
@Column(name = "nationality", length = 50)
|
||||
private String nationality;
|
||||
|
||||
@Column(name = "is_alive")
|
||||
private Boolean isAlive;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "linked_client_id")
|
||||
private Client linkedClient;
|
||||
|
||||
@Column(name = "marriage_date")
|
||||
private LocalDate marriageDate;
|
||||
|
||||
@Column(name = "marriage_authority", length = 50)
|
||||
private String marriageAuthority;
|
||||
|
||||
private ClientFamilyMembers(final Client client, final String firstName, final String middleName, final String lastName,
|
||||
final String qualification, final String mobileNumber, final Long age, final Boolean isDependent, final CodeValue relationship,
|
||||
final CodeValue maritalStatus, final CodeValue gender, final LocalDate dateOfBirth, final CodeValue profession,
|
||||
@@ -107,6 +130,7 @@ public class ClientFamilyMembers extends AbstractPersistableCustom<Long> {
|
||||
this.nominationType = nominationType;
|
||||
this.percentageShare = percentageShare;
|
||||
this.remarks = remarks;
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
public ClientFamilyMembers() {
|
||||
@@ -249,4 +273,24 @@ public class ClientFamilyMembers extends AbstractPersistableCustom<Long> {
|
||||
this.remarks = remarks;
|
||||
}
|
||||
|
||||
public String getAadhaarNumber() { return aadhaarNumber; }
|
||||
public void setAadhaarNumber(String aadhaarNumber) { this.aadhaarNumber = aadhaarNumber; }
|
||||
|
||||
public String getPanNumber() { return panNumber; }
|
||||
public void setPanNumber(String panNumber) { this.panNumber = panNumber; }
|
||||
|
||||
public String getNationality() { return nationality; }
|
||||
public void setNationality(String nationality) { this.nationality = nationality; }
|
||||
|
||||
public Boolean getIsAlive() { return isAlive; }
|
||||
public void setIsAlive(Boolean isAlive) { this.isAlive = isAlive; }
|
||||
|
||||
public Client getLinkedClient() { return linkedClient; }
|
||||
public void setLinkedClient(Client linkedClient) { this.linkedClient = linkedClient; }
|
||||
|
||||
public LocalDate getMarriageDate() { return marriageDate; }
|
||||
public void setMarriageDate(LocalDate marriageDate) { this.marriageDate = marriageDate; }
|
||||
|
||||
public String getMarriageAuthority() { return marriageAuthority; }
|
||||
public void setMarriageAuthority(String marriageAuthority) { this.marriageAuthority = marriageAuthority; }
|
||||
}
|
||||
|
||||
+45
-3
@@ -64,9 +64,20 @@ public class ClientNonPerson extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "remarks", length = 150, nullable = true)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "authorized_signatory", length = 500, nullable = true)
|
||||
private String authorizedSignatory;
|
||||
|
||||
@Column(name = "additional_info", length = 500, nullable = true)
|
||||
private String additionalInfo;
|
||||
|
||||
@Column(name = "date_of_registration", nullable = true)
|
||||
private LocalDate dateOfRegistration;
|
||||
|
||||
public static ClientNonPerson createNew(final Client client, final CodeValue constitution, final CodeValue mainBusinessLine,
|
||||
String incorpNumber, LocalDate incorpValidityTill, String remarks) {
|
||||
return new ClientNonPerson(client, constitution, mainBusinessLine, incorpNumber, incorpValidityTill, remarks);
|
||||
String incorpNumber, LocalDate incorpValidityTill, String remarks, String authorizedSignatory, String additionalInfo,
|
||||
LocalDate dateOfRegistration) {
|
||||
return new ClientNonPerson(client, constitution, mainBusinessLine, incorpNumber, incorpValidityTill, remarks, authorizedSignatory,
|
||||
additionalInfo, dateOfRegistration);
|
||||
}
|
||||
|
||||
protected ClientNonPerson() {
|
||||
@@ -74,7 +85,8 @@ public class ClientNonPerson extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
|
||||
private ClientNonPerson(final Client client, final CodeValue constitution, final CodeValue mainBusinessLine, final String incorpNumber,
|
||||
final LocalDate incorpValidityTill, final String remarks) {
|
||||
final LocalDate incorpValidityTill, final String remarks, final String authorizedSignatory, final String additionalInfo,
|
||||
final LocalDate dateOfRegistration) {
|
||||
if (client != null) {
|
||||
this.client = client;
|
||||
}
|
||||
@@ -97,6 +109,16 @@ public class ClientNonPerson extends AbstractPersistableCustom<Long> {
|
||||
this.remarks = remarks.trim();
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(authorizedSignatory)) {
|
||||
this.authorizedSignatory = authorizedSignatory.trim();
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(additionalInfo)) {
|
||||
this.additionalInfo = additionalInfo.trim();
|
||||
}
|
||||
|
||||
this.dateOfRegistration = dateOfRegistration;
|
||||
|
||||
validate(client);
|
||||
}
|
||||
|
||||
@@ -192,6 +214,26 @@ public class ClientNonPerson extends AbstractPersistableCustom<Long> {
|
||||
actualChanges.put(ClientApiConstants.mainBusinessLineIdParamName, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.authorizedSignatoryParamName, this.authorizedSignatory)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.authorizedSignatoryParamName);
|
||||
actualChanges.put(ClientApiConstants.authorizedSignatoryParamName, newValue);
|
||||
this.authorizedSignatory = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.additionalInfoParamName, this.additionalInfo)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.additionalInfoParamName);
|
||||
actualChanges.put(ClientApiConstants.additionalInfoParamName, newValue);
|
||||
this.additionalInfo = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.dateOfRegistrationParamName, this.dateOfRegistration)) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.dateOfRegistrationParamName);
|
||||
actualChanges.put(ClientApiConstants.dateOfRegistrationParamName, valueAsInput);
|
||||
actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
|
||||
actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
|
||||
this.dateOfRegistration = command.localDateValueOfParameterNamed(ClientApiConstants.dateOfRegistrationParamName);
|
||||
}
|
||||
|
||||
// validate();
|
||||
|
||||
return actualChanges;
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 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.client.entityclassification.api;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
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.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.core.MediaType;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.EntityClassificationData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.service.EntityClassificationReadService;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.service.EntityClassificationWriteService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/clients/{clientId}/entityclassification")
|
||||
@Component
|
||||
@Tag(name = "Entity Classification", description = "Entity Classification for non-individual client onboarding. "
|
||||
+ "Supports 6 regulatory categories: Startup India (DPIIT), Mudra Borrower (PMMY), "
|
||||
+ "Export Oriented Unit (EOU), Import/Export (IEC/DGFT), Priority Sector Lending (PSL), "
|
||||
+ "Non-Priority Sector (NPS), and MSME (Udyam Registration).")
|
||||
@RequiredArgsConstructor
|
||||
public class EntityClassificationApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME = "ENTITYCLASSIFICATION";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final EntityClassificationReadService readService;
|
||||
private final EntityClassificationWriteService writeService;
|
||||
private final DefaultToApiJsonSerializer<EntityClassificationData> toApiJsonSerializer;
|
||||
private final DefaultToApiJsonSerializer<CommandProcessingResult> resultSerializer;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve all entity classifications for a client",
|
||||
description = "Returns all 6 entity classification categories for the given client.")
|
||||
public String retrieveAll(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME);
|
||||
final EntityClassificationData data = readService.retrieveAllClassifications(clientId);
|
||||
return toApiJsonSerializer.serialize(data);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("startup")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save Startup India (DPIIT) classification",
|
||||
description = "Creates or updates the Startup India entity classification for the given client.")
|
||||
public String saveStartupIndia(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.saveStartupIndia(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("mudra")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save Mudra Borrower (PMMY) classification",
|
||||
description = "Creates or updates the Mudra Borrower entity classification for the given client.")
|
||||
public String saveMudraBorrower(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.saveMudraBorrower(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("eou")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save Export Oriented Unit (EOU) classification",
|
||||
description = "Creates or updates the EOU entity classification for the given client.")
|
||||
public String saveEou(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.saveEou(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("importexport")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save Import/Export (IEC/DGFT) classification",
|
||||
description = "Creates or updates the Import/Export entity classification for the given client.")
|
||||
public String saveImportExport(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.saveImportExport(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("psl")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save Priority Sector Lending (PSL) classification",
|
||||
description = "Creates or updates the Priority Sector Lending entity classification for the given client.")
|
||||
public String savePrioritySector(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.savePrioritySector(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("nps")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save Non-Priority Sector (NPS) classification",
|
||||
description = "Creates or updates the Non-Priority Sector entity classification for the given client.")
|
||||
public String saveNonPrioritySector(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.saveNonPrioritySector(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("msme")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save MSME (Udyam Registration) classification",
|
||||
description = "Creates or updates the MSME entity classification for the given client.")
|
||||
public String saveMsme(
|
||||
@PathParam("clientId") @Parameter(description = "Client identifier") final Long clientId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
|
||||
this.context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME);
|
||||
final JsonCommand command = toJsonCommand(clientId, apiRequestBodyAsJson);
|
||||
final CommandProcessingResult result = writeService.saveMsme(clientId, command);
|
||||
return resultSerializer.serialize(result);
|
||||
}
|
||||
|
||||
private JsonCommand toJsonCommand(final Long clientId, final String json) {
|
||||
final JsonElement parsedCommand = this.fromJsonHelper.parse(json);
|
||||
return new JsonCommand(null, json, parsedCommand, this.fromJsonHelper,
|
||||
"ENTITYCLASSIFICATION", clientId, null, null, clientId, null, null, null, null, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.client.entityclassification.data;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Wrapper DTO containing all 7 entity classification categories for a client.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class EntityClassificationData {
|
||||
|
||||
private Long clientId;
|
||||
private StartupIndiaData startupIndia;
|
||||
private MudraBorrowerData mudraBorrower;
|
||||
private EouData eou;
|
||||
private ImportExportData importExport;
|
||||
private PrioritySectorData prioritySector;
|
||||
private NonPrioritySectorData nonPrioritySector;
|
||||
private MsmeData msme;
|
||||
}
|
||||
+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.client.entityclassification.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class EouData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String eouRegistrationNumber;
|
||||
private String registeredDcZone;
|
||||
private String bondLutNumber;
|
||||
private LocalDate validityFrom;
|
||||
private LocalDate validityTo;
|
||||
private String gstinEou;
|
||||
private BigDecimal annualExportObligation;
|
||||
private BigDecimal nfeTarget;
|
||||
private String softexFilingStatus;
|
||||
private String brcSubmissionStatus;
|
||||
private String adCategoryBankCode;
|
||||
private LocalDate femaDeclarationDate;
|
||||
}
|
||||
+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.client.entityclassification.data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ImportExportData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String iecCode;
|
||||
private LocalDate iecIssueDate;
|
||||
private String iecStatus;
|
||||
private String tradeType;
|
||||
private String primaryHsCode;
|
||||
private String keyTradingCountries;
|
||||
private String adCategoryBankCode;
|
||||
private String ecgcPolicyNumber;
|
||||
private LocalDate ecgcPolicyExpiry;
|
||||
private Boolean eefcAccountRequired;
|
||||
private Boolean pcfcFacilityRequired;
|
||||
private Boolean femaDeclarationObtained;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.client.entityclassification.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class MsmeData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String udyamRegistrationNumber;
|
||||
private LocalDate registrationDate;
|
||||
private String registrationStatus;
|
||||
private String msmeClassification;
|
||||
private String enterpriseType;
|
||||
private String nicActivityCode;
|
||||
private BigDecimal annualTurnover;
|
||||
private BigDecimal investmentPlantMachinery;
|
||||
private String financialYearAssessment;
|
||||
private String socialCategoryOwner;
|
||||
private String genderPrimaryOwner;
|
||||
private Integer noOfEmployees;
|
||||
private Boolean womenLedEnterprise;
|
||||
private Boolean divyangPwdOwned;
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.client.entityclassification.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class MudraBorrowerData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String mudraCategory;
|
||||
private String mudraLoanAccountNo;
|
||||
private String lendingInstitutionName;
|
||||
private BigDecimal sanctionedAmount;
|
||||
private LocalDate disbursementDate;
|
||||
private String loanPurpose;
|
||||
private String mudraCardNumber;
|
||||
private BigDecimal currentOutstanding;
|
||||
private String repaymentStatus;
|
||||
private Boolean npaFlag;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.client.entityclassification.data;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class NonPrioritySectorData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String industrySector;
|
||||
private String nicCode;
|
||||
private String industryDescription;
|
||||
private String annualRevenueBand;
|
||||
private String riskCategory;
|
||||
private Integer customerRiskScore;
|
||||
private String amlRiskClassification;
|
||||
private Boolean pepFlag;
|
||||
private Boolean amlWatchlistMatch;
|
||||
private Boolean adverseMediaFlag;
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.client.entityclassification.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class PrioritySectorData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String pslCategory;
|
||||
private String pslSubCategory;
|
||||
private String pslLoanPurposeCode;
|
||||
private String pslEligibilityBasis;
|
||||
private String fiddReportingCode;
|
||||
private BigDecimal pslSanctionedLimit;
|
||||
private String msmeClassification;
|
||||
private String udyamRegistrationNumber;
|
||||
private LocalDate udyamRegistrationDate;
|
||||
private BigDecimal annualTurnover;
|
||||
private BigDecimal investmentPlantMachinery;
|
||||
private Boolean weakerSectionFlag;
|
||||
private Boolean scStBorrowerFlag;
|
||||
private Boolean womenEntrepreneurFlag;
|
||||
private Boolean minorityCommunityFlag;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.client.entityclassification.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class StartupIndiaData {
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Boolean enabled;
|
||||
private String dpiitRecognitionNumber;
|
||||
private LocalDate recognitionDate;
|
||||
private LocalDate certificateExpiryDate;
|
||||
private String entityStage;
|
||||
private String sectorAsDpiit;
|
||||
private String nicIndustryCode;
|
||||
private Boolean taxExemption80iac;
|
||||
private Boolean sidbiFund;
|
||||
private Boolean selfCertification;
|
||||
private Boolean fastTrackPatent;
|
||||
private Boolean govtTenderExemption;
|
||||
private Boolean creditGuaranteeCgss;
|
||||
private BigDecimal pslLoanLimit;
|
||||
private String pslSubCategory;
|
||||
}
|
||||
+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.client.entityclassification.service;
|
||||
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.EntityClassificationData;
|
||||
|
||||
public interface EntityClassificationReadService {
|
||||
|
||||
EntityClassificationData retrieveAllClassifications(Long clientId);
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* 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.client.entityclassification.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.EntityClassificationData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.EouData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.ImportExportData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.MudraBorrowerData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.NonPrioritySectorData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.PrioritySectorData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.MsmeData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.data.StartupIndiaData;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientEou;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExport;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsme;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrower;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySector;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySector;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndia;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class EntityClassificationReadServiceImpl implements EntityClassificationReadService {
|
||||
|
||||
private final ClientStartupIndiaRepository startupIndiaRepository;
|
||||
private final ClientMudraBorrowerRepository mudraBorrowerRepository;
|
||||
private final ClientEouRepository eouRepository;
|
||||
private final ClientImportExportRepository importExportRepository;
|
||||
private final ClientPrioritySectorRepository prioritySectorRepository;
|
||||
private final ClientNonPrioritySectorRepository nonPrioritySectorRepository;
|
||||
private final ClientMsmeRepository msmeRepository;
|
||||
|
||||
@Override
|
||||
public EntityClassificationData retrieveAllClassifications(final Long clientId) {
|
||||
final EntityClassificationData data = new EntityClassificationData();
|
||||
data.setClientId(clientId);
|
||||
|
||||
startupIndiaRepository.findByClientId(clientId).ifPresent(e -> data.setStartupIndia(toStartupIndiaData(e)));
|
||||
mudraBorrowerRepository.findByClientId(clientId).ifPresent(e -> data.setMudraBorrower(toMudraBorrowerData(e)));
|
||||
eouRepository.findByClientId(clientId).ifPresent(e -> data.setEou(toEouData(e)));
|
||||
importExportRepository.findByClientId(clientId).ifPresent(e -> data.setImportExport(toImportExportData(e)));
|
||||
prioritySectorRepository.findByClientId(clientId).ifPresent(e -> data.setPrioritySector(toPrioritySectorData(e)));
|
||||
nonPrioritySectorRepository.findByClientId(clientId).ifPresent(e -> data.setNonPrioritySector(toNonPrioritySectorData(e)));
|
||||
msmeRepository.findByClientId(clientId).ifPresent(e -> data.setMsme(toMsmeData(e)));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private StartupIndiaData toStartupIndiaData(final ClientStartupIndia e) {
|
||||
final StartupIndiaData d = new StartupIndiaData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setDpiitRecognitionNumber(e.getDpiitRecognitionNumber());
|
||||
d.setRecognitionDate(e.getRecognitionDate());
|
||||
d.setCertificateExpiryDate(e.getCertificateExpiryDate());
|
||||
d.setEntityStage(e.getEntityStage());
|
||||
d.setSectorAsDpiit(e.getSectorAsDpiit());
|
||||
d.setNicIndustryCode(e.getNicIndustryCode());
|
||||
d.setTaxExemption80iac(e.getTaxExemption80iac());
|
||||
d.setSidbiFund(e.getSidbiFund());
|
||||
d.setSelfCertification(e.getSelfCertification());
|
||||
d.setFastTrackPatent(e.getFastTrackPatent());
|
||||
d.setGovtTenderExemption(e.getGovtTenderExemption());
|
||||
d.setCreditGuaranteeCgss(e.getCreditGuaranteeCgss());
|
||||
d.setPslLoanLimit(e.getPslLoanLimit());
|
||||
d.setPslSubCategory(e.getPslSubCategory());
|
||||
return d;
|
||||
}
|
||||
|
||||
private MudraBorrowerData toMudraBorrowerData(final ClientMudraBorrower e) {
|
||||
final MudraBorrowerData d = new MudraBorrowerData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setMudraCategory(e.getMudraCategory());
|
||||
d.setMudraLoanAccountNo(e.getMudraLoanAccountNo());
|
||||
d.setLendingInstitutionName(e.getLendingInstitutionName());
|
||||
d.setSanctionedAmount(e.getSanctionedAmount());
|
||||
d.setDisbursementDate(e.getDisbursementDate());
|
||||
d.setLoanPurpose(e.getLoanPurpose());
|
||||
d.setMudraCardNumber(e.getMudraCardNumber());
|
||||
d.setCurrentOutstanding(e.getCurrentOutstanding());
|
||||
d.setRepaymentStatus(e.getRepaymentStatus());
|
||||
d.setNpaFlag(e.getNpaFlag());
|
||||
return d;
|
||||
}
|
||||
|
||||
private EouData toEouData(final ClientEou e) {
|
||||
final EouData d = new EouData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setEouRegistrationNumber(e.getEouRegistrationNumber());
|
||||
d.setRegisteredDcZone(e.getRegisteredDcZone());
|
||||
d.setBondLutNumber(e.getBondLutNumber());
|
||||
d.setValidityFrom(e.getValidityFrom());
|
||||
d.setValidityTo(e.getValidityTo());
|
||||
d.setGstinEou(e.getGstinEou());
|
||||
d.setAnnualExportObligation(e.getAnnualExportObligation());
|
||||
d.setNfeTarget(e.getNfeTarget());
|
||||
d.setSoftexFilingStatus(e.getSoftexFilingStatus());
|
||||
d.setBrcSubmissionStatus(e.getBrcSubmissionStatus());
|
||||
d.setAdCategoryBankCode(e.getAdCategoryBankCode());
|
||||
d.setFemaDeclarationDate(e.getFemaDeclarationDate());
|
||||
return d;
|
||||
}
|
||||
|
||||
private ImportExportData toImportExportData(final ClientImportExport e) {
|
||||
final ImportExportData d = new ImportExportData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setIecCode(e.getIecCode());
|
||||
d.setIecIssueDate(e.getIecIssueDate());
|
||||
d.setIecStatus(e.getIecStatus());
|
||||
d.setTradeType(e.getTradeType());
|
||||
d.setPrimaryHsCode(e.getPrimaryHsCode());
|
||||
d.setKeyTradingCountries(e.getKeyTradingCountries());
|
||||
d.setAdCategoryBankCode(e.getAdCategoryBankCode());
|
||||
d.setEcgcPolicyNumber(e.getEcgcPolicyNumber());
|
||||
d.setEcgcPolicyExpiry(e.getEcgcPolicyExpiry());
|
||||
d.setEefcAccountRequired(e.getEefcAccountRequired());
|
||||
d.setPcfcFacilityRequired(e.getPcfcFacilityRequired());
|
||||
d.setFemaDeclarationObtained(e.getFemaDeclarationObtained());
|
||||
return d;
|
||||
}
|
||||
|
||||
private PrioritySectorData toPrioritySectorData(final ClientPrioritySector e) {
|
||||
final PrioritySectorData d = new PrioritySectorData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setPslCategory(e.getPslCategory());
|
||||
d.setPslSubCategory(e.getPslSubCategory());
|
||||
d.setPslLoanPurposeCode(e.getPslLoanPurposeCode());
|
||||
d.setPslEligibilityBasis(e.getPslEligibilityBasis());
|
||||
d.setFiddReportingCode(e.getFiddReportingCode());
|
||||
d.setPslSanctionedLimit(e.getPslSanctionedLimit());
|
||||
d.setMsmeClassification(e.getMsmeClassification());
|
||||
d.setUdyamRegistrationNumber(e.getUdyamRegistrationNumber());
|
||||
d.setUdyamRegistrationDate(e.getUdyamRegistrationDate());
|
||||
d.setAnnualTurnover(e.getAnnualTurnover());
|
||||
d.setInvestmentPlantMachinery(e.getInvestmentPlantMachinery());
|
||||
d.setWeakerSectionFlag(e.getWeakerSectionFlag());
|
||||
d.setScStBorrowerFlag(e.getScStBorrowerFlag());
|
||||
d.setWomenEntrepreneurFlag(e.getWomenEntrepreneurFlag());
|
||||
d.setMinorityCommunityFlag(e.getMinorityCommunityFlag());
|
||||
return d;
|
||||
}
|
||||
|
||||
private MsmeData toMsmeData(final ClientMsme e) {
|
||||
final MsmeData d = new MsmeData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setUdyamRegistrationNumber(e.getUdyamRegistrationNumber());
|
||||
d.setRegistrationDate(e.getRegistrationDate());
|
||||
d.setRegistrationStatus(e.getRegistrationStatus());
|
||||
d.setMsmeClassification(e.getMsmeClassification());
|
||||
d.setEnterpriseType(e.getEnterpriseType());
|
||||
d.setNicActivityCode(e.getNicActivityCode());
|
||||
d.setAnnualTurnover(e.getAnnualTurnover());
|
||||
d.setInvestmentPlantMachinery(e.getInvestmentPlantMachinery());
|
||||
d.setFinancialYearAssessment(e.getFinancialYearAssessment());
|
||||
d.setSocialCategoryOwner(e.getSocialCategoryOwner());
|
||||
d.setGenderPrimaryOwner(e.getGenderPrimaryOwner());
|
||||
d.setNoOfEmployees(e.getNoOfEmployees());
|
||||
d.setWomenLedEnterprise(e.getWomenLedEnterprise());
|
||||
d.setDivyangPwdOwned(e.getDivyangPwdOwned());
|
||||
return d;
|
||||
}
|
||||
|
||||
private NonPrioritySectorData toNonPrioritySectorData(final ClientNonPrioritySector e) {
|
||||
final NonPrioritySectorData d = new NonPrioritySectorData();
|
||||
d.setId(e.getId());
|
||||
d.setClientId(e.getClient().getId());
|
||||
d.setEnabled(e.getEnabled());
|
||||
d.setIndustrySector(e.getIndustrySector());
|
||||
d.setNicCode(e.getNicCode());
|
||||
d.setIndustryDescription(e.getIndustryDescription());
|
||||
d.setAnnualRevenueBand(e.getAnnualRevenueBand());
|
||||
d.setRiskCategory(e.getRiskCategory());
|
||||
d.setCustomerRiskScore(e.getCustomerRiskScore());
|
||||
d.setAmlRiskClassification(e.getAmlRiskClassification());
|
||||
d.setPepFlag(e.getPepFlag());
|
||||
d.setAmlWatchlistMatch(e.getAmlWatchlistMatch());
|
||||
d.setAdverseMediaFlag(e.getAdverseMediaFlag());
|
||||
return d;
|
||||
}
|
||||
}
|
||||
+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.client.entityclassification.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface EntityClassificationWriteService {
|
||||
|
||||
CommandProcessingResult saveStartupIndia(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult saveMudraBorrower(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult saveEou(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult saveImportExport(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult savePrioritySector(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult saveNonPrioritySector(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult saveMsme(Long clientId, JsonCommand command);
|
||||
}
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* 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.client.entityclassification.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientEou;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExport;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsme;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrower;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySector;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySector;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndia;
|
||||
import org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EntityClassificationWriteServiceImpl implements EntityClassificationWriteService {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final FromJsonHelper fromJsonHelper;
|
||||
private final ClientStartupIndiaRepository startupIndiaRepository;
|
||||
private final ClientMudraBorrowerRepository mudraBorrowerRepository;
|
||||
private final ClientEouRepository eouRepository;
|
||||
private final ClientImportExportRepository importExportRepository;
|
||||
private final ClientPrioritySectorRepository prioritySectorRepository;
|
||||
private final ClientNonPrioritySectorRepository nonPrioritySectorRepository;
|
||||
private final ClientMsmeRepository msmeRepository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveStartupIndia(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientStartupIndia entity = startupIndiaRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientStartupIndia newEntity = new ClientStartupIndia();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setDpiitRecognitionNumber(strVal(command, "dpiitRecognitionNumber"));
|
||||
entity.setRecognitionDate(dateVal(command, "recognitionDate"));
|
||||
entity.setCertificateExpiryDate(dateVal(command, "certificateExpiryDate"));
|
||||
entity.setEntityStage(strVal(command, "entityStage"));
|
||||
entity.setSectorAsDpiit(strVal(command, "sectorAsDpiit"));
|
||||
entity.setNicIndustryCode(strVal(command, "nicIndustryCode"));
|
||||
entity.setTaxExemption80iac(boolVal(command, "taxExemption80iac"));
|
||||
entity.setSidbiFund(boolVal(command, "sidbiFund"));
|
||||
entity.setSelfCertification(boolVal(command, "selfCertification"));
|
||||
entity.setFastTrackPatent(boolVal(command, "fastTrackPatent"));
|
||||
entity.setGovtTenderExemption(boolVal(command, "govtTenderExemption"));
|
||||
entity.setCreditGuaranteeCgss(boolVal(command, "creditGuaranteeCgss"));
|
||||
entity.setPslLoanLimit(decVal(command, "pslLoanLimit"));
|
||||
entity.setPslSubCategory(strVal(command, "pslSubCategory"));
|
||||
|
||||
startupIndiaRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveMudraBorrower(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientMudraBorrower entity = mudraBorrowerRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientMudraBorrower newEntity = new ClientMudraBorrower();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setMudraCategory(strVal(command, "mudraCategory"));
|
||||
entity.setMudraLoanAccountNo(strVal(command, "mudraLoanAccountNo"));
|
||||
entity.setLendingInstitutionName(strVal(command, "lendingInstitutionName"));
|
||||
entity.setSanctionedAmount(decVal(command, "sanctionedAmount"));
|
||||
entity.setDisbursementDate(dateVal(command, "disbursementDate"));
|
||||
entity.setLoanPurpose(strVal(command, "loanPurpose"));
|
||||
entity.setMudraCardNumber(strVal(command, "mudraCardNumber"));
|
||||
entity.setCurrentOutstanding(decVal(command, "currentOutstanding"));
|
||||
entity.setRepaymentStatus(strVal(command, "repaymentStatus"));
|
||||
entity.setNpaFlag(boolVal(command, "npaFlag"));
|
||||
|
||||
mudraBorrowerRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveEou(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientEou entity = eouRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientEou newEntity = new ClientEou();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setEouRegistrationNumber(strVal(command, "eouRegistrationNumber"));
|
||||
entity.setRegisteredDcZone(strVal(command, "registeredDcZone"));
|
||||
entity.setBondLutNumber(strVal(command, "bondLutNumber"));
|
||||
entity.setValidityFrom(dateVal(command, "validityFrom"));
|
||||
entity.setValidityTo(dateVal(command, "validityTo"));
|
||||
entity.setGstinEou(strVal(command, "gstinEou"));
|
||||
entity.setAnnualExportObligation(decVal(command, "annualExportObligation"));
|
||||
entity.setNfeTarget(decVal(command, "nfeTarget"));
|
||||
entity.setSoftexFilingStatus(strVal(command, "softexFilingStatus"));
|
||||
entity.setBrcSubmissionStatus(strVal(command, "brcSubmissionStatus"));
|
||||
entity.setAdCategoryBankCode(strVal(command, "adCategoryBankCode"));
|
||||
entity.setFemaDeclarationDate(dateVal(command, "femaDeclarationDate"));
|
||||
|
||||
eouRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveImportExport(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientImportExport entity = importExportRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientImportExport newEntity = new ClientImportExport();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setIecCode(strVal(command, "iecCode"));
|
||||
entity.setIecIssueDate(dateVal(command, "iecIssueDate"));
|
||||
entity.setIecStatus(strVal(command, "iecStatus"));
|
||||
entity.setTradeType(strVal(command, "tradeType"));
|
||||
entity.setPrimaryHsCode(strVal(command, "primaryHsCode"));
|
||||
entity.setKeyTradingCountries(strVal(command, "keyTradingCountries"));
|
||||
entity.setAdCategoryBankCode(strVal(command, "adCategoryBankCode"));
|
||||
entity.setEcgcPolicyNumber(strVal(command, "ecgcPolicyNumber"));
|
||||
entity.setEcgcPolicyExpiry(dateVal(command, "ecgcPolicyExpiry"));
|
||||
entity.setEefcAccountRequired(boolVal(command, "eefcAccountRequired"));
|
||||
entity.setPcfcFacilityRequired(boolVal(command, "pcfcFacilityRequired"));
|
||||
entity.setFemaDeclarationObtained(boolVal(command, "femaDeclarationObtained"));
|
||||
|
||||
importExportRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult savePrioritySector(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientPrioritySector entity = prioritySectorRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientPrioritySector newEntity = new ClientPrioritySector();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setPslCategory(strVal(command, "pslCategory"));
|
||||
entity.setPslSubCategory(strVal(command, "pslSubCategory"));
|
||||
entity.setPslLoanPurposeCode(strVal(command, "pslLoanPurposeCode"));
|
||||
entity.setPslEligibilityBasis(strVal(command, "pslEligibilityBasis"));
|
||||
entity.setFiddReportingCode(strVal(command, "fiddReportingCode"));
|
||||
entity.setPslSanctionedLimit(decVal(command, "pslSanctionedLimit"));
|
||||
entity.setMsmeClassification(strVal(command, "msmeClassification"));
|
||||
entity.setUdyamRegistrationNumber(strVal(command, "udyamRegistrationNumber"));
|
||||
entity.setUdyamRegistrationDate(dateVal(command, "udyamRegistrationDate"));
|
||||
entity.setAnnualTurnover(decVal(command, "annualTurnover"));
|
||||
entity.setInvestmentPlantMachinery(decVal(command, "investmentPlantMachinery"));
|
||||
entity.setWeakerSectionFlag(boolVal(command, "weakerSectionFlag"));
|
||||
entity.setScStBorrowerFlag(boolVal(command, "scStBorrowerFlag"));
|
||||
entity.setWomenEntrepreneurFlag(boolVal(command, "womenEntrepreneurFlag"));
|
||||
entity.setMinorityCommunityFlag(boolVal(command, "minorityCommunityFlag"));
|
||||
|
||||
prioritySectorRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveNonPrioritySector(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientNonPrioritySector entity = nonPrioritySectorRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientNonPrioritySector newEntity = new ClientNonPrioritySector();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setIndustrySector(strVal(command, "industrySector"));
|
||||
entity.setNicCode(strVal(command, "nicCode"));
|
||||
entity.setIndustryDescription(strVal(command, "industryDescription"));
|
||||
entity.setAnnualRevenueBand(strVal(command, "annualRevenueBand"));
|
||||
entity.setRiskCategory(strVal(command, "riskCategory"));
|
||||
entity.setCustomerRiskScore(intVal(command, "customerRiskScore"));
|
||||
entity.setAmlRiskClassification(strVal(command, "amlRiskClassification"));
|
||||
entity.setPepFlag(boolVal(command, "pepFlag"));
|
||||
entity.setAmlWatchlistMatch(boolVal(command, "amlWatchlistMatch"));
|
||||
entity.setAdverseMediaFlag(boolVal(command, "adverseMediaFlag"));
|
||||
|
||||
nonPrioritySectorRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult saveMsme(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
final ClientMsme entity = msmeRepository.findByClientId(clientId).orElseGet(() -> {
|
||||
final ClientMsme newEntity = new ClientMsme();
|
||||
newEntity.setClient(client);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
entity.setEnabled(boolVal(command, "enabled"));
|
||||
entity.setUdyamRegistrationNumber(strVal(command, "udyamRegistrationNumber"));
|
||||
entity.setRegistrationDate(dateVal(command, "registrationDate"));
|
||||
entity.setRegistrationStatus(strVal(command, "registrationStatus"));
|
||||
entity.setMsmeClassification(strVal(command, "msmeClassification"));
|
||||
entity.setEnterpriseType(strVal(command, "enterpriseType"));
|
||||
entity.setNicActivityCode(strVal(command, "nicActivityCode"));
|
||||
entity.setAnnualTurnover(decVal(command, "annualTurnover"));
|
||||
entity.setInvestmentPlantMachinery(decVal(command, "investmentPlantMachinery"));
|
||||
entity.setFinancialYearAssessment(strVal(command, "financialYearAssessment"));
|
||||
entity.setSocialCategoryOwner(strVal(command, "socialCategoryOwner"));
|
||||
entity.setGenderPrimaryOwner(strVal(command, "genderPrimaryOwner"));
|
||||
entity.setNoOfEmployees(intVal(command, "noOfEmployees"));
|
||||
entity.setWomenLedEnterprise(boolVal(command, "womenLedEnterprise"));
|
||||
entity.setDivyangPwdOwned(boolVal(command, "divyangPwdOwned"));
|
||||
|
||||
msmeRepository.saveAndFlush(entity);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(entity.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
// --- Helper methods for extracting values from JsonCommand ---
|
||||
|
||||
private String strVal(final JsonCommand command, final String paramName) {
|
||||
return command.stringValueOfParameterNamed(paramName);
|
||||
}
|
||||
|
||||
private Boolean boolVal(final JsonCommand command, final String paramName) {
|
||||
if (command.parameterExists(paramName)) {
|
||||
return command.booleanObjectValueOfParameterNamed(paramName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LocalDate dateVal(final JsonCommand command, final String paramName) {
|
||||
return command.localDateValueOfParameterNamed(paramName);
|
||||
}
|
||||
|
||||
private BigDecimal decVal(final JsonCommand command, final String paramName) {
|
||||
return command.bigDecimalValueOfParameterNamed(paramName);
|
||||
}
|
||||
|
||||
private Integer intVal(final JsonCommand command, final String paramName) {
|
||||
if (command.parameterExists(paramName)) {
|
||||
return command.integerValueOfParameterNamed(paramName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+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.client.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.client.service.ClientForm60WritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "CLIENTFORM60", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateClientForm60CommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ClientForm60WritePlatformService form60WritePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return form60WritePlatformService.createForm60(command.getClientId(), command);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.client.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.client.service.ClientKycWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Handles the {@code verify-cin} KYC command.
|
||||
*
|
||||
* <p>Calls LincPay {@code /validate-company} with the CIN stored on the KYC record.
|
||||
* On success, marks {@code cinVerified = true} on the KYC record.
|
||||
*/
|
||||
@Service
|
||||
@CommandType(entity = "CLIENTKYC", action = "VERIFYCIN")
|
||||
@RequiredArgsConstructor
|
||||
public class VerifyCinClientKycCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ClientKycWritePlatformService clientKycWritePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return clientKycWritePlatformService.verifyCin(
|
||||
command.getClientId(), command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.client.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.client.service.ClientKycWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Handles the {@code verify-din} KYC command.
|
||||
*
|
||||
* <p>Calls LincPay {@code /validate-director} with the DIN stored on the KYC record.
|
||||
* On success, marks {@code dinVerified = true} on the KYC record.
|
||||
*/
|
||||
@Service
|
||||
@CommandType(entity = "CLIENTKYC", action = "VERIFYDIN")
|
||||
@RequiredArgsConstructor
|
||||
public class VerifyDinClientKycCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final ClientKycWritePlatformService clientKycWritePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return clientKycWritePlatformService.verifyDin(
|
||||
command.getClientId(), command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+16
@@ -74,4 +74,20 @@ public interface KycVerificationService {
|
||||
* @return verification result
|
||||
*/
|
||||
KycVerificationResult verifyGstin(String gstinNumber);
|
||||
|
||||
/**
|
||||
* Verify a CIN (Corporate Identification Number) via the MCA database through LincPay API.
|
||||
*
|
||||
* @param cinNumber 21-character CIN (e.g. {@code U12345MH2020PTC123456})
|
||||
* @return verification result containing company master data
|
||||
*/
|
||||
KycVerificationResult verifyCin(String cinNumber);
|
||||
|
||||
/**
|
||||
* Verify a DIN (Director Identification Number) via the MCA database through LincPay API.
|
||||
*
|
||||
* @param dinNumber 8-digit DIN
|
||||
* @return verification result containing director and associated company data
|
||||
*/
|
||||
KycVerificationResult verifyDin(String dinNumber);
|
||||
}
|
||||
|
||||
+14
@@ -67,4 +67,18 @@ public class KycVerificationServiceImpl implements KycVerificationService {
|
||||
return KycVerificationResult.success("STUB-GSTIN-REF-" + gstinNumber,
|
||||
"{\"status\":\"SUCCESS\",\"message\":\"Stub: GSTIN verified\",\"gstin\":\"" + gstinNumber + "\"}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KycVerificationResult verifyCin(final String cinNumber) {
|
||||
LOG.info("[KYC-STUB] CIN verification called for CIN: {}. Replace with LincPay.", cinNumber);
|
||||
return KycVerificationResult.success("STUB-CIN-REF-" + cinNumber,
|
||||
"{\"status\":\"SUCCESS\",\"message\":\"Stub: CIN verified\",\"cin\":\"" + cinNumber + "\"}");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KycVerificationResult verifyDin(final String dinNumber) {
|
||||
LOG.info("[KYC-STUB] DIN verification called for DIN: {}. Replace with LincPay.", dinNumber);
|
||||
return KycVerificationResult.success("STUB-DIN-REF-" + dinNumber,
|
||||
"{\"status\":\"SUCCESS\",\"message\":\"Stub: DIN verified\",\"din\":\"" + dinNumber + "\"}");
|
||||
}
|
||||
}
|
||||
|
||||
+88
@@ -90,6 +90,12 @@ public class LincPayKycVerificationServiceImpl implements KycVerificationService
|
||||
@Value("${fineract.kyc.lincpay.path.gstin-validate}")
|
||||
private String gstinValidatePath;
|
||||
|
||||
@Value("${fineract.kyc.lincpay.path.cin-validate}")
|
||||
private String cinValidatePath;
|
||||
|
||||
@Value("${fineract.kyc.lincpay.path.din-validate}")
|
||||
private String dinValidatePath;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public LincPayKycVerificationServiceImpl() {
|
||||
@@ -274,6 +280,88 @@ public class LincPayKycVerificationServiceImpl implements KycVerificationService
|
||||
}
|
||||
}
|
||||
|
||||
// ── CIN Validation (Company Identification Number) ─────────────────────
|
||||
|
||||
/**
|
||||
* POST https://ekyc.lincpay.in/EKYC/ekyc/validate-company
|
||||
* Body: { "apiKey": "...", "cin": "...", "secretKey": "..." }
|
||||
*/
|
||||
@Override
|
||||
public KycVerificationResult verifyCin(final String cinNumber) {
|
||||
final String url = baseUrl + cinValidatePath;
|
||||
try {
|
||||
final JsonObject requestBody = new JsonObject();
|
||||
requestBody.addProperty("apiKey", apiKey);
|
||||
requestBody.addProperty("cin", cinNumber);
|
||||
requestBody.addProperty("secretKey", secret);
|
||||
|
||||
LOG.info("LincPay CIN request URL: {}", url);
|
||||
final String rawResponse = post(url, requestBody.toString());
|
||||
LOG.info("LincPay CIN raw response: {}", rawResponse);
|
||||
final JsonObject response = parseJson(rawResponse);
|
||||
|
||||
if (isSuccess(response)) {
|
||||
LOG.info("LincPay CIN verified successfully for CIN: {}", cinNumber);
|
||||
return KycVerificationResult.success("CIN-" + cinNumber, rawResponse);
|
||||
}
|
||||
|
||||
final String errorMsg = extractMessage(response);
|
||||
LOG.warn("LincPay CIN validation failed for CIN {}: {}", cinNumber, errorMsg);
|
||||
return KycVerificationResult.failure(errorMsg, rawResponse);
|
||||
|
||||
} catch (final HttpClientErrorException | HttpServerErrorException e) {
|
||||
LOG.error("LincPay CIN validation HTTP error for CIN {}: {} {}", cinNumber,
|
||||
e.getStatusCode(), e.getResponseBodyAsString());
|
||||
return KycVerificationResult.failure(
|
||||
"CIN verification failed (HTTP " + e.getStatusCode() + "): "
|
||||
+ e.getResponseBodyAsString(), null);
|
||||
} catch (final Exception e) {
|
||||
LOG.error("LincPay CIN validation unexpected error for CIN: {}", cinNumber, e);
|
||||
return KycVerificationResult.failure("CIN verification failed: " + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ── DIN Validation (Director Identification Number) ──────────────────────
|
||||
|
||||
/**
|
||||
* POST https://ekyc.lincpay.in/EKYC/ekyc/validate-director
|
||||
* Body: { "apiKey": "...", "din": "...", "secretKey": "..." }
|
||||
*/
|
||||
@Override
|
||||
public KycVerificationResult verifyDin(final String dinNumber) {
|
||||
final String url = baseUrl + dinValidatePath;
|
||||
try {
|
||||
final JsonObject requestBody = new JsonObject();
|
||||
requestBody.addProperty("apiKey", apiKey);
|
||||
requestBody.addProperty("din", dinNumber);
|
||||
requestBody.addProperty("secretKey", secret);
|
||||
|
||||
LOG.info("LincPay DIN request URL: {}", url);
|
||||
final String rawResponse = post(url, requestBody.toString());
|
||||
LOG.info("LincPay DIN raw response: {}", rawResponse);
|
||||
final JsonObject response = parseJson(rawResponse);
|
||||
|
||||
if (isSuccess(response)) {
|
||||
LOG.info("LincPay DIN verified successfully for DIN: {}", dinNumber);
|
||||
return KycVerificationResult.success("DIN-" + dinNumber, rawResponse);
|
||||
}
|
||||
|
||||
final String errorMsg = extractMessage(response);
|
||||
LOG.warn("LincPay DIN validation failed for DIN {}: {}", dinNumber, errorMsg);
|
||||
return KycVerificationResult.failure(errorMsg, rawResponse);
|
||||
|
||||
} catch (final HttpClientErrorException | HttpServerErrorException e) {
|
||||
LOG.error("LincPay DIN validation HTTP error for DIN {}: {} {}", dinNumber,
|
||||
e.getStatusCode(), e.getResponseBodyAsString());
|
||||
return KycVerificationResult.failure(
|
||||
"DIN verification failed (HTTP " + e.getStatusCode() + "): "
|
||||
+ e.getResponseBodyAsString(), null);
|
||||
} catch (final Exception e) {
|
||||
LOG.error("LincPay DIN validation unexpected error for DIN: {}", dinNumber, e);
|
||||
return KycVerificationResult.failure("DIN verification failed: " + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Internal helpers ──────────────────────────────────────────────────────
|
||||
|
||||
private String post(final String url, final String requestBodyJson) {
|
||||
|
||||
+51
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.client.mapper;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
|
||||
@@ -27,8 +29,10 @@ import org.apache.fineract.portfolio.client.data.ClientTimelineData;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.Named;
|
||||
|
||||
@Mapper(config = MapstructMapperConfig.class)
|
||||
@@ -43,6 +47,8 @@ public interface ClientMapper {
|
||||
@Mapping(target = "transferToOfficeName", source = "transferToOffice.name")
|
||||
@Mapping(target = "externalId", source = "externalId")
|
||||
@Mapping(target = "gender", source = "source", qualifiedByName = "clientGenderCode")
|
||||
@Mapping(target = "salutation", source = "source", qualifiedByName = "salutationCode")
|
||||
@Mapping(target = "salutationOptions", ignore = true)
|
||||
@Mapping(target = "imageId", source = "imageId")
|
||||
@Mapping(target = "staffId", source = "staff.id")
|
||||
@Mapping(target = "staffName", source = "staff.displayName")
|
||||
@@ -54,10 +60,16 @@ public interface ClientMapper {
|
||||
@Mapping(target = "clientClassification", source = "source", qualifiedByName = "clientClassificationCode")
|
||||
@Mapping(target = "occupationCategory", source = "source", qualifiedByName = "occupationCategoryCode")
|
||||
@Mapping(target = "occupation", source = "source", qualifiedByName = "occupationCode")
|
||||
@Mapping(target = "maritalStatus", source = "source", qualifiedByName = "maritalStatusCode")
|
||||
@Mapping(target = "occupationCategoryOptions", ignore = true)
|
||||
@Mapping(target = "occupationOptions", ignore = true)
|
||||
@Mapping(target = "legalForm", source = "source", qualifiedByName = "clientLegalFormEnum")
|
||||
@Mapping(target = "isStaff", source = "source", qualifiedByName = "clientIsStaff")
|
||||
@Mapping(target = "ageYears", ignore = true)
|
||||
@Mapping(target = "ageMonths", ignore = true)
|
||||
@Mapping(target = "ageDays", ignore = true)
|
||||
@Mapping(target = "guardianClientId", source = "source", qualifiedByName = "guardianClientIdValue")
|
||||
@Mapping(target = "guardianClientDisplayName", source = "source", qualifiedByName = "guardianClientDisplayNameValue")
|
||||
@Mapping(target = "imagePresent", ignore = true)
|
||||
@Mapping(target = "officeOptions", ignore = true)
|
||||
@Mapping(target = "staffOptions", ignore = true)
|
||||
@@ -122,6 +134,24 @@ public interface ClientMapper {
|
||||
return CodeValueData.instance(code.getId(), code.getLabel());
|
||||
}
|
||||
|
||||
@Named("salutationCode")
|
||||
default CodeValueData salutationCode(Client client) {
|
||||
final CodeValue code = client.getSalutation();
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
return CodeValueData.instance(code.getId(), code.getLabel());
|
||||
}
|
||||
|
||||
@Named("maritalStatusCode")
|
||||
default CodeValueData maritalStatusCode(Client client) {
|
||||
final CodeValue code = client.getMaritalStatus();
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
return CodeValueData.instance(code.getId(), code.getLabel());
|
||||
}
|
||||
|
||||
@Named("clientSubStatusCode")
|
||||
default CodeValueData clientSubStatusCode(Client client) {
|
||||
final CodeValue code = client.getSubStatus();
|
||||
@@ -178,4 +208,25 @@ public interface ClientMapper {
|
||||
return Boolean.valueOf(client.isStaff());
|
||||
}
|
||||
|
||||
@Named("guardianClientIdValue")
|
||||
default Long guardianClientIdValue(Client client) {
|
||||
return client.getGuardianClient() != null ? client.getGuardianClient().getId() : null;
|
||||
}
|
||||
|
||||
@Named("guardianClientDisplayNameValue")
|
||||
default String guardianClientDisplayNameValue(Client client) {
|
||||
return client.getGuardianClient() != null ? client.getGuardianClient().getDisplayName() : null;
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
default void computeAge(Client source, @MappingTarget ClientData target) {
|
||||
final LocalDate dob = source.getDateOfBirth();
|
||||
if (dob != null) {
|
||||
final Period age = Period.between(dob, LocalDate.now());
|
||||
target.setAgeYears(age.getYears());
|
||||
target.setAgeMonths(age.getMonths());
|
||||
target.setAgeDays(age.getDays());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+92
-1
@@ -62,10 +62,25 @@ public final class ClientFamilyMemberCommandFromApiJsonDeserializer {
|
||||
public static final String NOMINATION_TYPE = "nominationType";
|
||||
public static final String PERCENTAGE_SHARE = "percentageShare";
|
||||
public static final String REMARKS = "remarks";
|
||||
public static final String AADHAAR_NUMBER = "aadhaarNumber";
|
||||
public static final String PAN_NUMBER = "panNumber";
|
||||
public static final String NATIONALITY = "nationality";
|
||||
public static final String IS_ALIVE = "isAlive";
|
||||
public static final String LINKED_CLIENT_ID = "linkedClientId";
|
||||
public static final String MARRIAGE_DATE = "marriageDate";
|
||||
public static final String MARRIAGE_AUTHORITY = "marriageAuthority";
|
||||
|
||||
private static final java.util.regex.Pattern AADHAAR_PATTERN = java.util.regex.Pattern.compile("^\\d{12}$");
|
||||
private static final java.util.regex.Pattern PAN_PATTERN = java.util.regex.Pattern.compile("^[A-Z]{5}[0-9]{4}[A-Z]$");
|
||||
private static final Set<String> VALID_MARRIAGE_AUTHORITIES = Set.of(
|
||||
"SUB_REGISTRAR_OFFICE", "MARRIAGE_REGISTRAR", "COURT_MARRIAGE_OFFICER",
|
||||
"MUNICIPAL_AUTHORITY", "EMBASSY_CONSULATE", "DEFENCE_AUTHORITY");
|
||||
|
||||
private static final Set<String> SUPPORTED_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList(ID, CLIENT_ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME, QUALIFICATION, MOBILE_NUMBER, AGE, IS_DEPENDENT,
|
||||
RELATIONSHIP_ID, MARITAL_STATUS_ID, GENDER_ID, DATE_OF_BIRTH, PROFESSION_ID, LOCALE, DATE_FORMAT, FAMILY_MEMBERS,
|
||||
NOMINATION_TYPE, PERCENTAGE_SHARE, REMARKS));
|
||||
NOMINATION_TYPE, PERCENTAGE_SHARE, REMARKS,
|
||||
AADHAAR_NUMBER, PAN_NUMBER, NATIONALITY, IS_ALIVE, LINKED_CLIENT_ID, MARRIAGE_DATE, MARRIAGE_AUTHORITY));
|
||||
public static final String FAMILY_MEMBERS1 = "FamilyMembers";
|
||||
public static final String RELATION_SHIP_ID = "relationShipId";
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
@@ -207,6 +222,44 @@ public final class ClientFamilyMemberCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter(REMARKS).value(remarks).notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
// CBS extended field validations
|
||||
if (this.fromApiJsonHelper.extractStringNamed(AADHAAR_NUMBER, element) != null) {
|
||||
final String aadhaar = this.fromApiJsonHelper.extractStringNamed(AADHAAR_NUMBER, element);
|
||||
baseDataValidator.reset().parameter(AADHAAR_NUMBER).value(aadhaar).notExceedingLengthOf(12);
|
||||
if (aadhaar != null && !aadhaar.isEmpty() && !AADHAAR_PATTERN.matcher(aadhaar).matches()) {
|
||||
baseDataValidator.reset().parameter(AADHAAR_NUMBER).value(aadhaar)
|
||||
.failWithCode("invalid.format", "Aadhaar number must be exactly 12 digits");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractStringNamed(PAN_NUMBER, element) != null) {
|
||||
final String pan = this.fromApiJsonHelper.extractStringNamed(PAN_NUMBER, element);
|
||||
baseDataValidator.reset().parameter(PAN_NUMBER).value(pan).notExceedingLengthOf(10);
|
||||
if (pan != null && !pan.isEmpty() && !PAN_PATTERN.matcher(pan).matches()) {
|
||||
baseDataValidator.reset().parameter(PAN_NUMBER).value(pan)
|
||||
.failWithCode("invalid.format", "PAN must match format ABCDE1234F");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractStringNamed(NATIONALITY, element) != null) {
|
||||
final String nationality = this.fromApiJsonHelper.extractStringNamed(NATIONALITY, element);
|
||||
baseDataValidator.reset().parameter(NATIONALITY).value(nationality).notExceedingLengthOf(50);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractStringNamed(MARRIAGE_AUTHORITY, element) != null) {
|
||||
final String authority = this.fromApiJsonHelper.extractStringNamed(MARRIAGE_AUTHORITY, element);
|
||||
baseDataValidator.reset().parameter(MARRIAGE_AUTHORITY).value(authority).notExceedingLengthOf(50);
|
||||
if (authority != null && !authority.isEmpty() && !VALID_MARRIAGE_AUTHORITIES.contains(authority)) {
|
||||
baseDataValidator.reset().parameter(MARRIAGE_AUTHORITY).value(authority)
|
||||
.failWithCode("invalid.value", "Must be one of: " + VALID_MARRIAGE_AUTHORITIES);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractLongNamed(LINKED_CLIENT_ID, element) != null) {
|
||||
final long linkedId = this.fromApiJsonHelper.extractLongNamed(LINKED_CLIENT_ID, element);
|
||||
baseDataValidator.reset().parameter(LINKED_CLIENT_ID).value(linkedId).longGreaterThanZero();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void validateForUpdate(final long familyMemberId, String json) {
|
||||
@@ -292,6 +345,44 @@ public final class ClientFamilyMemberCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter(REMARKS).value(remarks).notExceedingLengthOf(500);
|
||||
}
|
||||
|
||||
// CBS extended field validations
|
||||
if (this.fromApiJsonHelper.extractStringNamed(AADHAAR_NUMBER, element) != null) {
|
||||
final String aadhaar = this.fromApiJsonHelper.extractStringNamed(AADHAAR_NUMBER, element);
|
||||
baseDataValidator.reset().parameter(AADHAAR_NUMBER).value(aadhaar).notExceedingLengthOf(12);
|
||||
if (aadhaar != null && !aadhaar.isEmpty() && !AADHAAR_PATTERN.matcher(aadhaar).matches()) {
|
||||
baseDataValidator.reset().parameter(AADHAAR_NUMBER).value(aadhaar)
|
||||
.failWithCode("invalid.format", "Aadhaar number must be exactly 12 digits");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractStringNamed(PAN_NUMBER, element) != null) {
|
||||
final String pan = this.fromApiJsonHelper.extractStringNamed(PAN_NUMBER, element);
|
||||
baseDataValidator.reset().parameter(PAN_NUMBER).value(pan).notExceedingLengthOf(10);
|
||||
if (pan != null && !pan.isEmpty() && !PAN_PATTERN.matcher(pan).matches()) {
|
||||
baseDataValidator.reset().parameter(PAN_NUMBER).value(pan)
|
||||
.failWithCode("invalid.format", "PAN must match format ABCDE1234F");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractStringNamed(NATIONALITY, element) != null) {
|
||||
final String nationality = this.fromApiJsonHelper.extractStringNamed(NATIONALITY, element);
|
||||
baseDataValidator.reset().parameter(NATIONALITY).value(nationality).notExceedingLengthOf(50);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractStringNamed(MARRIAGE_AUTHORITY, element) != null) {
|
||||
final String authority = this.fromApiJsonHelper.extractStringNamed(MARRIAGE_AUTHORITY, element);
|
||||
baseDataValidator.reset().parameter(MARRIAGE_AUTHORITY).value(authority).notExceedingLengthOf(50);
|
||||
if (authority != null && !authority.isEmpty() && !VALID_MARRIAGE_AUTHORITIES.contains(authority)) {
|
||||
baseDataValidator.reset().parameter(MARRIAGE_AUTHORITY).value(authority)
|
||||
.failWithCode("invalid.value", "Must be one of: " + VALID_MARRIAGE_AUTHORITIES);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.extractLongNamed(LINKED_CLIENT_ID, element) != null) {
|
||||
final long linkedId = this.fromApiJsonHelper.extractLongNamed(LINKED_CLIENT_ID, element);
|
||||
baseDataValidator.reset().parameter(LINKED_CLIENT_ID).value(linkedId).longGreaterThanZero();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void validateForDelete(final long familyMemberId) {
|
||||
|
||||
+20
-3
@@ -57,10 +57,15 @@ public class ClientFamilyMembersReadPlatformServiceImpl implements ClientFamilyM
|
||||
+ "fmb.lastname AS lastName,fmb.qualification AS qualification,fmb.mobile_number as mobileNumber,fmb.age as age,fmb.is_dependent as isDependent,cv.code_value AS relationship,fmb.relationship_cv_id AS relationshipId,"
|
||||
+ "c.code_value AS maritalStatus,fmb.marital_status_cv_id AS maritalStatusId,"
|
||||
+ "c1.code_value AS gender, fmb.gender_cv_id AS genderId, fmb.date_of_birth AS dateOfBirth, c2.code_value AS profession, fmb.profession_cv_id AS professionId,"
|
||||
+ "fmb.nomination_type AS nominationType, fmb.percentage_share AS percentageShare, fmb.remarks AS remarks"
|
||||
+ "fmb.nomination_type AS nominationType, fmb.percentage_share AS percentageShare, fmb.remarks AS remarks,"
|
||||
+ "fmb.aadhaar_number AS aadhaarNumber, fmb.pan_number AS panNumber, fmb.nationality AS nationality,"
|
||||
+ "fmb.is_alive AS isAlive, fmb.linked_client_id AS linkedClientId, fmb.marriage_date AS marriageDate,"
|
||||
+ "fmb.marriage_authority AS marriageAuthority,"
|
||||
+ "lc.display_name AS linkedClientName, lc.cif_number AS linkedClientCif"
|
||||
+ " FROM m_family_members fmb" + " LEFT JOIN m_code_value cv ON fmb.relationship_cv_id=cv.id"
|
||||
+ " LEFT JOIN m_code_value c ON fmb.marital_status_cv_id=c.id" + " LEFT JOIN m_code_value c1 ON fmb.gender_cv_id=c1.id"
|
||||
+ " LEFT JOIN m_code_value c2 ON fmb.profession_cv_id=c2.id";
|
||||
+ " LEFT JOIN m_code_value c2 ON fmb.profession_cv_id=c2.id"
|
||||
+ " LEFT JOIN m_client lc ON fmb.linked_client_id=lc.id";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,12 +91,24 @@ public class ClientFamilyMembersReadPlatformServiceImpl implements ClientFamilyM
|
||||
final String nominationType = rs.getString("nominationType");
|
||||
final BigDecimal percentageShare = rs.getBigDecimal("percentageShare");
|
||||
final String remarks = rs.getString("remarks");
|
||||
final String aadhaarNumber = rs.getString("aadhaarNumber");
|
||||
final String panNumber = rs.getString("panNumber");
|
||||
final String nationality = rs.getString("nationality");
|
||||
final Boolean isAlive = rs.getObject("isAlive") != null ? rs.getBoolean("isAlive") : null;
|
||||
final Long linkedClientId = JdbcSupport.getLong(rs, "linkedClientId");
|
||||
final String linkedClientName = rs.getString("linkedClientName");
|
||||
final String linkedClientCif = rs.getString("linkedClientCif");
|
||||
final LocalDate marriageDate = JdbcSupport.getLocalDate(rs, "marriageDate");
|
||||
final String marriageAuthority = rs.getString("marriageAuthority");
|
||||
|
||||
return ClientFamilyMembersData.builder().id(id).clientId(clientId).firstName(firstName).middleName(middleName)
|
||||
.lastName(lastName).qualification(qualification).mobileNumber(mobileNumber).age(age).isDependent(isDependent)
|
||||
.relationship(relationship).relationshipId(relationshipId).maritalStatus(maritalStatus).maritalStatusId(maritalStatusId)
|
||||
.gender(gender).genderId(genderId).dateOfBirth(dateOfBirth).profession(profession).professionId(professionId)
|
||||
.nominationType(nominationType).percentageShare(percentageShare).remarks(remarks).build();
|
||||
.nominationType(nominationType).percentageShare(percentageShare).remarks(remarks)
|
||||
.aadhaarNumber(aadhaarNumber).panNumber(panNumber).nationality(nationality)
|
||||
.isAlive(isAlive).linkedClientId(linkedClientId).linkedClientName(linkedClientName)
|
||||
.linkedClientCif(linkedClientCif).marriageDate(marriageDate).marriageAuthority(marriageAuthority).build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+68
-4
@@ -129,6 +129,19 @@ public class ClientFamilyMembersWritePlatformServiceImpl implements ClientFamily
|
||||
mobileNumber, age, isDependent, relationship, maritalStatus, gender, dateOfBirth, profession,
|
||||
nominationType, percentageShare, remarks);
|
||||
|
||||
// CBS extended fields
|
||||
clientFamilyMembers.setAadhaarNumber(command.stringValueOfParameterNamed("aadhaarNumber"));
|
||||
clientFamilyMembers.setPanNumber(command.stringValueOfParameterNamed("panNumber"));
|
||||
clientFamilyMembers.setNationality(command.stringValueOfParameterNamed("nationality"));
|
||||
final Boolean isAlive = command.booleanObjectValueOfParameterNamed("isAlive");
|
||||
clientFamilyMembers.setIsAlive(isAlive != null ? isAlive : true);
|
||||
clientFamilyMembers.setMarriageDate(command.localDateValueOfParameterNamed("marriageDate"));
|
||||
clientFamilyMembers.setMarriageAuthority(command.stringValueOfParameterNamed("marriageAuthority"));
|
||||
final Long linkedClientId = command.longValueOfParameterNamed("linkedClientId");
|
||||
if (linkedClientId != null) {
|
||||
clientFamilyMembers.setLinkedClient(clientRepositoryWrapper.findOneWithNotFoundDetection(linkedClientId));
|
||||
}
|
||||
|
||||
this.clientFamilyRepository.saveAndFlush(clientFamilyMembers);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(clientFamilyMembers.getId()).build();
|
||||
@@ -258,6 +271,34 @@ public class ClientFamilyMembersWritePlatformServiceImpl implements ClientFamily
|
||||
isDependent, relationship, maritalStatus, gender, dateOfBirth, profession,
|
||||
nominationType, percentageShare, remarks);
|
||||
|
||||
// CBS extended fields
|
||||
if (member.get("aadhaarNumber") != null && !member.get("aadhaarNumber").isJsonNull()) {
|
||||
familyMember.setAadhaarNumber(member.get("aadhaarNumber").getAsString());
|
||||
}
|
||||
if (member.get("panNumber") != null && !member.get("panNumber").isJsonNull()) {
|
||||
familyMember.setPanNumber(member.get("panNumber").getAsString());
|
||||
}
|
||||
if (member.get("nationality") != null && !member.get("nationality").isJsonNull()) {
|
||||
familyMember.setNationality(member.get("nationality").getAsString());
|
||||
}
|
||||
if (member.get("isAlive") != null && !member.get("isAlive").isJsonNull()) {
|
||||
familyMember.setIsAlive(member.get("isAlive").getAsBoolean());
|
||||
}
|
||||
if (member.get("linkedClientId") != null && !member.get("linkedClientId").isJsonNull()) {
|
||||
familyMember.setLinkedClient(this.clientRepositoryWrapper.findOneWithNotFoundDetection(member.get("linkedClientId").getAsLong()));
|
||||
}
|
||||
if (member.get("marriageDate") != null && !member.get("marriageDate").isJsonNull()) {
|
||||
try {
|
||||
DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendPattern(member.get("dateFormat").getAsString()).toFormatter();
|
||||
familyMember.setMarriageDate(LocalDate.parse(member.get("marriageDate").getAsString(), fmt));
|
||||
} catch (DateTimeParseException e) {
|
||||
LOG.warn("Could not parse marriageDate", e);
|
||||
}
|
||||
}
|
||||
if (member.get("marriageAuthority") != null && !member.get("marriageAuthority").isJsonNull()) {
|
||||
familyMember.setMarriageAuthority(member.get("marriageAuthority").getAsString());
|
||||
}
|
||||
|
||||
this.clientFamilyRepository.saveAndFlush(familyMember);
|
||||
|
||||
}
|
||||
@@ -378,10 +419,33 @@ public class ClientFamilyMembersWritePlatformServiceImpl implements ClientFamily
|
||||
clientFamilyMember.setRemarks(command.stringValueOfParameterNamed("remarks"));
|
||||
}
|
||||
|
||||
// ClientFamilyMembers
|
||||
// clientFamilyMembers=ClientFamilyMembers.fromJson(client, firstName,
|
||||
// middleName, lastName, qualification, relationship, maritalStatus,
|
||||
// gender, dateOfBirth, profession);
|
||||
// CBS extended fields
|
||||
if (command.parameterExists("aadhaarNumber")) {
|
||||
clientFamilyMember.setAadhaarNumber(command.stringValueOfParameterNamed("aadhaarNumber"));
|
||||
}
|
||||
if (command.parameterExists("panNumber")) {
|
||||
clientFamilyMember.setPanNumber(command.stringValueOfParameterNamed("panNumber"));
|
||||
}
|
||||
if (command.parameterExists("nationality")) {
|
||||
clientFamilyMember.setNationality(command.stringValueOfParameterNamed("nationality"));
|
||||
}
|
||||
if (command.parameterExists("isAlive")) {
|
||||
clientFamilyMember.setIsAlive(command.booleanObjectValueOfParameterNamed("isAlive"));
|
||||
}
|
||||
if (command.parameterExists("marriageDate")) {
|
||||
clientFamilyMember.setMarriageDate(command.localDateValueOfParameterNamed("marriageDate"));
|
||||
}
|
||||
if (command.parameterExists("marriageAuthority")) {
|
||||
clientFamilyMember.setMarriageAuthority(command.stringValueOfParameterNamed("marriageAuthority"));
|
||||
}
|
||||
if (command.parameterExists("linkedClientId")) {
|
||||
final Long linkedId = command.longValueOfParameterNamed("linkedClientId");
|
||||
if (linkedId != null) {
|
||||
clientFamilyMember.setLinkedClient(this.clientRepositoryWrapper.findOneWithNotFoundDetection(linkedId));
|
||||
} else {
|
||||
clientFamilyMember.setLinkedClient(null);
|
||||
}
|
||||
}
|
||||
|
||||
this.clientFamilyRepository.saveAndFlush(clientFamilyMember);
|
||||
|
||||
|
||||
+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.client.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.client.data.ClientForm60Data;
|
||||
|
||||
public interface ClientForm60ReadPlatformService {
|
||||
|
||||
List<ClientForm60Data> retrieveAllByClientId(Long clientId);
|
||||
|
||||
ClientForm60Data retrieveOne(Long clientId, Long form60Id);
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.client.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.client.data.ClientForm60Data;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientForm60;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientForm60Repository;
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class ClientForm60ReadPlatformServiceImpl implements ClientForm60ReadPlatformService {
|
||||
|
||||
private final ClientForm60Repository form60Repository;
|
||||
|
||||
@Override
|
||||
public List<ClientForm60Data> retrieveAllByClientId(final Long clientId) {
|
||||
return form60Repository.findByClientId(clientId).stream()
|
||||
.map(this::toData)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientForm60Data retrieveOne(final Long clientId, final Long form60Id) {
|
||||
final ClientForm60 form60 = form60Repository.findByIdAndClientId(form60Id, clientId)
|
||||
.orElseThrow(() -> new AbstractPlatformResourceNotFoundException("error.msg.client.form60.not.found",
|
||||
"Form 60 record with identifier " + form60Id + " does not exist", form60Id) {});
|
||||
return toData(form60);
|
||||
}
|
||||
|
||||
private ClientForm60Data toData(final ClientForm60 f) {
|
||||
return ClientForm60Data.builder()
|
||||
.id(f.getId())
|
||||
.clientId(f.getClient().getId())
|
||||
.clientDisplayName(f.getClient().getDisplayName())
|
||||
.fullName(f.getFullName())
|
||||
.dateOfBirth(f.getDateOfBirth())
|
||||
.fatherName(f.getFatherName())
|
||||
.address(f.getAddress())
|
||||
.transactionType(f.getTransactionType())
|
||||
.transactionAmount(f.getTransactionAmount())
|
||||
.estimatedTotalIncome(f.getEstimatedTotalIncome())
|
||||
.reasonForNoPan(f.getReasonForNoPan())
|
||||
.declaration(f.isDeclaration())
|
||||
.submittedOn(f.getSubmittedOn())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+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.client.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface ClientForm60WritePlatformService {
|
||||
|
||||
CommandProcessingResult createForm60(Long clientId, JsonCommand command);
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.client.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientForm60;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientForm60Repository;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ClientForm60WritePlatformServiceImpl implements ClientForm60WritePlatformService {
|
||||
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final ClientForm60Repository form60Repository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult createForm60(final Long clientId, final JsonCommand command) {
|
||||
final Client client = clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
final ClientForm60 form60 = ClientForm60.fromJson(client, command);
|
||||
form60Repository.saveAndFlush(form60);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(form60.getId())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+27
-2
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.client.service;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.client.data.ClientKycData;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientKyc;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientKycRepository;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientKycRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientKycStatus;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientKycType;
|
||||
@@ -34,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
public class ClientKycReadPlatformServiceImpl implements ClientKycReadPlatformService {
|
||||
|
||||
private final ClientKycRepositoryWrapper kycRepositoryWrapper;
|
||||
private final ClientKycRepository kycRepository;
|
||||
|
||||
@Override
|
||||
public ClientKycData retrieveClientKyc(final Long kycId) {
|
||||
@@ -43,8 +45,9 @@ public class ClientKycReadPlatformServiceImpl implements ClientKycReadPlatformSe
|
||||
|
||||
@Override
|
||||
public ClientKycData retrieveClientKycByClientId(final Long clientId) {
|
||||
final ClientKyc kyc = kycRepositoryWrapper.findByClientIdWithNotFoundDetection(clientId);
|
||||
return toData(kyc);
|
||||
return kycRepository.findByClientId(clientId)
|
||||
.map(this::toData)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private ClientKycData toData(final ClientKyc kyc) {
|
||||
@@ -66,6 +69,8 @@ public class ClientKycReadPlatformServiceImpl implements ClientKycReadPlatformSe
|
||||
.panVerified(kyc.isPanVerified())
|
||||
.panVerifiedOn(kyc.getPanVerifiedOn())
|
||||
.panFullName(kyc.getPanFullName())
|
||||
.panFirstName(kyc.getPanFirstName())
|
||||
.panLastName(kyc.getPanLastName())
|
||||
.panAadhaarSeedingStatus(kyc.getPanAadhaarSeedingStatus())
|
||||
.panCategory(kyc.getPanCategory())
|
||||
.panStatus(kyc.getPanStatus())
|
||||
@@ -75,6 +80,26 @@ public class ClientKycReadPlatformServiceImpl implements ClientKycReadPlatformSe
|
||||
.gstinNumber(kyc.getGstinNumber())
|
||||
.gstinVerified(kyc.isGstinVerified())
|
||||
.gstinVerifiedOn(kyc.getGstinVerifiedOn())
|
||||
.enterprisePan(kyc.getEnterprisePan())
|
||||
.cinNumber(kyc.getCinNumber())
|
||||
.incorporationDate(kyc.getIncorporationDate())
|
||||
.kycDocumentName(kyc.getKycDocumentName())
|
||||
.kycDocumentNumber(kyc.getKycDocumentNumber())
|
||||
.kycDocIssueDate(kyc.getKycDocIssueDate())
|
||||
.kycDocExpiryDate(kyc.getKycDocExpiryDate())
|
||||
.amlCheckStatus(kyc.getAmlCheckStatus())
|
||||
.bannedEntityMatch(kyc.getBannedEntityMatch())
|
||||
.willfulDefaulter(kyc.getWillfulDefaulter())
|
||||
.ecgcSalFlag(kyc.getEcgcSalFlag())
|
||||
.dormantFlag(kyc.getDormantFlag())
|
||||
.accountStatus(kyc.getAccountStatus())
|
||||
.freezeReason(kyc.getFreezeReason())
|
||||
.freezeAuthority(kyc.getFreezeAuthority())
|
||||
.form60Type(kyc.getForm60Type())
|
||||
.form60Date(kyc.getForm60Date())
|
||||
.form15Applicable(kyc.getForm15Applicable())
|
||||
.form15Type(kyc.getForm15Type())
|
||||
.form15Fy(kyc.getForm15Fy())
|
||||
.ckycNumber(kyc.getCkycNumber())
|
||||
.initiatedOn(kyc.getInitiatedOn())
|
||||
.verifiedOn(kyc.getVerifiedOn())
|
||||
|
||||
+10
@@ -69,6 +69,16 @@ public interface ClientKycWritePlatformService {
|
||||
*/
|
||||
CommandProcessingResult verifyGstin(Long clientId, Long kycId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Verify CIN (Corporate Identification Number) via LincPay MCA API and mark cinVerified on the KYC record.
|
||||
*/
|
||||
CommandProcessingResult verifyCin(Long clientId, Long kycId, JsonCommand command);
|
||||
|
||||
/**
|
||||
* Verify DIN (Director Identification Number) via LincPay MCA API and mark dinVerified on the KYC record.
|
||||
*/
|
||||
CommandProcessingResult verifyDin(Long clientId, Long kycId, JsonCommand command);
|
||||
|
||||
/** Reject the KYC; stores a rejection reason. */
|
||||
CommandProcessingResult rejectKyc(Long clientId, Long kycId, JsonCommand command);
|
||||
}
|
||||
|
||||
+191
-21
@@ -31,6 +31,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.command.ClientKycCommand;
|
||||
import org.apache.fineract.portfolio.client.data.ClientKycApiConstants;
|
||||
@@ -62,18 +63,26 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
private final ClientKycRepositoryWrapper kycRepositoryWrapper;
|
||||
private final ClientKycCommandFromApiJsonDeserializer deserializer;
|
||||
private final KycVerificationService kycVerificationService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult initiateKyc(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
LOG.info("initiateKyc: clientId={}, requestJson={}", clientId, command.json());
|
||||
|
||||
final ClientKycCommand kycCommand = deserializer.commandFromApiJson(
|
||||
command.json(), ClientKycApiConstants.INITIATE_REQUEST_PARAMS);
|
||||
kycCommand.validateForInitiate();
|
||||
|
||||
if (kycRepositoryWrapper.existsByClientId(clientId)) {
|
||||
throw new ClientKycAlreadyExistsException(clientId);
|
||||
LOG.info("KYC record already exists for client {}. Returning existing record.", clientId);
|
||||
final ClientKyc existing = kycRepositoryWrapper.findByClientIdWithNotFoundDetection(clientId);
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(existing.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -186,12 +195,43 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
if (Boolean.TRUE.equals(kycCommand.getVerifyPan()) && kyc.getPanNumber() != null) {
|
||||
final String panDob = client.dateOfBirth() != null
|
||||
? client.dateOfBirth().format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) : null;
|
||||
// Build name without salutation: firstname + middlename (if not blank) + lastname
|
||||
final StringBuilder panName = new StringBuilder();
|
||||
if (client.getFirstname() != null && !client.getFirstname().isBlank()) {
|
||||
panName.append(client.getFirstname().trim());
|
||||
}
|
||||
if (client.getMiddlename() != null && !client.getMiddlename().isBlank()) {
|
||||
panName.append(" ").append(client.getMiddlename().trim());
|
||||
}
|
||||
if (client.getLastname() != null && !client.getLastname().isBlank()) {
|
||||
panName.append(" ").append(client.getLastname().trim());
|
||||
}
|
||||
final String clientNameForPan = panName.toString().trim();
|
||||
LOG.info("verifyPan: using name='{}' (without salutation) for PAN verification", clientNameForPan);
|
||||
final KycVerificationResult result = kycVerificationService.verifyPan(
|
||||
kyc.getPanNumber(), client.getDisplayName(), panDob);
|
||||
kyc.getPanNumber(), clientNameForPan, panDob);
|
||||
if (result.isSuccess()) {
|
||||
final JsonObject panData = extractPanData(result.getRawResponse());
|
||||
kyc.markPanVerified(
|
||||
getJsonString(panData, "full_name"),
|
||||
final String fullName = getJsonString(panData, "full_name");
|
||||
final String firstName = getJsonString(panData, "first_name");
|
||||
final String lastName = getJsonString(panData, "last_name");
|
||||
LOG.info("verifyKyc PAN: full_name=[{}], first_name=[{}], last_name=[{}]", fullName, firstName, lastName);
|
||||
|
||||
// If LincPay returns empty names, the client's name/DOB does not match the PAN holder
|
||||
if (fullName == null || fullName.isBlank()) {
|
||||
LOG.warn("PAN verification name mismatch for client {}: LincPay returned empty full_name. "
|
||||
+ "Client name '{}' or DOB does not match PAN holder.", clientId, client.getDisplayName());
|
||||
kyc.markRejected("PAN verification failed: Client name or date of birth does not match the PAN card holder. "
|
||||
+ "Please ensure the client's name and date of birth exactly match the PAN card details.");
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
throw new PlatformServiceUnavailableException(
|
||||
"error.msg.client.kyc.pan.name.mismatch",
|
||||
"PAN verification failed: Client name or date of birth does not match the PAN card holder. "
|
||||
+ "Please ensure the client's name and date of birth exactly match the PAN card details.",
|
||||
client.getDisplayName());
|
||||
}
|
||||
|
||||
kyc.markPanVerified(fullName, firstName, lastName,
|
||||
getJsonString(panData, "aadhaar_seeding_status"),
|
||||
getJsonString(panData, "category"),
|
||||
getJsonString(panData, "status"));
|
||||
@@ -266,11 +306,23 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
final String dob = client.dateOfBirth() != null
|
||||
? client.dateOfBirth().format(DateTimeFormatter.ofPattern("dd/MM/yyyy"))
|
||||
: null;
|
||||
// Build name without salutation: firstname + middlename (if not blank) + lastname
|
||||
final StringBuilder panName = new StringBuilder();
|
||||
if (client.getFirstname() != null && !client.getFirstname().isBlank()) {
|
||||
panName.append(client.getFirstname().trim());
|
||||
}
|
||||
if (client.getMiddlename() != null && !client.getMiddlename().isBlank()) {
|
||||
panName.append(" ").append(client.getMiddlename().trim());
|
||||
}
|
||||
if (client.getLastname() != null && !client.getLastname().isBlank()) {
|
||||
panName.append(" ").append(client.getLastname().trim());
|
||||
}
|
||||
final String clientNameForPan = panName.toString().trim();
|
||||
LOG.info("verifyPan: calling LincPay with panNumber='{}', name='{}', dob='{}'",
|
||||
kyc.getPanNumber(), client.getDisplayName(), dob);
|
||||
kyc.getPanNumber(), clientNameForPan, dob);
|
||||
|
||||
final KycVerificationResult result = kycVerificationService.verifyPan(
|
||||
kyc.getPanNumber(), client.getDisplayName(), dob);
|
||||
kyc.getPanNumber(), clientNameForPan, dob);
|
||||
|
||||
LOG.info("verifyPan: LincPay result success={}, referenceId={}, errorMessage={}, rawResponse={}",
|
||||
result.isSuccess(), result.getReferenceId(), result.getErrorMessage(), result.getRawResponse());
|
||||
@@ -285,16 +337,40 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
}
|
||||
|
||||
final JsonObject panData = extractPanData(result.getRawResponse());
|
||||
kyc.markPanVerified(
|
||||
getJsonString(panData, "full_name"),
|
||||
final String fullName = getJsonString(panData, "full_name");
|
||||
|
||||
// If LincPay returns empty names, the client's name/DOB does not match the PAN holder
|
||||
if (fullName == null || fullName.isBlank()) {
|
||||
LOG.error("verifyPan: Name mismatch for clientId={}, kycId={}. Client name '{}' or DOB does not match PAN holder.",
|
||||
clientId, kycId, client.getDisplayName());
|
||||
throw new PlatformServiceUnavailableException(
|
||||
"error.msg.client.kyc.pan.name.mismatch",
|
||||
"PAN verification failed: Client name or date of birth does not match the PAN card holder. "
|
||||
+ "Please ensure the client's name and date of birth exactly match the PAN card details.",
|
||||
client.getDisplayName());
|
||||
}
|
||||
|
||||
kyc.markPanVerified(fullName,
|
||||
getJsonString(panData, "first_name"),
|
||||
getJsonString(panData, "last_name"),
|
||||
getJsonString(panData, "aadhaar_seeding_status"),
|
||||
getJsonString(panData, "category"),
|
||||
getJsonString(panData, "status"));
|
||||
kyc.tryAutoVerify();
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
LOG.info("verifyPan: SUCCESS for clientId={}, kycId={}", clientId, kycId);
|
||||
LOG.info("verifyPan: SUCCESS for clientId={}, kycId={}, kycStatus={}", clientId, kycId, kyc.getKycStatusValue());
|
||||
|
||||
return buildResult(command, client, clientId, kycId,
|
||||
Map.of("panVerified", true, "panVerifiedOn", kyc.getPanVerifiedOn()));
|
||||
final Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("panVerified", true);
|
||||
changes.put("panFullName", kyc.getPanFullName() != null ? kyc.getPanFullName() : "");
|
||||
changes.put("panFirstName", kyc.getPanFirstName() != null ? kyc.getPanFirstName() : "");
|
||||
changes.put("panLastName", kyc.getPanLastName() != null ? kyc.getPanLastName() : "");
|
||||
changes.put("panAadhaarSeedingStatus", kyc.getPanAadhaarSeedingStatus() != null ? kyc.getPanAadhaarSeedingStatus() : "");
|
||||
changes.put("panCategory", kyc.getPanCategory() != null ? kyc.getPanCategory() : "");
|
||||
changes.put("panStatus", kyc.getPanStatus() != null ? kyc.getPanStatus() : "");
|
||||
changes.put("panVerifiedOn", kyc.getPanVerifiedOn());
|
||||
changes.put("kycStatus", kyc.getKycStatusValue());
|
||||
return buildResult(command, client, clientId, kycId, changes);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -324,10 +400,14 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
}
|
||||
|
||||
kyc.markAadhaarVerified();
|
||||
kyc.tryAutoVerify();
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
|
||||
return buildResult(command, client, clientId, kycId,
|
||||
Map.of("aadhaarVerified", true, "aadhaarVerifiedOn", kyc.getAadhaarVerifiedOn()));
|
||||
final Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("aadhaarVerified", true);
|
||||
changes.put("aadhaarVerifiedOn", kyc.getAadhaarVerifiedOn());
|
||||
changes.put("kycStatus", kyc.getKycStatusValue());
|
||||
return buildResult(command, client, clientId, kycId, changes);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -362,10 +442,98 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
}
|
||||
|
||||
kyc.markGstinVerified();
|
||||
kyc.tryAutoVerify();
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
|
||||
return buildResult(command, client, clientId, kycId,
|
||||
Map.of("gstinVerified", true, "gstinVerifiedOn", kyc.getGstinVerifiedOn()));
|
||||
final Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("gstinVerified", true);
|
||||
changes.put("gstinVerifiedOn", kyc.getGstinVerifiedOn());
|
||||
changes.put("kycStatus", kyc.getKycStatusValue());
|
||||
return buildResult(command, client, clientId, kycId, changes);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult verifyCin(final Long clientId, final Long kycId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ClientKycCommand kycCommand = deserializer.commandFromApiJson(
|
||||
command.json(), ClientKycApiConstants.VERIFY_CIN_REQUEST_PARAMS);
|
||||
kycCommand.validateForVerifyCin();
|
||||
|
||||
final Client client = clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
final ClientKyc kyc = kycRepositoryWrapper.findOneWithNotFoundDetection(kycId);
|
||||
|
||||
if (kyc.getKycStatus().isRejected()) {
|
||||
throw new ClientKycInvalidStateTransitionException("verify-cin", kyc.getKycStatus().name());
|
||||
}
|
||||
|
||||
if (kyc.getCinNumber() == null) {
|
||||
throw new PlatformServiceUnavailableException(
|
||||
"error.msg.client.kyc.cin.not.found",
|
||||
"CIN number is not set on this KYC record. Update the KYC record first.");
|
||||
}
|
||||
|
||||
final KycVerificationResult result = kycVerificationService.verifyCin(kyc.getCinNumber());
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
throw new PlatformServiceUnavailableException(
|
||||
"error.msg.client.kyc.cin.verification.failed",
|
||||
"CIN verification failed: " + result.getErrorMessage(),
|
||||
result.getErrorMessage());
|
||||
}
|
||||
|
||||
kyc.markCinVerified();
|
||||
kyc.tryAutoVerify();
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
|
||||
final Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("cinVerified", true);
|
||||
changes.put("cinVerifiedOn", kyc.getCinVerifiedOn());
|
||||
changes.put("kycStatus", kyc.getKycStatusValue());
|
||||
return buildResult(command, client, clientId, kycId, changes);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult verifyDin(final Long clientId, final Long kycId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ClientKycCommand kycCommand = deserializer.commandFromApiJson(
|
||||
command.json(), ClientKycApiConstants.VERIFY_DIN_REQUEST_PARAMS);
|
||||
kycCommand.validateForVerifyDin();
|
||||
|
||||
final Client client = clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
final ClientKyc kyc = kycRepositoryWrapper.findOneWithNotFoundDetection(kycId);
|
||||
|
||||
if (kyc.getKycStatus().isRejected()) {
|
||||
throw new ClientKycInvalidStateTransitionException("verify-din", kyc.getKycStatus().name());
|
||||
}
|
||||
|
||||
if (kyc.getDinNumber() == null) {
|
||||
throw new PlatformServiceUnavailableException(
|
||||
"error.msg.client.kyc.din.not.found",
|
||||
"DIN number is not set on this KYC record. Update the KYC record first.");
|
||||
}
|
||||
|
||||
final KycVerificationResult result = kycVerificationService.verifyDin(kyc.getDinNumber());
|
||||
|
||||
if (!result.isSuccess()) {
|
||||
throw new PlatformServiceUnavailableException(
|
||||
"error.msg.client.kyc.din.verification.failed",
|
||||
"DIN verification failed: " + result.getErrorMessage(),
|
||||
result.getErrorMessage());
|
||||
}
|
||||
|
||||
kyc.markDinVerified();
|
||||
kyc.tryAutoVerify();
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
|
||||
final Map<String, Object> changes = new java.util.LinkedHashMap<>();
|
||||
changes.put("dinVerified", true);
|
||||
changes.put("dinVerifiedOn", kyc.getDinVerifiedOn());
|
||||
changes.put("kycStatus", kyc.getKycStatusValue());
|
||||
return buildResult(command, client, clientId, kycId, changes);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -398,13 +566,14 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
kycCommand.validateForInitiate();
|
||||
|
||||
if (kycRepositoryWrapper.existsByClientId(client.getId())) {
|
||||
throw new ClientKycAlreadyExistsException(client.getId());
|
||||
LOG.info("KYC record already exists for client {}. Skipping inline initiation.", client.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
final ClientKyc kyc = ClientKyc.initiateKyc(client,
|
||||
kycCommand.getKycType(), kycCommand.getRiskCategory(),
|
||||
kycCommand.getPanNumber(), kycCommand.getAadhaarNumberMasked(),
|
||||
kycCommand.getGstinNumber(), kycCommand.getRemarks());
|
||||
final JsonElement parsedJson = this.fromApiJsonHelper.parse(kycJson);
|
||||
final JsonCommand jsonCommand = JsonCommand.from(kycJson, parsedJson, this.fromApiJsonHelper,
|
||||
"CLIENTKYC", null, null, null, client.getId(), null, null, null, null, null, null, null, null, null);
|
||||
final ClientKyc kyc = ClientKyc.initiateKyc(client, jsonCommand);
|
||||
kycRepositoryWrapper.saveAndFlush(kyc);
|
||||
}
|
||||
|
||||
@@ -453,7 +622,8 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc
|
||||
&& cause.getMessage().contains("uq_m_client_kyc_client_id")) {
|
||||
throw new ClientKycAlreadyExistsException(-1L);
|
||||
}
|
||||
LOG.error("Unexpected data integrity issue in KYC.", dve);
|
||||
LOG.error("Unexpected data integrity issue in KYC. Root cause: {}",
|
||||
cause != null ? cause.getMessage() : "unknown", dve);
|
||||
throw ErrorHandler.getMappable(dve, "error.msg.client.kyc.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with KYC resource.");
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.client.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.client.data.ClientNomineeData;
|
||||
|
||||
public interface ClientNomineeReadPlatformService {
|
||||
|
||||
List<ClientNomineeData> retrieveAllNominees(Long clientId);
|
||||
|
||||
List<ClientNomineeData> retrieveNomineesByAccount(Long clientId, String accountNo, String accountType);
|
||||
|
||||
ClientNomineeData retrieveNominee(Long clientId, Long nomineeId);
|
||||
|
||||
ClientNomineeData retrieveTemplate(Long clientId);
|
||||
|
||||
/** Returns true if the client has at least one savings, current, FD, RD, or loan account */
|
||||
boolean clientHasAnyAccount(Long clientId);
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 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.client.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.portfolio.client.data.ClientNomineeData;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientNominee;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientNomineeRepository;
|
||||
import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class ClientNomineeReadPlatformServiceImpl implements ClientNomineeReadPlatformService {
|
||||
|
||||
private final ClientNomineeRepository nomineeRepository;
|
||||
private final CodeValueReadPlatformService codeValueReadPlatformService;
|
||||
private final SavingsAccountRepository savingsAccountRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
|
||||
@Override
|
||||
public List<ClientNomineeData> retrieveAllNominees(final Long clientId) {
|
||||
final List<ClientNominee> nominees = nomineeRepository.findAllByClientId(clientId);
|
||||
return nominees.stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientNomineeData> retrieveNomineesByAccount(final Long clientId, final String accountNo, final String accountType) {
|
||||
final List<ClientNominee> nominees = nomineeRepository.findByClientIdAndAccountNoAndAccountType(clientId, accountNo, accountType);
|
||||
return nominees.stream().map(this::toData).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientNomineeData retrieveNominee(final Long clientId, final Long nomineeId) {
|
||||
final ClientNominee nominee = nomineeRepository.findByClientIdAndId(clientId, nomineeId)
|
||||
.orElseThrow(() -> new ClientNotFoundException(nomineeId));
|
||||
return toData(nominee);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientNomineeData retrieveTemplate(final Long clientId) {
|
||||
final ClientNomineeData data = new ClientNomineeData();
|
||||
data.setClientId(clientId);
|
||||
final Collection<CodeValueData> relationshipOptions = codeValueReadPlatformService
|
||||
.retrieveCodeValuesByCode("RELATIONSHIP");
|
||||
data.setRelationshipOptions(relationshipOptions);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clientHasAnyAccount(final Long clientId) {
|
||||
// Check savings/current/FD/RD accounts
|
||||
final List<?> savingsAccounts = savingsAccountRepository.findSavingAccountByClientId(clientId);
|
||||
if (savingsAccounts != null && !savingsAccounts.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
// Check loan accounts
|
||||
final List<?> loans = loanRepository.findLoanByClientId(clientId);
|
||||
return loans != null && !loans.isEmpty();
|
||||
}
|
||||
|
||||
private ClientNomineeData toData(final ClientNominee n) {
|
||||
final ClientNomineeData d = new ClientNomineeData();
|
||||
d.setId(n.getId());
|
||||
d.setClientId(n.getClient().getId());
|
||||
|
||||
final Client nominee = n.getNomineeClient();
|
||||
d.setNomineeClientId(nominee.getId());
|
||||
d.setNomineeDisplayName(nominee.getDisplayName());
|
||||
d.setNomineeCifNumber(nominee.getCifNumber());
|
||||
d.setNomineeAccountNo(nominee.getAccountNumber());
|
||||
d.setNomineeDateOfBirth(nominee.getDateOfBirth());
|
||||
d.setNomineeMobileNo(nominee.getMobileNo());
|
||||
|
||||
if (n.getRelationship() != null) {
|
||||
d.setRelationshipId(n.getRelationship().getId());
|
||||
d.setRelationshipName(n.getRelationship().getLabel());
|
||||
}
|
||||
|
||||
d.setPercentageShare(n.getPercentageShare());
|
||||
d.setIsActive(n.getIsActive());
|
||||
d.setNomineeIsMinor(n.getNomineeIsMinor());
|
||||
|
||||
if (n.getGuardianClient() != null) {
|
||||
d.setGuardianClientId(n.getGuardianClient().getId());
|
||||
d.setGuardianDisplayName(n.getGuardianClient().getDisplayName());
|
||||
d.setGuardianCifNumber(n.getGuardianClient().getCifNumber());
|
||||
}
|
||||
if (n.getGuardianRelationship() != null) {
|
||||
d.setGuardianRelationshipId(n.getGuardianRelationship().getId());
|
||||
d.setGuardianRelationshipName(n.getGuardianRelationship().getLabel());
|
||||
}
|
||||
|
||||
d.setEffectiveDate(n.getEffectiveDate());
|
||||
d.setRemarks(n.getRemarks());
|
||||
d.setNomineeIdentifier(n.getNomineeIdentifier());
|
||||
d.setAccountNo(n.getAccountNo());
|
||||
d.setAccountType(n.getAccountType());
|
||||
return d;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.client.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface ClientNomineeWritePlatformService {
|
||||
|
||||
CommandProcessingResult addNominee(Long clientId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult updateNominee(Long clientId, Long nomineeId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteNominee(Long clientId, Long nomineeId);
|
||||
}
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* 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.client.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||
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.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.client.domain.Client;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientNominee;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientNomineeRepository;
|
||||
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ClientNomineeWritePlatformServiceImpl implements ClientNomineeWritePlatformService {
|
||||
|
||||
private static final String RELATIONSHIP_CODE = "RELATIONSHIP";
|
||||
private static final Set<String> VALID_ACCOUNT_TYPES = Set.of("SB", "CB", "FD", "RD", "LA");
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final ClientNomineeRepository nomineeRepository;
|
||||
private final CodeValueRepositoryWrapper codeValueRepository;
|
||||
private final SavingsAccountRepository savingsAccountRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult addNominee(final Long clientId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
|
||||
// ── Account linkage (MANDATORY) ──
|
||||
final String accountNo = command.stringValueOfParameterNamed("accountNo");
|
||||
final String accountType = command.stringValueOfParameterNamed("accountType");
|
||||
validateAccountParams(accountNo, accountType);
|
||||
validateAccountBelongsToClient(clientId, accountNo, accountType);
|
||||
|
||||
// ── Nominee client ──
|
||||
final Long nomineeClientId = command.longValueOfParameterNamed("nomineeClientId");
|
||||
if (nomineeClientId == null) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.client.id.required",
|
||||
"Nominee must be an existing client. nomineeClientId is required.", "nomineeClientId");
|
||||
}
|
||||
if (nomineeClientId.equals(clientId)) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.cannot.be.self",
|
||||
"Client cannot be their own nominee.", "nomineeClientId");
|
||||
}
|
||||
final Client nomineeClient = this.clientRepository.findOneWithNotFoundDetection(nomineeClientId);
|
||||
|
||||
// ── Percentage share validation (per account, not per client) ──
|
||||
final BigDecimal newShare = command.bigDecimalValueOfParameterNamed("percentageShare");
|
||||
validatePercentageShareForAccount(accountNo, accountType, null, newShare);
|
||||
|
||||
final ClientNominee nominee = new ClientNominee();
|
||||
nominee.setClient(client);
|
||||
nominee.setNomineeClient(nomineeClient);
|
||||
nominee.setPercentageShare(newShare);
|
||||
nominee.setIsActive(command.booleanObjectValueOfParameterNamed("isActive") != null
|
||||
? command.booleanObjectValueOfParameterNamed("isActive") : true);
|
||||
nominee.setAccountNo(accountNo);
|
||||
nominee.setAccountType(accountType);
|
||||
nominee.setNomineeIdentifier(generateNomineeIdentifier(accountNo, accountType));
|
||||
|
||||
final Long relationshipId = command.longValueOfParameterNamed("relationshipId");
|
||||
if (relationshipId != null) {
|
||||
final CodeValue relationship = codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
|
||||
RELATIONSHIP_CODE, relationshipId);
|
||||
nominee.setRelationship(relationship);
|
||||
}
|
||||
|
||||
final Boolean nomineeIsMinor = command.booleanObjectValueOfParameterNamed("nomineeIsMinor");
|
||||
nominee.setNomineeIsMinor(nomineeIsMinor != null ? nomineeIsMinor : false);
|
||||
|
||||
if (Boolean.TRUE.equals(nomineeIsMinor)) {
|
||||
final Long guardianClientId = command.longValueOfParameterNamed("guardianClientId");
|
||||
if (guardianClientId != null) {
|
||||
final Client guardian = this.clientRepository.findOneWithNotFoundDetection(guardianClientId);
|
||||
nominee.setGuardianClient(guardian);
|
||||
}
|
||||
final Long guardianRelationshipId = command.longValueOfParameterNamed("guardianRelationshipId");
|
||||
if (guardianRelationshipId != null) {
|
||||
final CodeValue guardianRel = codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
|
||||
RELATIONSHIP_CODE, guardianRelationshipId);
|
||||
nominee.setGuardianRelationship(guardianRel);
|
||||
}
|
||||
}
|
||||
|
||||
nominee.setEffectiveDate(command.localDateValueOfParameterNamed("effectiveDate"));
|
||||
nominee.setRemarks(command.stringValueOfParameterNamed("remarks"));
|
||||
|
||||
nomineeRepository.saveAndFlush(nominee);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(nominee.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult updateNominee(final Long clientId, final Long nomineeId, final JsonCommand command) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final ClientNominee nominee = nomineeRepository.findByClientIdAndId(clientId, nomineeId)
|
||||
.orElseThrow(() -> new ClientNotFoundException(nomineeId));
|
||||
|
||||
final Long nomineeClientId = command.longValueOfParameterNamed("nomineeClientId");
|
||||
if (nomineeClientId != null) {
|
||||
if (nomineeClientId.equals(clientId)) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.cannot.be.self",
|
||||
"Client cannot be their own nominee.", "nomineeClientId");
|
||||
}
|
||||
final Client nomineeClient = this.clientRepository.findOneWithNotFoundDetection(nomineeClientId);
|
||||
nominee.setNomineeClient(nomineeClient);
|
||||
}
|
||||
|
||||
// Percentage share validated per account
|
||||
final BigDecimal newShare = command.bigDecimalValueOfParameterNamed("percentageShare");
|
||||
if (newShare != null) {
|
||||
validatePercentageShareForAccount(nominee.getAccountNo(), nominee.getAccountType(), nomineeId, newShare);
|
||||
nominee.setPercentageShare(newShare);
|
||||
}
|
||||
|
||||
final Long relationshipId = command.longValueOfParameterNamed("relationshipId");
|
||||
if (relationshipId != null) {
|
||||
final CodeValue relationship = codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
|
||||
RELATIONSHIP_CODE, relationshipId);
|
||||
nominee.setRelationship(relationship);
|
||||
}
|
||||
|
||||
if (command.parameterExists("isActive")) {
|
||||
nominee.setIsActive(command.booleanObjectValueOfParameterNamed("isActive"));
|
||||
}
|
||||
|
||||
if (command.parameterExists("nomineeIsMinor")) {
|
||||
final Boolean isMinor = command.booleanObjectValueOfParameterNamed("nomineeIsMinor");
|
||||
nominee.setNomineeIsMinor(isMinor);
|
||||
|
||||
if (Boolean.TRUE.equals(isMinor)) {
|
||||
final Long guardianClientId = command.longValueOfParameterNamed("guardianClientId");
|
||||
if (guardianClientId != null) {
|
||||
nominee.setGuardianClient(this.clientRepository.findOneWithNotFoundDetection(guardianClientId));
|
||||
}
|
||||
final Long guardianRelationshipId = command.longValueOfParameterNamed("guardianRelationshipId");
|
||||
if (guardianRelationshipId != null) {
|
||||
nominee.setGuardianRelationship(codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(
|
||||
RELATIONSHIP_CODE, guardianRelationshipId));
|
||||
}
|
||||
} else {
|
||||
nominee.setGuardianClient(null);
|
||||
nominee.setGuardianRelationship(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.parameterExists("effectiveDate")) {
|
||||
nominee.setEffectiveDate(command.localDateValueOfParameterNamed("effectiveDate"));
|
||||
}
|
||||
if (command.parameterExists("remarks")) {
|
||||
nominee.setRemarks(command.stringValueOfParameterNamed("remarks"));
|
||||
}
|
||||
|
||||
nomineeRepository.saveAndFlush(nominee);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(command.commandId())
|
||||
.withClientId(clientId)
|
||||
.withEntityId(nominee.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult deleteNominee(final Long clientId, final Long nomineeId) {
|
||||
this.context.authenticatedUser();
|
||||
final ClientNominee nominee = nomineeRepository.findByClientIdAndId(clientId, nomineeId)
|
||||
.orElseThrow(() -> new ClientNotFoundException(nomineeId));
|
||||
|
||||
nomineeRepository.delete(nominee);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withClientId(clientId)
|
||||
.withEntityId(nomineeId)
|
||||
.build();
|
||||
}
|
||||
|
||||
// ── Validation helpers ──────────────────────────────────────────────────
|
||||
|
||||
private void validateAccountParams(final String accountNo, final String accountType) {
|
||||
if (accountNo == null || accountNo.isBlank()) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.account.no.required",
|
||||
"accountNo is required. Nominees must be linked to a specific account.", "accountNo");
|
||||
}
|
||||
if (accountType == null || accountType.isBlank()) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.account.type.required",
|
||||
"accountType is required. Must be one of: SB, CB, FD, RD, LA.", "accountType");
|
||||
}
|
||||
if (!VALID_ACCOUNT_TYPES.contains(accountType)) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.account.type.invalid",
|
||||
"Invalid accountType '" + accountType + "'. Must be one of: SB, CB, FD, RD, LA.", "accountType");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the account exists and belongs to the client.
|
||||
* SB/CB/FD/RD → savings account; LA → loan account.
|
||||
*/
|
||||
private void validateAccountBelongsToClient(final Long clientId, final String accountNo, final String accountType) {
|
||||
if ("LA".equals(accountType)) {
|
||||
final List<Loan> clientLoans = loanRepository.findLoanByClientId(clientId);
|
||||
final boolean found = clientLoans.stream()
|
||||
.anyMatch(loan -> accountNo.equals(loan.getAccountNumber()));
|
||||
if (!found) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.loan.account.not.found",
|
||||
"Loan account '" + accountNo + "' not found for client " + clientId + ".", "accountNo");
|
||||
}
|
||||
} else {
|
||||
// SB, CB, FD, RD are all savings accounts with different deposit_type_enum
|
||||
final SavingsAccount sa = savingsAccountRepository.findSavingsAccountByAccountNumber(accountNo);
|
||||
if (sa == null) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.savings.account.not.found",
|
||||
"Account '" + accountNo + "' not found.", "accountNo");
|
||||
}
|
||||
if (sa.getClient() == null || !sa.getClient().getId().equals(clientId)) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.account.does.not.belong.to.client",
|
||||
"Account '" + accountNo + "' does not belong to client " + clientId + ".", "accountNo");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique nominee identifier: NOM-{TYPE}{ACCNO}-{SEQ}
|
||||
* e.g. NOM-SB00123456-01, NOM-CB00123456-02, NOM-LA00123456-01
|
||||
*/
|
||||
private String generateNomineeIdentifier(final String accountNo, final String accountType) {
|
||||
final long existingCount = nomineeRepository.countByAccountNoAndAccountType(accountNo, accountType);
|
||||
final long seq = existingCount + 1;
|
||||
return String.format("NOM-%s%s-%02d", accountType, accountNo, seq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that total percentage share for an account does not exceed 100%.
|
||||
* This is per-account validation, not per-client.
|
||||
*/
|
||||
private void validatePercentageShareForAccount(final String accountNo, final String accountType,
|
||||
final Long excludeNomineeId, final BigDecimal newShare) {
|
||||
if (newShare == null) {
|
||||
return;
|
||||
}
|
||||
final List<ClientNominee> existing = nomineeRepository.findActiveByAccountNoAndAccountType(accountNo, accountType);
|
||||
BigDecimal total = newShare;
|
||||
for (final ClientNominee n : existing) {
|
||||
if (excludeNomineeId != null && excludeNomineeId.equals(n.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (n.getPercentageShare() != null) {
|
||||
total = total.add(n.getPercentageShare());
|
||||
}
|
||||
}
|
||||
if (total.compareTo(new BigDecimal("100")) > 0) {
|
||||
throw new PlatformDataIntegrityException("error.msg.nominee.percentage.exceeds.100",
|
||||
"Total nominee percentage share for account " + accountType + accountNo
|
||||
+ " cannot exceed 100%. Current total would be " + total + "%.",
|
||||
"percentageShare", total);
|
||||
}
|
||||
}
|
||||
}
|
||||
+135
-10
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -308,6 +309,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
|
||||
sqlBuilder.append("c.gender_cv_id as genderId, ");
|
||||
sqlBuilder.append("cv.code_value as genderValue, ");
|
||||
sqlBuilder.append("c.salutation_cv_id as salutationId, ");
|
||||
sqlBuilder.append("cvSalutation.code_value as salutationValue, ");
|
||||
sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
|
||||
sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
|
||||
sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
|
||||
@@ -317,6 +320,22 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append("cvOccupationCategory.code_value as occupationCategoryValue, ");
|
||||
sqlBuilder.append("c.occupation_cv_id as occupationId, ");
|
||||
sqlBuilder.append("cvOccupation.code_value as occupationValue, ");
|
||||
sqlBuilder.append("c.monthly_income as monthlyIncome, ");
|
||||
sqlBuilder.append("c.annual_turnover as annualTurnover, ");
|
||||
sqlBuilder.append("c.nationality as nationality, ");
|
||||
sqlBuilder.append("c.is_minor as isMinor, ");
|
||||
sqlBuilder.append("c.guardian_client_id as guardianClientId, ");
|
||||
sqlBuilder.append("gc.display_name as guardianClientDisplayName, ");
|
||||
sqlBuilder.append("c.guardian_relation as guardianRelation, ");
|
||||
sqlBuilder.append("c.alternate_mobile_no as alternateMobileNo, ");
|
||||
sqlBuilder.append("c.dip_verified as dipVerified, ");
|
||||
sqlBuilder.append("c.dip_lookup_log_id as dipLookupLogId, ");
|
||||
sqlBuilder.append("c.dip_timestamp as dipTimestamp, ");
|
||||
sqlBuilder.append("c.alternate_dip_verified as alternateDipVerified, ");
|
||||
sqlBuilder.append("c.alternate_dip_lookup_log_id as alternateDipLookupLogId, ");
|
||||
sqlBuilder.append("c.alternate_dip_timestamp as alternateDipTimestamp, ");
|
||||
sqlBuilder.append("c.name_verified as nameVerified, ");
|
||||
sqlBuilder.append("c.screening_log_id as screeningLogId, ");
|
||||
sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
|
||||
sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
|
||||
sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
|
||||
@@ -342,7 +361,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
|
||||
sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
|
||||
sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
|
||||
sqlBuilder.append("cnp.remarks as remarks ");
|
||||
sqlBuilder.append("cnp.remarks as remarks, ");
|
||||
sqlBuilder.append("cnp.authorized_signatory as authorizedSignatory, ");
|
||||
sqlBuilder.append("cnp.additional_info as additionalInfo, ");
|
||||
sqlBuilder.append("cnp.date_of_registration as dateOfRegistration ");
|
||||
|
||||
sqlBuilder.append("from m_client c ");
|
||||
sqlBuilder.append("join m_office o on o.id = c.office_id ");
|
||||
@@ -356,11 +378,13 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
sqlBuilder.append("left join m_appuser acu on acu.id = c.activatedon_userid ");
|
||||
sqlBuilder.append("left join m_appuser clu on clu.id = c.closedon_userid ");
|
||||
sqlBuilder.append("left join m_code_value cv on cv.id = c.gender_cv_id ");
|
||||
sqlBuilder.append("left join m_code_value cvSalutation on cvSalutation.id = c.salutation_cv_id ");
|
||||
sqlBuilder.append("left join m_code_value cvclienttype on cvclienttype.id = c.client_type_cv_id ");
|
||||
sqlBuilder.append("left join m_code_value cvclassification on cvclassification.id = c.client_classification_cv_id ");
|
||||
sqlBuilder.append("left join m_code_value cvSubStatus on cvSubStatus.id = c.sub_status ");
|
||||
sqlBuilder.append("left join m_code_value cvOccupationCategory on cvOccupationCategory.id = c.occupation_category_cv_id ");
|
||||
sqlBuilder.append("left join m_code_value cvOccupation on cvOccupation.id = c.occupation_cv_id ");
|
||||
sqlBuilder.append("left join m_client gc on gc.id = c.guardian_client_id ");
|
||||
sqlBuilder.append("left join m_code_value cvConstitution on cvConstitution.id = cnp.constitution_cv_id ");
|
||||
sqlBuilder.append("left join m_code_value cvMainBusinessLine on cvMainBusinessLine.id = cnp.main_business_line_cv_id ");
|
||||
|
||||
@@ -407,6 +431,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String genderValue = rs.getString("genderValue");
|
||||
final CodeValueData gender = CodeValueData.instance(genderId, genderValue);
|
||||
|
||||
final Long salutationId = JdbcSupport.getLong(rs, "salutationId");
|
||||
final String salutationValue = rs.getString("salutationValue");
|
||||
final CodeValueData salutation = CodeValueData.instance(salutationId, salutationValue);
|
||||
|
||||
final Long clienttypeId = JdbcSupport.getLong(rs, "clienttypeId");
|
||||
final String clienttypeValue = rs.getString("clienttypeValue");
|
||||
final CodeValueData clienttype = CodeValueData.instance(clienttypeId, clienttypeValue);
|
||||
@@ -453,6 +481,19 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String occupationValue = rs.getString("occupationValue");
|
||||
final CodeValueData occupation = CodeValueData.instance(occupationId, occupationValue);
|
||||
|
||||
final BigDecimal monthlyIncome1 = rs.getBigDecimal("monthlyIncome");
|
||||
final BigDecimal annualTurnover1 = rs.getBigDecimal("annualTurnover");
|
||||
|
||||
Integer ageYears1 = null;
|
||||
Integer ageMonths1 = null;
|
||||
Integer ageDays1 = null;
|
||||
if (dateOfBirth != null) {
|
||||
final Period age = Period.between(dateOfBirth, LocalDate.now());
|
||||
ageYears1 = age.getYears();
|
||||
ageMonths1 = age.getMonths();
|
||||
ageDays1 = age.getDays();
|
||||
}
|
||||
|
||||
final Long constitutionId = JdbcSupport.getLong(rs, "constitutionId");
|
||||
final String constitutionValue = rs.getString("constitutionValue");
|
||||
final CodeValueData constitution = CodeValueData.instance(constitutionId, constitutionValue);
|
||||
@@ -462,18 +503,42 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String mainBusinessLineValue = rs.getString("mainBusinessLineValue");
|
||||
final CodeValueData mainBusinessLine = CodeValueData.instance(mainBusinessLineId, mainBusinessLineValue);
|
||||
final String remarks = rs.getString("remarks");
|
||||
final String authorizedSignatory = rs.getString("authorizedSignatory");
|
||||
final String additionalInfo = rs.getString("additionalInfo");
|
||||
final LocalDate dateOfRegistration = JdbcSupport.getLocalDate(rs, "dateOfRegistration");
|
||||
|
||||
final ClientNonPersonData clientNonPerson = new ClientNonPersonData(constitution, incorpNo, incorpValidityTill,
|
||||
mainBusinessLine, remarks);
|
||||
mainBusinessLine, remarks, authorizedSignatory, additionalInfo, dateOfRegistration);
|
||||
|
||||
final ClientTimelineData timeline = new ClientTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname,
|
||||
submittedByLastname, activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate,
|
||||
closedByUsername, closedByFirstname, closedByLastname);
|
||||
|
||||
return ClientData.instance(accountNo, cifNumber, status, subStatus, officeId, officeName, transferToOfficeId,
|
||||
ClientData clientData = ClientData.instance(accountNo, cifNumber, status, subStatus, officeId, officeName, transferToOfficeId,
|
||||
transferToOfficeName, id, firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress,
|
||||
dateOfBirth, gender, activationDate, imageId, staffId, staffName, timeline, savingsProductId, savingsProductName,
|
||||
savingsAccountId, clienttype, classification, occupationCategory, occupation, legalForm, clientNonPerson, isStaff);
|
||||
dateOfBirth, gender, salutation, activationDate, imageId, staffId, staffName, timeline, savingsProductId,
|
||||
savingsProductName, savingsAccountId, clienttype, classification, occupationCategory, occupation, legalForm,
|
||||
clientNonPerson, isStaff);
|
||||
clientData.setMonthlyIncome(monthlyIncome1);
|
||||
clientData.setAnnualTurnover(annualTurnover1);
|
||||
clientData.setAgeYears(ageYears1);
|
||||
clientData.setAgeMonths(ageMonths1);
|
||||
clientData.setAgeDays(ageDays1);
|
||||
clientData.setNationality(rs.getString("nationality"));
|
||||
clientData.setIsMinor(rs.getBoolean("isMinor"));
|
||||
clientData.setGuardianClientId(JdbcSupport.getLong(rs, "guardianClientId"));
|
||||
clientData.setGuardianClientDisplayName(rs.getString("guardianClientDisplayName"));
|
||||
clientData.setGuardianRelation(rs.getString("guardianRelation"));
|
||||
clientData.setAlternateMobileNo(rs.getString("alternateMobileNo"));
|
||||
clientData.setDipVerified(rs.getBoolean("dipVerified"));
|
||||
clientData.setDipLookupLogId(JdbcSupport.getLong(rs, "dipLookupLogId"));
|
||||
clientData.setDipTimestamp(rs.getString("dipTimestamp"));
|
||||
clientData.setAlternateDipVerified(rs.getBoolean("alternateDipVerified"));
|
||||
clientData.setAlternateDipLookupLogId(JdbcSupport.getLong(rs, "alternateDipLookupLogId"));
|
||||
clientData.setAlternateDipTimestamp(rs.getString("alternateDipTimestamp"));
|
||||
clientData.setNameVerified(rs.getBoolean("nameVerified"));
|
||||
clientData.setScreeningLogId(JdbcSupport.getLong(rs, "screeningLogId"));
|
||||
return clientData;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -591,7 +656,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final Collection<CodeValueData> clientNonPersonConstitutionOptions = null;
|
||||
final Collection<CodeValueData> clientNonPersonMainBusinessLineOptions = null;
|
||||
final List<EnumOptionData> clientLegalFormOptions = null;
|
||||
return ClientData.template(null, null, null, null, narrations, null, null, clientTypeOptions, clientClassificationOptions,
|
||||
return ClientData.template(null, null, null, null, narrations, null, null, null, clientTypeOptions, clientClassificationOptions,
|
||||
null, null, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, null, null,
|
||||
null, null);
|
||||
}
|
||||
@@ -637,6 +702,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
builder.append("c.date_of_birth as dateOfBirth, ");
|
||||
builder.append("c.gender_cv_id as genderId, ");
|
||||
builder.append("cv.code_value as genderValue, ");
|
||||
builder.append("c.salutation_cv_id as salutationId, ");
|
||||
builder.append("cvSalutation.code_value as salutationValue, ");
|
||||
builder.append("c.client_type_cv_id as clienttypeId, ");
|
||||
builder.append("cvclienttype.code_value as clienttypeValue, ");
|
||||
builder.append("c.client_classification_cv_id as classificationId, ");
|
||||
@@ -646,6 +713,18 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
builder.append("cvOccupationCategory.code_value as occupationCategoryValue, ");
|
||||
builder.append("c.occupation_cv_id as occupationId, ");
|
||||
builder.append("cvOccupation.code_value as occupationValue, ");
|
||||
builder.append("c.monthly_income as monthlyIncome, ");
|
||||
builder.append("c.annual_turnover as annualTurnover, ");
|
||||
builder.append("c.nationality as nationality, ");
|
||||
builder.append("c.is_minor as isMinor, ");
|
||||
builder.append("c.guardian_client_id as guardianClientId, ");
|
||||
builder.append("c.guardian_relation as guardianRelation, ");
|
||||
builder.append("c.alternate_mobile_no as alternateMobileNo, ");
|
||||
builder.append("c.dip_verified as dipVerified, ");
|
||||
builder.append("c.alternate_dip_verified as alternateDipVerified, ");
|
||||
builder.append("c.alternate_dip_lookup_log_id as alternateDipLookupLogId, ");
|
||||
builder.append("c.alternate_dip_timestamp as alternateDipTimestamp, ");
|
||||
builder.append("c.name_verified as nameVerified, ");
|
||||
|
||||
builder.append("c.submittedon_date as submittedOnDate, ");
|
||||
builder.append("sbu.username as submittedByUsername, ");
|
||||
@@ -669,6 +748,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
builder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
|
||||
builder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
|
||||
builder.append("cnp.remarks as remarks, ");
|
||||
builder.append("cnp.authorized_signatory as authorizedSignatory, ");
|
||||
builder.append("cnp.additional_info as additionalInfo, ");
|
||||
builder.append("cnp.date_of_registration as dateOfRegistration, ");
|
||||
|
||||
builder.append("c.activation_date as activationDate, c.image_id as imageId, ");
|
||||
builder.append("c.staff_id as staffId, s.display_name as staffName, ");
|
||||
@@ -684,11 +766,13 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
builder.append("left join m_appuser acu on acu.id = c.activatedon_userid ");
|
||||
builder.append("left join m_appuser clu on clu.id = c.closedon_userid ");
|
||||
builder.append("left join m_code_value cv on cv.id = c.gender_cv_id ");
|
||||
builder.append("left join m_code_value cvSalutation on cvSalutation.id = c.salutation_cv_id ");
|
||||
builder.append("left join m_code_value cvclienttype on cvclienttype.id = c.client_type_cv_id ");
|
||||
builder.append("left join m_code_value cvclassification on cvclassification.id = c.client_classification_cv_id ");
|
||||
builder.append("left join m_code_value cvSubStatus on cvSubStatus.id = c.sub_status ");
|
||||
builder.append("left join m_code_value cvOccupationCategory on cvOccupationCategory.id = c.occupation_category_cv_id ");
|
||||
builder.append("left join m_code_value cvOccupation on cvOccupation.id = c.occupation_cv_id ");
|
||||
builder.append("left join m_client gc on gc.id = c.guardian_client_id ");
|
||||
builder.append("left join m_code_value cvConstitution on cvConstitution.id = cnp.constitution_cv_id ");
|
||||
builder.append("left join m_code_value cvMainBusinessLine on cvMainBusinessLine.id = cnp.main_business_line_cv_id ");
|
||||
|
||||
@@ -735,6 +819,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String genderValue = rs.getString("genderValue");
|
||||
final CodeValueData gender = CodeValueData.instance(genderId, genderValue);
|
||||
|
||||
final Long salutationId = JdbcSupport.getLong(rs, "salutationId");
|
||||
final String salutationValue = rs.getString("salutationValue");
|
||||
final CodeValueData salutation = CodeValueData.instance(salutationId, salutationValue);
|
||||
|
||||
final Long clienttypeId = JdbcSupport.getLong(rs, "clienttypeId");
|
||||
final String clienttypeValue = rs.getString("clienttypeValue");
|
||||
final CodeValueData clienttype = CodeValueData.instance(clienttypeId, clienttypeValue);
|
||||
@@ -780,6 +868,19 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String occupationValue = rs.getString("occupationValue");
|
||||
final CodeValueData occupation = CodeValueData.instance(occupationId, occupationValue);
|
||||
|
||||
final BigDecimal monthlyIncome = rs.getBigDecimal("monthlyIncome");
|
||||
final BigDecimal annualTurnover = rs.getBigDecimal("annualTurnover");
|
||||
|
||||
Integer ageYears = null;
|
||||
Integer ageMonths = null;
|
||||
Integer ageDays = null;
|
||||
if (dateOfBirth != null) {
|
||||
final Period age = Period.between(dateOfBirth, LocalDate.now());
|
||||
ageYears = age.getYears();
|
||||
ageMonths = age.getMonths();
|
||||
ageDays = age.getDays();
|
||||
}
|
||||
|
||||
final Long constitutionId = JdbcSupport.getLong(rs, "constitutionId");
|
||||
final String constitutionValue = rs.getString("constitutionValue");
|
||||
final CodeValueData constitution = CodeValueData.instance(constitutionId, constitutionValue);
|
||||
@@ -789,18 +890,42 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
|
||||
final String mainBusinessLineValue = rs.getString("mainBusinessLineValue");
|
||||
final CodeValueData mainBusinessLine = CodeValueData.instance(mainBusinessLineId, mainBusinessLineValue);
|
||||
final String remarks = rs.getString("remarks");
|
||||
final String authorizedSignatory = rs.getString("authorizedSignatory");
|
||||
final String additionalInfo = rs.getString("additionalInfo");
|
||||
final LocalDate dateOfRegistration = JdbcSupport.getLocalDate(rs, "dateOfRegistration");
|
||||
|
||||
final ClientNonPersonData clientNonPerson = new ClientNonPersonData(constitution, incorpNo, incorpValidityTill,
|
||||
mainBusinessLine, remarks);
|
||||
mainBusinessLine, remarks, authorizedSignatory, additionalInfo, dateOfRegistration);
|
||||
|
||||
final ClientTimelineData timeline = new ClientTimelineData(submittedOnDate, submittedByUsername, submittedByFirstname,
|
||||
submittedByLastname, activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate,
|
||||
closedByUsername, closedByFirstname, closedByLastname);
|
||||
|
||||
return ClientData.instance(accountNo, cifNumber, status, subStatus, officeId, officeName, transferToOfficeId,
|
||||
ClientData clientData = ClientData.instance(accountNo, cifNumber, status, subStatus, officeId, officeName, transferToOfficeId,
|
||||
transferToOfficeName, id, firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress,
|
||||
dateOfBirth, gender, activationDate, imageId, staffId, staffName, timeline, savingsProductId, savingsProductName,
|
||||
savingsAccountId, clienttype, classification, occupationCategory, occupation, legalForm, clientNonPerson, isStaff);
|
||||
dateOfBirth, gender, salutation, activationDate, imageId, staffId, staffName, timeline, savingsProductId,
|
||||
savingsProductName, savingsAccountId, clienttype, classification, occupationCategory, occupation, legalForm,
|
||||
clientNonPerson, isStaff);
|
||||
clientData.setMonthlyIncome(monthlyIncome);
|
||||
clientData.setAnnualTurnover(annualTurnover);
|
||||
clientData.setAgeYears(ageYears);
|
||||
clientData.setAgeMonths(ageMonths);
|
||||
clientData.setAgeDays(ageDays);
|
||||
clientData.setNationality(rs.getString("nationality"));
|
||||
clientData.setIsMinor(rs.getBoolean("isMinor"));
|
||||
clientData.setGuardianClientId(JdbcSupport.getLong(rs, "guardianClientId"));
|
||||
clientData.setGuardianClientDisplayName(rs.getString("guardianClientDisplayName"));
|
||||
clientData.setGuardianRelation(rs.getString("guardianRelation"));
|
||||
clientData.setAlternateMobileNo(rs.getString("alternateMobileNo"));
|
||||
clientData.setDipVerified(rs.getBoolean("dipVerified"));
|
||||
clientData.setDipLookupLogId(JdbcSupport.getLong(rs, "dipLookupLogId"));
|
||||
clientData.setDipTimestamp(rs.getString("dipTimestamp"));
|
||||
clientData.setAlternateDipVerified(rs.getBoolean("alternateDipVerified"));
|
||||
clientData.setAlternateDipLookupLogId(JdbcSupport.getLong(rs, "alternateDipLookupLogId"));
|
||||
clientData.setAlternateDipTimestamp(rs.getString("alternateDipTimestamp"));
|
||||
clientData.setNameVerified(rs.getBoolean("nameVerified"));
|
||||
clientData.setScreeningLogId(JdbcSupport.getLong(rs, "screeningLogId"));
|
||||
return clientData;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -99,6 +99,9 @@ public class ClientTemplateReadPlatformServiceImpl implements ClientTemplateRead
|
||||
final List<CodeValueData> genderOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.GENDER));
|
||||
|
||||
final List<CodeValueData> salutationOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.SALUTATION));
|
||||
|
||||
final List<CodeValueData> clientTypeOptions = new ArrayList<>(
|
||||
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_TYPE));
|
||||
|
||||
@@ -123,9 +126,10 @@ public class ClientTemplateReadPlatformServiceImpl implements ClientTemplateRead
|
||||
EntityTables.CLIENT.getName(), null);
|
||||
|
||||
return ClientData.template(defaultOfficeId, LocalDate.now(DateUtils.getDateTimeZoneOfTenant()), offices, staffOptions, null,
|
||||
genderOptions, savingsProductDatas, clientTypeOptions, clientClassificationOptions, occupationCategoryOptions,
|
||||
occupationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientLegalFormOptions,
|
||||
familyMemberOptions, new ArrayList<AddressData>(Arrays.asList(address)), isAddressEnabled, datatableTemplates);
|
||||
genderOptions, salutationOptions, savingsProductDatas, clientTypeOptions, clientClassificationOptions,
|
||||
occupationCategoryOptions, occupationOptions, clientNonPersonConstitutionOptions,
|
||||
clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, familyMemberOptions,
|
||||
new ArrayList<AddressData>(Arrays.asList(address)), isAddressEnabled, datatableTemplates);
|
||||
}
|
||||
|
||||
private Long defaultToUsersOfficeIfNull(final Long officeId) {
|
||||
|
||||
+259
-3
@@ -125,6 +125,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final ClientKycWritePlatformService clientKycWritePlatformService;
|
||||
private final org.apache.fineract.portfolio.mobilelookup.domain.MobileLookupLogRepository mobileLookupLogRepository;
|
||||
private final org.apache.fineract.portfolio.sanctions.domain.SanctionsScreeningLogRepository sanctionsScreeningLogRepository;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -220,6 +222,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
gender = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.GENDER, genderId);
|
||||
}
|
||||
|
||||
CodeValue salutation = null;
|
||||
final Long salutationId = command.longValueOfParameterNamed(ClientApiConstants.salutationIdParamName);
|
||||
if (salutationId != null) {
|
||||
salutation = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.SALUTATION,
|
||||
salutationId);
|
||||
}
|
||||
|
||||
CodeValue clientType = null;
|
||||
final Long clientTypeId = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
|
||||
if (clientTypeId != null) {
|
||||
@@ -248,6 +257,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.OCCUPATION, occupationId);
|
||||
}
|
||||
|
||||
CodeValue maritalStatus = null;
|
||||
final Long maritalStatusId = command.longValueOfParameterNamed(ClientApiConstants.maritalStatusIdParamName);
|
||||
if (maritalStatusId != null) {
|
||||
maritalStatus = this.codeValueRepository
|
||||
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.MARITAL_STATUS, maritalStatusId);
|
||||
}
|
||||
|
||||
final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
|
||||
if (savingsProductId != null) {
|
||||
this.savingsProductRepository.findById(savingsProductId)
|
||||
@@ -270,6 +286,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
|
||||
final String cifNumber = command.stringValueOfParameterNamed(ClientApiConstants.cifNumberParamName);
|
||||
final String mobileNo = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
|
||||
final String alternateMobileNo = command.stringValueOfParameterNamed(ClientApiConstants.alternateMobileNoParamName);
|
||||
final String emailAddress = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
|
||||
final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
|
||||
final String middlename = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
|
||||
@@ -305,8 +322,87 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
|
||||
final Client newClient = Client.instance(currentUser, status, clientOffice, clientParentGroup, accountNo, cifNumber, firstname,
|
||||
middlename, lastname, fullname, activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff,
|
||||
submittedOnDate, savingsProductId, savingsAccountId, dataOfBirth, gender, clientType, clientClassification,
|
||||
occupationCategory, occupation, legalForm.getValue(), isStaff);
|
||||
submittedOnDate, savingsProductId, savingsAccountId, dataOfBirth, gender, salutation, clientType,
|
||||
clientClassification, occupationCategory, occupation, legalForm.getValue(), isStaff);
|
||||
|
||||
final java.math.BigDecimal monthlyIncome = command.bigDecimalValueOfParameterNamed(ClientApiConstants.monthlyIncomeParamName);
|
||||
if (monthlyIncome != null) {
|
||||
newClient.setMonthlyIncome(monthlyIncome);
|
||||
}
|
||||
|
||||
final java.math.BigDecimal annualTurnover = command.bigDecimalValueOfParameterNamed(ClientApiConstants.annualTurnoverParamName);
|
||||
if (annualTurnover != null) {
|
||||
newClient.setAnnualTurnover(annualTurnover);
|
||||
}
|
||||
|
||||
if (maritalStatus != null) {
|
||||
newClient.setMaritalStatus(maritalStatus);
|
||||
}
|
||||
|
||||
final String nationality = command.stringValueOfParameterNamed(ClientApiConstants.nationalityIdParamName);
|
||||
if (nationality != null) {
|
||||
newClient.setNationality(nationality);
|
||||
}
|
||||
|
||||
final Boolean isMinor = command.booleanObjectValueOfParameterNamed(ClientApiConstants.isMinorParamName);
|
||||
if (Boolean.TRUE.equals(isMinor)) {
|
||||
newClient.setIsMinor(true);
|
||||
final Long guardianClientId = command.longValueOfParameterNamed(ClientApiConstants.guardianClientIdParamName);
|
||||
if (guardianClientId != null) {
|
||||
final Client guardianClient = this.clientRepository.findOneWithNotFoundDetection(guardianClientId);
|
||||
newClient.setGuardianClient(guardianClient);
|
||||
}
|
||||
final String guardianRelation = command.stringValueOfParameterNamed(ClientApiConstants.guardianRelationParamName);
|
||||
if (guardianRelation != null) {
|
||||
newClient.setGuardianRelation(guardianRelation);
|
||||
}
|
||||
} else {
|
||||
newClient.setIsMinor(false);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(alternateMobileNo)) {
|
||||
newClient.setAlternateMobileNo(alternateMobileNo.trim());
|
||||
}
|
||||
|
||||
final Boolean dipVerified = command.booleanObjectValueOfParameterNamed(ClientApiConstants.dipVerifiedParamName);
|
||||
if (dipVerified != null) {
|
||||
newClient.setDipVerified(dipVerified);
|
||||
}
|
||||
|
||||
final Long dipLookupLogId = command.longValueOfParameterNamed(ClientApiConstants.dipLookupLogIdParamName);
|
||||
if (dipLookupLogId != null) {
|
||||
newClient.setDipLookupLogId(dipLookupLogId);
|
||||
}
|
||||
|
||||
final String dipTimestamp = command.stringValueOfParameterNamed(ClientApiConstants.dipTimestampParamName);
|
||||
if (dipTimestamp != null) {
|
||||
newClient.setDipTimestamp(dipTimestamp);
|
||||
}
|
||||
|
||||
final Boolean alternateDipVerified = command.booleanObjectValueOfParameterNamed(ClientApiConstants.alternateDipVerifiedParamName);
|
||||
if (alternateDipVerified != null) {
|
||||
newClient.setAlternateDipVerified(alternateDipVerified);
|
||||
}
|
||||
|
||||
final Long alternateDipLookupLogId = command.longValueOfParameterNamed(ClientApiConstants.alternateDipLookupLogIdParamName);
|
||||
if (alternateDipLookupLogId != null) {
|
||||
newClient.setAlternateDipLookupLogId(alternateDipLookupLogId);
|
||||
}
|
||||
|
||||
final String alternateDipTimestamp = command.stringValueOfParameterNamed(ClientApiConstants.alternateDipTimestampParamName);
|
||||
if (alternateDipTimestamp != null) {
|
||||
newClient.setAlternateDipTimestamp(alternateDipTimestamp);
|
||||
}
|
||||
|
||||
final Boolean nameVerified = command.booleanObjectValueOfParameterNamed(ClientApiConstants.nameVerifiedParamName);
|
||||
if (nameVerified != null) {
|
||||
newClient.setNameVerified(nameVerified);
|
||||
}
|
||||
|
||||
final Long screeningLogId = command.longValueOfParameterNamed(ClientApiConstants.screeningLogIdParamName);
|
||||
if (screeningLogId != null) {
|
||||
newClient.setScreeningLogId(screeningLogId);
|
||||
}
|
||||
|
||||
// Account Number generation
|
||||
this.clientRepository.saveAndFlush(newClient);
|
||||
@@ -323,6 +419,28 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
this.clientRepository.saveAndFlush(newClient);
|
||||
}
|
||||
|
||||
// Backfill client_id on DIP lookup logs
|
||||
if (newClient.getDipLookupLogId() != null) {
|
||||
this.mobileLookupLogRepository.findById(newClient.getDipLookupLogId()).ifPresent(log -> {
|
||||
log.setClientId(newClient.getId());
|
||||
this.mobileLookupLogRepository.save(log);
|
||||
});
|
||||
}
|
||||
if (newClient.getAlternateDipLookupLogId() != null) {
|
||||
this.mobileLookupLogRepository.findById(newClient.getAlternateDipLookupLogId()).ifPresent(log -> {
|
||||
log.setClientId(newClient.getId());
|
||||
this.mobileLookupLogRepository.save(log);
|
||||
});
|
||||
}
|
||||
|
||||
// Backfill client_id on sanctions screening log
|
||||
if (newClient.getScreeningLogId() != null) {
|
||||
this.sanctionsScreeningLogRepository.findById(newClient.getScreeningLogId()).ifPresent(log -> {
|
||||
log.setClientId(newClient.getId());
|
||||
this.sanctionsScreeningLogRepository.save(log);
|
||||
});
|
||||
}
|
||||
|
||||
boolean rollbackTransaction = false;
|
||||
if (newClient.isActive()) {
|
||||
validateParentGroupRulesBeforeClientActivation(newClient);
|
||||
@@ -428,8 +546,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
ClientApiConstants.CLIENT_NON_PERSON_MAIN_BUSINESS_LINE, clientNonPersonMainBusinessLineId);
|
||||
}
|
||||
|
||||
final String authorizedSignatory = this.fromApiJsonHelper
|
||||
.extractStringNamed(ClientApiConstants.authorizedSignatoryParamName, clientNonPersonElement);
|
||||
final String additionalInfo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.additionalInfoParamName,
|
||||
clientNonPersonElement);
|
||||
final LocalDate dateOfRegistration = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed(ClientApiConstants.dateOfRegistrationParamName, clientNonPersonElement);
|
||||
|
||||
final ClientNonPerson newClientNonPerson = ClientNonPerson.createNew(client, clientNonPersonConstitution,
|
||||
clientNonPersonMainBusinessLine, incorpNumber, incorpValidityTill, remarks);
|
||||
clientNonPersonMainBusinessLine, incorpNumber, incorpValidityTill, remarks, authorizedSignatory, additionalInfo,
|
||||
dateOfRegistration);
|
||||
|
||||
this.clientNonPersonRepository.save(newClientNonPerson);
|
||||
}
|
||||
@@ -478,6 +604,12 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
clientForUpdate.setMobileNo(StringUtils.defaultIfEmpty(newValue, null));
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.alternateMobileNoParamName, clientForUpdate.getAlternateMobileNo())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.alternateMobileNoParamName);
|
||||
changes.put(ClientApiConstants.alternateMobileNoParamName, newValue);
|
||||
clientForUpdate.setAlternateMobileNo(StringUtils.defaultIfEmpty(newValue, null));
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.emailAddressParamName, clientForUpdate.getEmailAddress())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
|
||||
changes.put(ClientApiConstants.emailAddressParamName, newValue);
|
||||
@@ -518,6 +650,11 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
changes.put(ClientApiConstants.genderIdParamName, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ClientApiConstants.salutationIdParamName, clientForUpdate.salutationId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.salutationIdParamName);
|
||||
changes.put(ClientApiConstants.salutationIdParamName, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ClientApiConstants.savingsProductIdParamName, clientForUpdate.savingsProductId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
|
||||
changes.put(ClientApiConstants.savingsProductIdParamName, newValue);
|
||||
@@ -545,6 +682,104 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
changes.put(ClientApiConstants.occupationIdParamName, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ClientApiConstants.maritalStatusIdParamName,
|
||||
clientForUpdate.maritalStatusId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.maritalStatusIdParamName);
|
||||
changes.put(ClientApiConstants.maritalStatusIdParamName, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(ClientApiConstants.monthlyIncomeParamName, clientForUpdate.getMonthlyIncome())) {
|
||||
final java.math.BigDecimal newValue = command.bigDecimalValueOfParameterNamed(ClientApiConstants.monthlyIncomeParamName);
|
||||
changes.put(ClientApiConstants.monthlyIncomeParamName, newValue);
|
||||
clientForUpdate.setMonthlyIncome(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(ClientApiConstants.annualTurnoverParamName, clientForUpdate.getAnnualTurnover())) {
|
||||
final java.math.BigDecimal newValue = command.bigDecimalValueOfParameterNamed(ClientApiConstants.annualTurnoverParamName);
|
||||
changes.put(ClientApiConstants.annualTurnoverParamName, newValue);
|
||||
clientForUpdate.setAnnualTurnover(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.nationalityIdParamName, clientForUpdate.getNationality())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.nationalityIdParamName);
|
||||
changes.put(ClientApiConstants.nationalityIdParamName, newValue);
|
||||
clientForUpdate.setNationality(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(ClientApiConstants.isMinorParamName, clientForUpdate.getIsMinor())) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(ClientApiConstants.isMinorParamName);
|
||||
changes.put(ClientApiConstants.isMinorParamName, newValue);
|
||||
clientForUpdate.setIsMinor(newValue);
|
||||
}
|
||||
|
||||
if (command.hasParameter(ClientApiConstants.guardianClientIdParamName)) {
|
||||
final Long newGuardianId = command.longValueOfParameterNamed(ClientApiConstants.guardianClientIdParamName);
|
||||
final Long currentGuardianId = clientForUpdate.getGuardianClient() != null ? clientForUpdate.getGuardianClient().getId() : null;
|
||||
if (!java.util.Objects.equals(newGuardianId, currentGuardianId)) {
|
||||
changes.put(ClientApiConstants.guardianClientIdParamName, newGuardianId);
|
||||
if (newGuardianId != null) {
|
||||
final Client guardianClient = this.clientRepository.findOneWithNotFoundDetection(newGuardianId);
|
||||
clientForUpdate.setGuardianClient(guardianClient);
|
||||
} else {
|
||||
clientForUpdate.setGuardianClient(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.guardianRelationParamName, clientForUpdate.getGuardianRelation())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.guardianRelationParamName);
|
||||
changes.put(ClientApiConstants.guardianRelationParamName, newValue);
|
||||
clientForUpdate.setGuardianRelation(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(ClientApiConstants.dipVerifiedParamName, clientForUpdate.getDipVerified())) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(ClientApiConstants.dipVerifiedParamName);
|
||||
changes.put(ClientApiConstants.dipVerifiedParamName, newValue);
|
||||
clientForUpdate.setDipVerified(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ClientApiConstants.dipLookupLogIdParamName, clientForUpdate.getDipLookupLogId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.dipLookupLogIdParamName);
|
||||
changes.put(ClientApiConstants.dipLookupLogIdParamName, newValue);
|
||||
clientForUpdate.setDipLookupLogId(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.dipTimestampParamName, clientForUpdate.getDipTimestamp())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.dipTimestampParamName);
|
||||
changes.put(ClientApiConstants.dipTimestampParamName, newValue);
|
||||
clientForUpdate.setDipTimestamp(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(ClientApiConstants.alternateDipVerifiedParamName, clientForUpdate.getAlternateDipVerified())) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(ClientApiConstants.alternateDipVerifiedParamName);
|
||||
changes.put(ClientApiConstants.alternateDipVerifiedParamName, newValue);
|
||||
clientForUpdate.setAlternateDipVerified(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ClientApiConstants.alternateDipLookupLogIdParamName, clientForUpdate.getAlternateDipLookupLogId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.alternateDipLookupLogIdParamName);
|
||||
changes.put(ClientApiConstants.alternateDipLookupLogIdParamName, newValue);
|
||||
clientForUpdate.setAlternateDipLookupLogId(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInStringParameterNamed(ClientApiConstants.alternateDipTimestampParamName, clientForUpdate.getAlternateDipTimestamp())) {
|
||||
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.alternateDipTimestampParamName);
|
||||
changes.put(ClientApiConstants.alternateDipTimestampParamName, newValue);
|
||||
clientForUpdate.setAlternateDipTimestamp(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(ClientApiConstants.nameVerifiedParamName, clientForUpdate.getNameVerified())) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(ClientApiConstants.nameVerifiedParamName);
|
||||
changes.put(ClientApiConstants.nameVerifiedParamName, newValue);
|
||||
clientForUpdate.setNameVerified(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ClientApiConstants.screeningLogIdParamName, clientForUpdate.getScreeningLogId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.screeningLogIdParamName);
|
||||
changes.put(ClientApiConstants.screeningLogIdParamName, newValue);
|
||||
clientForUpdate.setScreeningLogId(newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(ClientApiConstants.legalFormIdParamName, clientForUpdate.getLegalForm())) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName);
|
||||
if (newValue != null) {
|
||||
@@ -642,6 +877,17 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
clientForUpdate.updateGender(newCodeVal);
|
||||
}
|
||||
|
||||
if (changes.containsKey(ClientApiConstants.salutationIdParamName)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.salutationIdParamName);
|
||||
CodeValue newCodeVal = null;
|
||||
if (newValue != null) {
|
||||
newCodeVal = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.SALUTATION,
|
||||
newValue);
|
||||
}
|
||||
clientForUpdate.updateSalutation(newCodeVal);
|
||||
clientForUpdate.deriveDisplayName();
|
||||
}
|
||||
|
||||
if (changes.containsKey(ClientApiConstants.clientTypeIdParamName)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
|
||||
CodeValue newCodeVal = null;
|
||||
@@ -682,6 +928,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
clientForUpdate.updateOccupation(newCodeVal);
|
||||
}
|
||||
|
||||
if (changes.containsKey(ClientApiConstants.maritalStatusIdParamName)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.maritalStatusIdParamName);
|
||||
CodeValue newCodeVal = null;
|
||||
if (newValue != null) {
|
||||
newCodeVal = this.codeValueRepository
|
||||
.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.MARITAL_STATUS, newValue);
|
||||
}
|
||||
clientForUpdate.updateMaritalStatus(newCodeVal);
|
||||
}
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
this.clientRepository.saveAndFlush(clientForUpdate);
|
||||
}
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ public class ClientSearchData {
|
||||
private String displayName;
|
||||
private ExternalId externalId;
|
||||
private String accountNumber;
|
||||
private String cifNumber;
|
||||
private Long officeId;
|
||||
private String officeName;
|
||||
private String mobileNo;
|
||||
|
||||
+11
-10
@@ -27,6 +27,7 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public class MobileLookupResult {
|
||||
|
||||
private final Long lookupLogId;
|
||||
private final boolean success;
|
||||
private final String message;
|
||||
private final MobileLookupData data;
|
||||
@@ -44,38 +45,38 @@ public class MobileLookupResult {
|
||||
private final MobileLookupDetailData disconnectedDetail;
|
||||
}
|
||||
|
||||
public static MobileLookupResult clean(String mobileNumber) {
|
||||
public static MobileLookupResult clean(Long lookupLogId, String mobileNumber) {
|
||||
MobileLookupData data = new MobileLookupData(mobileNumber, "CLEAN", false, false, null, null);
|
||||
return new MobileLookupResult(true,
|
||||
return new MobileLookupResult(lookupLogId, true,
|
||||
"Number is CLEAN — not found in DIP or MNRL records",
|
||||
data, OffsetDateTime.now(ZoneOffset.UTC).toString());
|
||||
}
|
||||
|
||||
public static MobileLookupResult suspected(String mobileNumber, MobileLookupDetailData suspectedDetail) {
|
||||
public static MobileLookupResult suspected(Long lookupLogId, String mobileNumber, MobileLookupDetailData suspectedDetail) {
|
||||
MobileLookupData data = new MobileLookupData(mobileNumber, "SUSPECTED", true, false, suspectedDetail, null);
|
||||
return new MobileLookupResult(true,
|
||||
return new MobileLookupResult(lookupLogId, true,
|
||||
"Number is SUSPECTED — found in DIP records",
|
||||
data, OffsetDateTime.now(ZoneOffset.UTC).toString());
|
||||
}
|
||||
|
||||
public static MobileLookupResult disconnected(String mobileNumber, MobileLookupDetailData disconnectedDetail) {
|
||||
public static MobileLookupResult disconnected(Long lookupLogId, String mobileNumber, MobileLookupDetailData disconnectedDetail) {
|
||||
MobileLookupData data = new MobileLookupData(mobileNumber, "DISCONNECTED", false, true, null, disconnectedDetail);
|
||||
return new MobileLookupResult(true,
|
||||
return new MobileLookupResult(lookupLogId, true,
|
||||
"Number is DISCONNECTED — found in MNRL records",
|
||||
data, OffsetDateTime.now(ZoneOffset.UTC).toString());
|
||||
}
|
||||
|
||||
public static MobileLookupResult suspectedAndDisconnected(String mobileNumber,
|
||||
public static MobileLookupResult suspectedAndDisconnected(Long lookupLogId, String mobileNumber,
|
||||
MobileLookupDetailData suspectedDetail, MobileLookupDetailData disconnectedDetail) {
|
||||
MobileLookupData data = new MobileLookupData(mobileNumber, "SUSPECTED_AND_DISCONNECTED",
|
||||
true, true, suspectedDetail, disconnectedDetail);
|
||||
return new MobileLookupResult(true,
|
||||
return new MobileLookupResult(lookupLogId, true,
|
||||
"Number is SUSPECTED and DISCONNECTED — found in both DIP and MNRL records",
|
||||
data, OffsetDateTime.now(ZoneOffset.UTC).toString());
|
||||
}
|
||||
|
||||
public static MobileLookupResult serviceUnavailable(String mobileNumber, String message) {
|
||||
public static MobileLookupResult serviceUnavailable(Long lookupLogId, String mobileNumber, String message) {
|
||||
MobileLookupData data = new MobileLookupData(mobileNumber, "SERVICE_UNAVAILABLE", false, false, null, null);
|
||||
return new MobileLookupResult(false, message, data, OffsetDateTime.now(ZoneOffset.UTC).toString());
|
||||
return new MobileLookupResult(lookupLogId, false, message, data, OffsetDateTime.now(ZoneOffset.UTC).toString());
|
||||
}
|
||||
}
|
||||
|
||||
+19
-19
@@ -81,19 +81,17 @@ public class MobileLookupServiceImpl implements MobileLookupService {
|
||||
LOG.info("Demo suspected number detected: {}", trimmed);
|
||||
final MobileLookupDetailData suspectedDetail = MobileLookupDetailData.forSuspected(
|
||||
"active", "Demo DIP suspected number", "DEMO");
|
||||
final MobileLookupResult result = MobileLookupResult.suspected(trimmed, suspectedDetail);
|
||||
saveLog(trimmed, "SUSPECTED", true, false,
|
||||
result.getMessage(), null, source, clientId);
|
||||
return result;
|
||||
final Long logId = saveLog(trimmed, "SUSPECTED", true, false,
|
||||
"Number is SUSPECTED — found in DIP records", null, source, clientId);
|
||||
return MobileLookupResult.suspected(logId, trimmed, suspectedDetail);
|
||||
}
|
||||
|
||||
// Demo numbers — return CLEAN without calling the external API
|
||||
if (isDemoCleanNumber(trimmed)) {
|
||||
LOG.info("Demo clean number detected: {}", trimmed);
|
||||
final MobileLookupResult result = MobileLookupResult.clean(trimmed);
|
||||
saveLog(trimmed, "CLEAN", false, false,
|
||||
result.getMessage(), null, source, clientId);
|
||||
return result;
|
||||
final Long logId = saveLog(trimmed, "CLEAN", false, false,
|
||||
"Number is CLEAN — not found in DIP or MNRL records", null, source, clientId);
|
||||
return MobileLookupResult.clean(logId, trimmed);
|
||||
}
|
||||
|
||||
final String url = baseUrl + "/api/v1/lookup/check?mobileNumber=" + trimmed;
|
||||
@@ -105,19 +103,19 @@ public class MobileLookupServiceImpl implements MobileLookupService {
|
||||
rawResponse = restTemplate.getForObject(url, String.class);
|
||||
LOG.info("DIP/MNRL lookup response for {}: {}", trimmed, rawResponse);
|
||||
|
||||
final MobileLookupResult result = parseResponse(rawResponse);
|
||||
final MobileLookupResult parsed = parseResponse(rawResponse);
|
||||
|
||||
saveLog(trimmed, result.getData().getStatus(),
|
||||
result.getData().isSuspected(), result.getData().isDisconnected(),
|
||||
result.getMessage(), rawResponse, source, clientId);
|
||||
final Long logId = saveLog(trimmed, parsed.getData().getStatus(),
|
||||
parsed.getData().isSuspected(), parsed.getData().isDisconnected(),
|
||||
parsed.getMessage(), rawResponse, source, clientId);
|
||||
|
||||
return result;
|
||||
return new MobileLookupResult(logId, parsed.isSuccess(), parsed.getMessage(),
|
||||
parsed.getData(), parsed.getTimestamp());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to call DIP/MNRL lookup API for {}: {}", trimmed, e.getMessage());
|
||||
saveLog(trimmed, "ERROR", false, false,
|
||||
final Long logId = saveLog(trimmed, "ERROR", false, false,
|
||||
e.getMessage(), rawResponse, source, clientId);
|
||||
// Return CLEAN with service-unavailable message instead of throwing
|
||||
return MobileLookupResult.serviceUnavailable(trimmed,
|
||||
return MobileLookupResult.serviceUnavailable(logId, trimmed,
|
||||
"DIP/MNRL lookup service is unavailable. Number treated as CLEAN by default.");
|
||||
}
|
||||
}
|
||||
@@ -140,7 +138,7 @@ public class MobileLookupServiceImpl implements MobileLookupService {
|
||||
return logRepository.findByLookupStatus(status);
|
||||
}
|
||||
|
||||
private void saveLog(final String mobileNumber, final String status,
|
||||
private Long saveLog(final String mobileNumber, final String status,
|
||||
final boolean suspected, final boolean disconnected,
|
||||
final String message, final String rawResponse,
|
||||
final String source, final Long clientId) {
|
||||
@@ -148,9 +146,11 @@ public class MobileLookupServiceImpl implements MobileLookupService {
|
||||
final MobileLookupLog log = new MobileLookupLog(
|
||||
mobileNumber, status, suspected, disconnected,
|
||||
message, rawResponse, source, clientId);
|
||||
logRepository.save(log);
|
||||
logRepository.saveAndFlush(log);
|
||||
return log.getId();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to save mobile lookup log for '{}': {}", mobileNumber, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ public class MobileLookupServiceImpl implements MobileLookupService {
|
||||
MobileLookupResult.MobileLookupData lookupData = new MobileLookupResult.MobileLookupData(
|
||||
mobileNumber, status, suspected, disconnected, suspectedDetail, disconnectedDetail);
|
||||
|
||||
return new MobileLookupResult(success, message, lookupData, timestamp);
|
||||
return new MobileLookupResult(null, success, message, lookupData, timestamp);
|
||||
}
|
||||
|
||||
private boolean isDemoCleanNumber(final String mobileNumber) {
|
||||
|
||||
+8
-5
@@ -25,6 +25,7 @@ import java.util.List;
|
||||
*/
|
||||
public final class SanctionsScreeningResult {
|
||||
|
||||
private final Long screeningLogId;
|
||||
private final String screenedName;
|
||||
private final boolean matchFound;
|
||||
private final String status;
|
||||
@@ -32,9 +33,10 @@ public final class SanctionsScreeningResult {
|
||||
private final int matchCount;
|
||||
private final List<SanctionsMatchData> matches;
|
||||
|
||||
private SanctionsScreeningResult(final String screenedName, final boolean matchFound,
|
||||
private SanctionsScreeningResult(final Long screeningLogId, final String screenedName, final boolean matchFound,
|
||||
final String status, final String message, final int matchCount,
|
||||
final List<SanctionsMatchData> matches) {
|
||||
this.screeningLogId = screeningLogId;
|
||||
this.screenedName = screenedName;
|
||||
this.matchFound = matchFound;
|
||||
this.status = status;
|
||||
@@ -43,20 +45,21 @@ public final class SanctionsScreeningResult {
|
||||
this.matches = matches;
|
||||
}
|
||||
|
||||
public static SanctionsScreeningResult matched(final String screenedName,
|
||||
public static SanctionsScreeningResult matched(final Long screeningLogId, final String screenedName,
|
||||
final List<SanctionsMatchData> matches) {
|
||||
return new SanctionsScreeningResult(screenedName, true, "FLAGGED",
|
||||
return new SanctionsScreeningResult(screeningLogId, screenedName, true, "FLAGGED",
|
||||
"Potential match found in UN Security Council consolidated sanctions list. "
|
||||
+ "Manual review required before proceeding.",
|
||||
matches.size(), matches);
|
||||
}
|
||||
|
||||
public static SanctionsScreeningResult clear(final String screenedName) {
|
||||
return new SanctionsScreeningResult(screenedName, false, "CLEAR",
|
||||
public static SanctionsScreeningResult clear(final Long screeningLogId, final String screenedName) {
|
||||
return new SanctionsScreeningResult(screeningLogId, screenedName, false, "CLEAR",
|
||||
"No match found in UN Security Council consolidated sanctions list.",
|
||||
0, List.of());
|
||||
}
|
||||
|
||||
public Long getScreeningLogId() { return screeningLogId; }
|
||||
public String getScreenedName() { return screenedName; }
|
||||
public boolean isMatchFound() { return matchFound; }
|
||||
public String getStatus() { return status; }
|
||||
|
||||
+10
-8
@@ -53,8 +53,8 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
||||
public SanctionsScreeningResult screenName(final String name, final String source,
|
||||
final Long clientId, final String ipAddress) {
|
||||
if (name == null || name.isBlank()) {
|
||||
saveLog(name, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
||||
return SanctionsScreeningResult.clear(name);
|
||||
final Long logId = saveLog(name, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
||||
return SanctionsScreeningResult.clear(logId, name);
|
||||
}
|
||||
|
||||
final String trimmedName = name.trim();
|
||||
@@ -64,8 +64,8 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
||||
|
||||
if (matches.isEmpty()) {
|
||||
LOG.info("No sanctions match found for: {}", trimmedName);
|
||||
saveLog(trimmedName, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
||||
return SanctionsScreeningResult.clear(trimmedName);
|
||||
final Long logId = saveLog(trimmedName, "CLEAR", 0, null, null, source, clientId, ipAddress);
|
||||
return SanctionsScreeningResult.clear(logId, trimmedName);
|
||||
}
|
||||
|
||||
LOG.warn("Sanctions match found for '{}': {} potential match(es)", trimmedName, matches.size());
|
||||
@@ -82,9 +82,9 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
||||
.map(SanctionsEntry::getFullName)
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
saveLog(trimmedName, "FLAGGED", matches.size(), matchedEntryIds, matchedNames, source, clientId, ipAddress);
|
||||
final Long logId = saveLog(trimmedName, "FLAGGED", matches.size(), matchedEntryIds, matchedNames, source, clientId, ipAddress);
|
||||
|
||||
return SanctionsScreeningResult.matched(trimmedName, matchDataList);
|
||||
return SanctionsScreeningResult.matched(logId, trimmedName, matchDataList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,16 +111,18 @@ public class SanctionsScreeningServiceImpl implements SanctionsScreeningService
|
||||
return screeningLogRepository.findByScreenedNameContaining(name);
|
||||
}
|
||||
|
||||
private void saveLog(final String screenedName, final String result, final int matchCount,
|
||||
private Long saveLog(final String screenedName, final String result, final int matchCount,
|
||||
final String matchedEntryIds, final String matchedNames, final String source,
|
||||
final Long clientId, final String ipAddress) {
|
||||
try {
|
||||
final SanctionsScreeningLog log = new SanctionsScreeningLog(
|
||||
screenedName, result, matchCount, matchedEntryIds, matchedNames,
|
||||
source, clientId, ipAddress);
|
||||
screeningLogRepository.save(log);
|
||||
screeningLogRepository.saveAndFlush(log);
|
||||
return log.getId();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to save sanctions screening log for '{}': {}", screenedName, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -652,6 +652,8 @@ fineract.kyc.lincpay.path.aadhaar-generate-otp=${FINERACT_KYC_LINCPAY_PATH_AADHA
|
||||
fineract.kyc.lincpay.path.aadhaar-verify-otp=${FINERACT_KYC_LINCPAY_PATH_AADHAAR_VERIFY_OTP:/aadhaar/verify-otp}
|
||||
fineract.kyc.lincpay.path.pan-validate=${FINERACT_KYC_LINCPAY_PATH_PAN_VALIDATE:/validate-pan}
|
||||
fineract.kyc.lincpay.path.gstin-validate=${FINERACT_KYC_LINCPAY_PATH_GSTIN_VALIDATE:/validate-gstn}
|
||||
fineract.kyc.lincpay.path.cin-validate=${FINERACT_KYC_LINCPAY_PATH_CIN_VALIDATE:/validate-company}
|
||||
fineract.kyc.lincpay.path.din-validate=${FINERACT_KYC_LINCPAY_PATH_DIN_VALIDATE:/validate-director}
|
||||
|
||||
# ── OpenProject Integration ──────────────────────────────────────────────────
|
||||
# Set FINERACT_OPENPROJECT_API_TOKEN as env var in production.
|
||||
|
||||
@@ -247,4 +247,25 @@
|
||||
<include file="parts/0226_add_cfcfrms_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0227_add_client_cif_number.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0228_add_occupation_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0229_add_salutation_field.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0230_add_client_form60.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0231_add_menu_permissions.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0232_add_pan_name_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0233_auto_verify_kyc_status.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0234_add_monthly_income.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0235_add_form60_purpose_code.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0236_add_marital_status.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0237_extend_family_members.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0238_add_annual_turnover.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0239_add_minor_guardian_nationality.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0240_add_entity_classification_tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0241_add_client_nominee_table.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0242_add_marriage_authority.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0243_add_nominee_identifier.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0244_add_alternate_mobile.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0245_add_address_client_fk_and_types.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0246_add_kyc_extended_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0247_add_huf_client_classification.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0248_add_cin_din_verification_fields.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/0249_add_non_person_extended_fields.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+12
-12
@@ -39,7 +39,7 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════════
|
||||
MySQL – m_cfcfrms_complaint_ticket
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<changeSet author="fineract" id="1" context="mysql">
|
||||
<changeSet author="fineract" id="0226-1" context="mysql">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cfcfrms_complaint_ticket"/></not>
|
||||
</preConditions>
|
||||
@@ -110,7 +110,7 @@
|
||||
</changeSet>
|
||||
|
||||
<!-- PostgreSQL – m_cfcfrms_complaint_ticket -->
|
||||
<changeSet author="fineract" id="1" context="postgresql">
|
||||
<changeSet author="fineract" id="0226-2" context="postgresql">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cfcfrms_complaint_ticket"/></not>
|
||||
</preConditions>
|
||||
@@ -183,7 +183,7 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════════
|
||||
MySQL – m_cfcfrms_complaint_audit_log
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<changeSet author="fineract" id="2" context="mysql">
|
||||
<changeSet author="fineract" id="0226-3" context="mysql">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cfcfrms_complaint_audit_log"/></not>
|
||||
</preConditions>
|
||||
@@ -220,7 +220,7 @@
|
||||
</changeSet>
|
||||
|
||||
<!-- PostgreSQL – m_cfcfrms_complaint_audit_log -->
|
||||
<changeSet author="fineract" id="2" context="postgresql">
|
||||
<changeSet author="fineract" id="0226-4" context="postgresql">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cfcfrms_complaint_audit_log"/></not>
|
||||
</preConditions>
|
||||
@@ -259,7 +259,7 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════════
|
||||
MySQL – m_cfcfrms_simulator_log
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<changeSet author="fineract" id="3" context="mysql">
|
||||
<changeSet author="fineract" id="0226-5" context="mysql">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cfcfrms_simulator_log"/></not>
|
||||
</preConditions>
|
||||
@@ -299,7 +299,7 @@
|
||||
</changeSet>
|
||||
|
||||
<!-- PostgreSQL – m_cfcfrms_simulator_log -->
|
||||
<changeSet author="fineract" id="3" context="postgresql">
|
||||
<changeSet author="fineract" id="0226-6" context="postgresql">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_cfcfrms_simulator_log"/></not>
|
||||
</preConditions>
|
||||
@@ -339,7 +339,7 @@
|
||||
</changeSet>
|
||||
|
||||
<!-- Indexes -->
|
||||
<changeSet author="fineract" id="4">
|
||||
<changeSet author="fineract" id="0226-7">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><indexExists indexName="idx_cfcfrms_ticket_id"/></not>
|
||||
</preConditions>
|
||||
@@ -348,7 +348,7 @@
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="5">
|
||||
<changeSet author="fineract" id="0226-8">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><indexExists indexName="idx_cfcfrms_ticket_status"/></not>
|
||||
</preConditions>
|
||||
@@ -357,7 +357,7 @@
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="6">
|
||||
<changeSet author="fineract" id="0226-9">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><indexExists indexName="idx_cfcfrms_fraud_account"/></not>
|
||||
</preConditions>
|
||||
@@ -366,7 +366,7 @@
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="7">
|
||||
<changeSet author="fineract" id="0226-10">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><indexExists indexName="idx_cfcfrms_audit_ticket_id"/></not>
|
||||
</preConditions>
|
||||
@@ -375,7 +375,7 @@
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="8">
|
||||
<changeSet author="fineract" id="0226-11">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><indexExists indexName="idx_cfcfrms_sim_ref"/></not>
|
||||
</preConditions>
|
||||
@@ -387,7 +387,7 @@
|
||||
<!-- ═══════════════════════════════════════════════════════════════════════
|
||||
Permissions
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<changeSet author="fineract" id="9">
|
||||
<changeSet author="fineract" id="0226-12">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">
|
||||
SELECT COUNT(*) FROM m_permission WHERE code = 'READ_CFCFRMS'
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- ==================== Add Salutation code ==================== -->
|
||||
<changeSet author="fineract" id="0229-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_code WHERE code_name = 'Salutation'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_code">
|
||||
<column name="code_name" value="Salutation"/>
|
||||
<column name="is_system_defined" valueBoolean="true"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0229-2">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_code_value WHERE code_id = (SELECT id FROM m_code WHERE code_name = 'Salutation')</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT id, val, pos, true, false FROM m_code
|
||||
CROSS JOIN (VALUES
|
||||
('Mr.', 1),
|
||||
('Mrs.', 2),
|
||||
('Ms.', 3),
|
||||
('Miss', 4),
|
||||
('Dr.', 5),
|
||||
('Shri', 6),
|
||||
('Smt.', 7)
|
||||
) AS t(val, pos)
|
||||
WHERE code_name = 'Salutation';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== Add salutation_cv_id column to m_client ==================== -->
|
||||
<changeSet author="fineract" id="0229-3">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_client" columnName="salutation_cv_id"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="salutation_cv_id" type="BIGINT">
|
||||
<constraints nullable="true" />
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0229-4">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><foreignKeyConstraintExists foreignKeyName="FK_m_client_salutation_cv_id"/></not>
|
||||
</preConditions>
|
||||
<addForeignKeyConstraint baseTableName="m_client" baseColumnNames="salutation_cv_id"
|
||||
referencedTableName="m_code_value" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_salutation_cv_id" />
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- ==================== Create m_client_form60 table ==================== -->
|
||||
<changeSet author="fineract" id="0230-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_form60"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_form60">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="full_name" type="VARCHAR(200)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="date_of_birth" type="DATE"/>
|
||||
<column name="father_name" type="VARCHAR(200)"/>
|
||||
<column name="address" type="VARCHAR(500)"/>
|
||||
<column name="transaction_type" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="transaction_amount" type="DECIMAL(19,6)"/>
|
||||
<column name="estimated_total_income" type="DECIMAL(19,6)"/>
|
||||
<column name="reason_for_no_pan" type="VARCHAR(200)"/>
|
||||
<column name="declaration" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="submitted_on" type="DATE"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_form60" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_form60_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== Add permissions ==================== -->
|
||||
<changeSet author="fineract" id="0230-2">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'CREATE_CLIENTFORM60'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="CREATE_CLIENTFORM60"/>
|
||||
<column name="entity_name" value="CLIENTFORM60"/>
|
||||
<column name="action_name" value="CREATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="READ_CLIENTFORM60"/>
|
||||
<column name="entity_name" value="CLIENTFORM60"/>
|
||||
<column name="action_name" value="READ"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- ==================== Heading Menu Permissions (Top Nav Bar) ==================== -->
|
||||
<changeSet author="fineract" id="0231-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'MENU_INSTITUTION'</sqlCheck>
|
||||
</preConditions>
|
||||
|
||||
<!-- Row 1 — 11 items -->
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_INSTITUTION"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="INSTITUTION"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_TASKS"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="TASKS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_ACCOUNTING"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="ACCOUNTING"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_REPORTS"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="REPORTS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_ADMIN"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="ADMIN"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_SUB_LEDGER"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="SUB_LEDGER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_GENERAL_LEDGER"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="GENERAL_LEDGER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_CASHIER"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="CASHIER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_OFFICER"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="OFFICER"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_DEPOSITS"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="DEPOSITS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_CLEARING"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="CLEARING"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
|
||||
<!-- Row 2 — 11 items -->
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_HRM"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="HRM"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_SHARE"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="SHARE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_OTHERS"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="OTHERS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_NPA"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="NPA"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_ADVANCES"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="ADVANCES"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_INSURANCE"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="INSURANCE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_RBI"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="RBI"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_DIGITAL"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="DIGITAL"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_CONFIG_WIZARD"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="CONFIG_WIZARD"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_CKYC_MGMT"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="CKYC_MGMT"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="heading_menu"/>
|
||||
<column name="code" value="MENU_LETTER_MGMT"/>
|
||||
<column name="entity_name" value="MENU"/>
|
||||
<column name="action_name" value="LETTER_MGMT"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== Left Panel Menu Permissions (Sidebar) ==================== -->
|
||||
<changeSet author="fineract" id="0231-2">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'SIDEBAR_DASHBOARD'</sqlCheck>
|
||||
</preConditions>
|
||||
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_DASHBOARD"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="DASHBOARD"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_NAVIGATION"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="NAVIGATION"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_CKYC"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="CKYC"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_CALENDAR"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="CALENDAR"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_CHECKER_INBOX"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="CHECKER_INBOX"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_COLLECTION_SHEET"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="COLLECTION_SHEET"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_REMITTANCES"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="REMITTANCES"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_ANALYTICS"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="ANALYTICS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_CFCFRMS"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="CFCFRMS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_RBI_INTEGRATION"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="RBI_INTEGRATION"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_RBI_CIRCULARS"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="RBI_CIRCULARS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_NOTIFICATIONS"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="NOTIFICATIONS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_FREQUENT_POSTINGS"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="FREQUENT_POSTINGS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_JOURNAL_ENTRY"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="JOURNAL_ENTRY"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="sidebar_menu"/>
|
||||
<column name="code" value="SIDEBAR_CHART_OF_ACCOUNTS"/>
|
||||
<column name="entity_name" value="SIDEBAR"/>
|
||||
<column name="action_name" value="CHART_OF_ACCOUNTS"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Add first_name and last_name from PAN verification response -->
|
||||
<changeSet author="fineract" id="0232-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_client_kyc" columnName="pan_first_name"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="pan_first_name" type="VARCHAR(200)"/>
|
||||
<column name="pan_last_name" type="VARCHAR(200)"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Auto-promote KYC status to VERIFIED (300) for Individual clients
|
||||
that already have both PAN and Aadhaar verified -->
|
||||
<changeSet author="fineract" id="0233-1" context="default">
|
||||
<sql>
|
||||
UPDATE m_client_kyc
|
||||
SET kyc_status = 300,
|
||||
verified_on = CURRENT_DATE
|
||||
WHERE kyc_type = 1
|
||||
AND pan_verified = true
|
||||
AND aadhaar_verified = true
|
||||
AND kyc_status != 300
|
||||
AND kyc_status != 400;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- Auto-promote KYC status to VERIFIED (300) for Non-Individual clients
|
||||
that already have both PAN and GSTIN verified -->
|
||||
<changeSet author="fineract" id="0233-2" context="default">
|
||||
<sql>
|
||||
UPDATE m_client_kyc
|
||||
SET kyc_status = 300,
|
||||
verified_on = CURRENT_DATE
|
||||
WHERE kyc_type = 2
|
||||
AND pan_verified = true
|
||||
AND gstin_verified = true
|
||||
AND kyc_status != 300
|
||||
AND kyc_status != 400;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0234-1" context="default">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="monthly_income" type="DECIMAL(19,2)" defaultValue="null">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Create the FORM_60_PURPOSE code -->
|
||||
<changeSet author="fineract" id="0235-1" context="default">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'FORM_60_PURPOSE', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'FORM_60_PURPOSE');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- Insert all purpose values -->
|
||||
<changeSet author="fineract" id="0235-2" context="default">
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT id, v.code_value, v.order_position, true, false
|
||||
FROM m_code,
|
||||
(VALUES
|
||||
('Opening Bank Account', 1),
|
||||
('Cash Deposit in Bank', 2),
|
||||
('Cash Withdrawal from Bank', 3),
|
||||
('Fixed Deposit / Term Deposit', 4),
|
||||
('Recurring Deposit', 5),
|
||||
('Purchase of Bank Draft / Pay Order / Bankers Cheque', 6),
|
||||
('Credit Card Application', 7),
|
||||
('Debit Card Application', 8),
|
||||
('Loan Application', 9),
|
||||
('Personal Loan', 10),
|
||||
('Home Loan', 11),
|
||||
('Vehicle Loan', 12),
|
||||
('Education Loan', 13),
|
||||
('Business Loan', 14),
|
||||
('Agricultural Loan', 15),
|
||||
('Gold Loan - New Loan', 16),
|
||||
('Gold Loan - Renewal', 17),
|
||||
('Gold Loan - Top Up', 18),
|
||||
('Gold Loan - Closure / Settlement', 19),
|
||||
('Gold Loan - Interest Payment', 20),
|
||||
('Gold Loan - Partial Payment', 21),
|
||||
('Gold Loan - Pledge of Gold Jewellery', 22),
|
||||
('Gold Loan - Release of Gold Jewellery', 23),
|
||||
('Loan Against Gold Coins', 24),
|
||||
('Loan Against Gold Ornaments', 25),
|
||||
('Agricultural Gold Loan', 26),
|
||||
('Purchase of Motor Vehicle', 27),
|
||||
('Purchase of Immovable Property', 28),
|
||||
('Sale of Immovable Property', 29),
|
||||
('Purchase of Securities / Shares', 30),
|
||||
('Purchase of Mutual Funds', 31),
|
||||
('Opening Demat Account', 32),
|
||||
('Life Insurance Premium Payment', 33),
|
||||
('Foreign Travel Payment', 34),
|
||||
('Purchase of Foreign Currency / Forex', 35),
|
||||
('Hotel or Restaurant Payment above prescribed limit', 36),
|
||||
('Purchase of Goods or Services above prescribed limit', 37),
|
||||
('Purchase of Gold Jewellery', 38),
|
||||
('Purchase of Gold Coins', 39),
|
||||
('Purchase of Gold Bullion', 40),
|
||||
('Sale of Gold Jewellery', 41),
|
||||
('Sale of Gold Coins', 42),
|
||||
('Sale of Gold Bullion', 43),
|
||||
('Gold Investment Purchase', 44),
|
||||
('Sovereign Gold Bond Purchase', 45),
|
||||
('Gold ETF Investment', 46),
|
||||
('Digital Gold Purchase', 47),
|
||||
('Gold Savings Scheme', 48),
|
||||
('Gold Deposit Scheme', 49),
|
||||
('Investment in Bonds / Debentures', 50),
|
||||
('Opening Post Office Savings Account', 51),
|
||||
('Opening Trading Account', 52),
|
||||
('Large Cash Transaction', 53),
|
||||
('High Value Financial Transaction', 54),
|
||||
('Other Financial Transaction', 55)
|
||||
) AS v(code_value, order_position)
|
||||
WHERE m_code.code_name = 'FORM_60_PURPOSE';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||
|
||||
<changeSet id="0236_01_insert_marital_status_code" author="fineract">
|
||||
<sql>
|
||||
INSERT INTO m_code (code_name, is_system_defined)
|
||||
SELECT 'MaritalStatus', false
|
||||
WHERE NOT EXISTS (SELECT 1 FROM m_code WHERE code_name = 'MaritalStatus');
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0236_02_insert_marital_status_values" author="fineract">
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT c.id, v.code_value, v.order_position, true, false
|
||||
FROM m_code c
|
||||
CROSS JOIN (
|
||||
VALUES
|
||||
('Single', 1),
|
||||
('Married', 2),
|
||||
('Unmarried', 3),
|
||||
('Divorced', 4),
|
||||
('Widowed', 5),
|
||||
('Separated', 6),
|
||||
('Annulled', 7),
|
||||
('Live-in Relationship', 8),
|
||||
('Other', 9),
|
||||
('Not Disclosed', 10)
|
||||
) AS v(code_value, order_position)
|
||||
WHERE c.code_name = 'MaritalStatus'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM m_code_value cv
|
||||
WHERE cv.code_id = c.id AND cv.code_value = v.code_value
|
||||
);
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="0236_03_add_marital_status_to_client" author="fineract">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="marital_status_cv_id" type="BIGINT"/>
|
||||
</addColumn>
|
||||
<addForeignKeyConstraint baseTableName="m_client" baseColumnNames="marital_status_cv_id"
|
||||
constraintName="fk_client_marital_status_cv"
|
||||
referencedTableName="m_code_value" referencedColumnNames="id"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Add new columns to m_family_members for CBS requirements -->
|
||||
<changeSet author="fineract" id="0237-1" context="default">
|
||||
<addColumn tableName="m_family_members">
|
||||
<column name="aadhaar_number" type="VARCHAR(12)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="pan_number" type="VARCHAR(10)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="nationality" type="VARCHAR(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="is_alive" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="linked_client_id" type="BIGINT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="marriage_date" type="DATE">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- FK for linked_client_id -->
|
||||
<changeSet author="fineract" id="0237-2" context="default">
|
||||
<addForeignKeyConstraint
|
||||
baseTableName="m_family_members"
|
||||
baseColumnNames="linked_client_id"
|
||||
referencedTableName="m_client"
|
||||
referencedColumnNames="id"
|
||||
constraintName="fk_family_member_linked_client"
|
||||
onDelete="SET NULL"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0238-1" context="default">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="annual_turnover" type="DECIMAL(19,2)" defaultValue="null"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0239-1" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="nationality"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="nationality" type="VARCHAR(100)" defaultValue="null"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-2" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="is_minor"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="is_minor" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-3" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="guardian_client_id"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="guardian_client_id" type="BIGINT" defaultValue="null"/>
|
||||
</addColumn>
|
||||
<addForeignKeyConstraint baseTableName="m_client" baseColumnNames="guardian_client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="fk_client_guardian_client"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-4" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="guardian_relation"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="guardian_relation" type="VARCHAR(50)" defaultValue="null"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-5" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="dip_verified"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="dip_verified" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-6" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="dip_lookup_log_id"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="dip_lookup_log_id" type="BIGINT" defaultValue="null"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-7" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="dip_timestamp"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="dip_timestamp" type="VARCHAR(50)" defaultValue="null"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-8" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="name_verified"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="name_verified" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0239-9" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><columnExists tableName="m_client" columnName="screening_log_id"/></not></preConditions>
|
||||
<addColumn tableName="m_client">
|
||||
<column name="screening_log_id" type="BIGINT" defaultValue="null"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- ==================== 1. Startup India (DPIIT) ==================== -->
|
||||
<changeSet author="fineract" id="0240-1">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_startup_india"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_startup_india">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="dpiit_recognition_number" type="VARCHAR(50)"/>
|
||||
<column name="recognition_date" type="DATE"/>
|
||||
<column name="certificate_expiry_date" type="DATE"/>
|
||||
<column name="entity_stage" type="VARCHAR(30)"/>
|
||||
<column name="sector_as_dpiit" type="VARCHAR(50)"/>
|
||||
<column name="nic_industry_code" type="VARCHAR(20)"/>
|
||||
<column name="tax_exemption_80iac" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="sidbi_fund" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="self_certification" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="fast_track_patent" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="govt_tender_exemption" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="credit_guarantee_cgss" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="psl_loan_limit" type="DECIMAL(19,2)"/>
|
||||
<column name="psl_sub_category" type="VARCHAR(50)"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_startup_india" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_startup_india_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== 2. Mudra Borrower (PMMY) ==================== -->
|
||||
<changeSet author="fineract" id="0240-2">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_mudra_borrower"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_mudra_borrower">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="mudra_category" type="VARCHAR(20)"/>
|
||||
<column name="mudra_loan_account_no" type="VARCHAR(50)"/>
|
||||
<column name="lending_institution_name" type="VARCHAR(100)"/>
|
||||
<column name="sanctioned_amount" type="DECIMAL(19,2)"/>
|
||||
<column name="disbursement_date" type="DATE"/>
|
||||
<column name="loan_purpose" type="VARCHAR(50)"/>
|
||||
<column name="mudra_card_number" type="VARCHAR(20)"/>
|
||||
<column name="current_outstanding" type="DECIMAL(19,2)"/>
|
||||
<column name="repayment_status" type="VARCHAR(30)"/>
|
||||
<column name="npa_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_mudra_borrower" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_mudra_borrower_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== 3. Export Oriented Unit (EOU) ==================== -->
|
||||
<changeSet author="fineract" id="0240-3">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_eou"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_eou">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="eou_registration_number" type="VARCHAR(50)"/>
|
||||
<column name="registered_dc_zone" type="VARCHAR(50)"/>
|
||||
<column name="bond_lut_number" type="VARCHAR(50)"/>
|
||||
<column name="validity_from" type="DATE"/>
|
||||
<column name="validity_to" type="DATE"/>
|
||||
<column name="gstin_eou" type="VARCHAR(15)"/>
|
||||
<column name="annual_export_obligation" type="DECIMAL(19,2)"/>
|
||||
<column name="nfe_target" type="DECIMAL(19,2)"/>
|
||||
<column name="softex_filing_status" type="VARCHAR(20)"/>
|
||||
<column name="brc_submission_status" type="VARCHAR(20)"/>
|
||||
<column name="ad_category_bank_code" type="VARCHAR(20)"/>
|
||||
<column name="fema_declaration_date" type="DATE"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_eou" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_eou_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== 4. Import/Export (IEC / DGFT) ==================== -->
|
||||
<changeSet author="fineract" id="0240-4">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_import_export"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_import_export">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="iec_code" type="VARCHAR(10)"/>
|
||||
<column name="iec_issue_date" type="DATE"/>
|
||||
<column name="iec_status" type="VARCHAR(20)"/>
|
||||
<column name="trade_type" type="VARCHAR(30)"/>
|
||||
<column name="primary_hs_code" type="VARCHAR(20)"/>
|
||||
<column name="key_trading_countries" type="VARCHAR(200)"/>
|
||||
<column name="ad_category_bank_code" type="VARCHAR(20)"/>
|
||||
<column name="ecgc_policy_number" type="VARCHAR(50)"/>
|
||||
<column name="ecgc_policy_expiry" type="DATE"/>
|
||||
<column name="eefc_account_required" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="pcfc_facility_required" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="fema_declaration_obtained" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_import_export" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_import_export_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== 5. Priority Sector Lending (PSL) ==================== -->
|
||||
<changeSet author="fineract" id="0240-5">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_priority_sector"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_priority_sector">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="psl_category" type="VARCHAR(30)"/>
|
||||
<column name="psl_sub_category" type="VARCHAR(50)"/>
|
||||
<column name="psl_loan_purpose_code" type="VARCHAR(10)"/>
|
||||
<column name="psl_eligibility_basis" type="VARCHAR(100)"/>
|
||||
<column name="fidd_reporting_code" type="VARCHAR(20)"/>
|
||||
<column name="psl_sanctioned_limit" type="DECIMAL(19,2)"/>
|
||||
<column name="msme_classification" type="VARCHAR(50)"/>
|
||||
<column name="udyam_registration_number" type="VARCHAR(30)"/>
|
||||
<column name="udyam_registration_date" type="DATE"/>
|
||||
<column name="annual_turnover" type="DECIMAL(19,2)"/>
|
||||
<column name="investment_plant_machinery" type="DECIMAL(19,2)"/>
|
||||
<column name="weaker_section_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="sc_st_borrower_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="women_entrepreneur_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="minority_community_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_priority_sector" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_priority_sector_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== 6. Non-Priority Sector (NPS) ==================== -->
|
||||
<changeSet author="fineract" id="0240-6">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><tableExists tableName="m_client_non_priority_sector"/></not>
|
||||
</preConditions>
|
||||
<createTable tableName="m_client_non_priority_sector">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="industry_sector" type="VARCHAR(50)"/>
|
||||
<column name="nic_code" type="VARCHAR(10)"/>
|
||||
<column name="industry_description" type="VARCHAR(200)"/>
|
||||
<column name="annual_revenue_band" type="VARCHAR(30)"/>
|
||||
<column name="risk_category" type="VARCHAR(20)"/>
|
||||
<column name="customer_risk_score" type="INTEGER"/>
|
||||
<column name="aml_risk_classification" type="VARCHAR(50)"/>
|
||||
<column name="pep_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="aml_watchlist_match" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="adverse_media_flag" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_non_priority_sector" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="FK_m_client_non_priority_sector_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- ==================== 7. Permissions ==================== -->
|
||||
<changeSet author="fineract" id="0240-7">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'READ_ENTITYCLASSIFICATION'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="READ_ENTITYCLASSIFICATION"/>
|
||||
<column name="entity_name" value="ENTITYCLASSIFICATION"/>
|
||||
<column name="action_name" value="READ"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="CREATE_ENTITYCLASSIFICATION"/>
|
||||
<column name="entity_name" value="ENTITYCLASSIFICATION"/>
|
||||
<column name="action_name" value="CREATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="UPDATE_ENTITYCLASSIFICATION"/>
|
||||
<column name="entity_name" value="ENTITYCLASSIFICATION"/>
|
||||
<column name="action_name" value="UPDATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- Table 7: m_client_msme -->
|
||||
<changeSet author="fineract" id="0240-8" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><tableExists tableName="m_client_msme"/></not></preConditions>
|
||||
<createTable tableName="m_client_msme">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="enabled" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="udyam_registration_number" type="VARCHAR(30)"/>
|
||||
<column name="registration_date" type="DATE"/>
|
||||
<column name="registration_status" type="VARCHAR(20)"/>
|
||||
<column name="msme_classification" type="VARCHAR(20)"/>
|
||||
<column name="enterprise_type" type="VARCHAR(30)"/>
|
||||
<column name="nic_activity_code" type="VARCHAR(20)"/>
|
||||
<column name="annual_turnover" type="DECIMAL(19,2)"/>
|
||||
<column name="investment_plant_machinery" type="DECIMAL(19,2)"/>
|
||||
<column name="financial_year_assessment" type="VARCHAR(10)"/>
|
||||
<column name="social_category_owner" type="VARCHAR(30)"/>
|
||||
<column name="gender_primary_owner" type="VARCHAR(20)"/>
|
||||
<column name="no_of_employees" type="INTEGER"/>
|
||||
<column name="women_led_enterprise" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="divyang_pwd_owned" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_msme" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="fk_msme_client_id"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0241-1" context="default">
|
||||
<preConditions onFail="MARK_RAN"><not><tableExists tableName="m_client_nominee"/></not></preConditions>
|
||||
<createTable tableName="m_client_nominee">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="nominee_client_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="relationship_cv_id" type="BIGINT"/>
|
||||
<column name="percentage_share" type="DECIMAL(5,2)"/>
|
||||
<column name="is_active" type="BOOLEAN" defaultValueBoolean="true"/>
|
||||
<column name="nominee_is_minor" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
<column name="guardian_client_id" type="BIGINT"/>
|
||||
<column name="guardian_relationship_cv_id" type="BIGINT"/>
|
||||
<column name="effective_date" type="DATE"/>
|
||||
<column name="remarks" type="VARCHAR(500)"/>
|
||||
<column name="created_by" type="BIGINT"/>
|
||||
<column name="created_on_utc" type="TIMESTAMP"/>
|
||||
<column name="last_modified_by" type="BIGINT"/>
|
||||
<column name="last_modified_on_utc" type="TIMESTAMP"/>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseTableName="m_client_nominee" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="fk_nominee_client_id"/>
|
||||
<addForeignKeyConstraint baseTableName="m_client_nominee" baseColumnNames="nominee_client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="fk_nominee_nominee_client_id"/>
|
||||
<addForeignKeyConstraint baseTableName="m_client_nominee" baseColumnNames="guardian_client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="fk_nominee_guardian_client_id"/>
|
||||
<addForeignKeyConstraint baseTableName="m_client_nominee" baseColumnNames="relationship_cv_id"
|
||||
referencedTableName="m_code_value" referencedColumnNames="id"
|
||||
constraintName="fk_nominee_relationship_cv"/>
|
||||
<addForeignKeyConstraint baseTableName="m_client_nominee" baseColumnNames="guardian_relationship_cv_id"
|
||||
referencedTableName="m_code_value" referencedColumnNames="id"
|
||||
constraintName="fk_nominee_guardian_rel_cv"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Add RELATIONSHIP code if not exists -->
|
||||
<changeSet author="fineract" id="0241-2" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_code WHERE code_name = 'RELATIONSHIP'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_code">
|
||||
<column name="code_name" value="RELATIONSHIP"/>
|
||||
<column name="is_system_defined" valueBoolean="true"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- Add relationship code values -->
|
||||
<changeSet author="fineract" id="0241-3" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_code_value cv JOIN m_code c ON c.id = cv.code_id WHERE c.code_name = 'RELATIONSHIP'</sqlCheck>
|
||||
</preConditions>
|
||||
<sql>
|
||||
INSERT INTO m_code_value (code_id, code_value, order_position, is_active, is_mandatory)
|
||||
SELECT c.id, v.val, v.pos, true, false
|
||||
FROM m_code c
|
||||
CROSS JOIN (VALUES
|
||||
('FATHER', 1), ('MOTHER', 2), ('SPOUSE', 3), ('SON', 4), ('DAUGHTER', 5),
|
||||
('BROTHER', 6), ('SISTER', 7), ('GRANDFATHER', 8), ('GRANDMOTHER', 9),
|
||||
('UNCLE', 10), ('AUNT', 11), ('NEPHEW', 12), ('NIECE', 13),
|
||||
('FATHER_IN_LAW', 14), ('MOTHER_IN_LAW', 15), ('FRIEND', 16), ('OTHER', 17)
|
||||
) AS v(val, pos)
|
||||
WHERE c.code_name = 'RELATIONSHIP';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- Permissions -->
|
||||
<changeSet author="fineract" id="0241-4" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM m_permission WHERE code = 'READ_CLIENTNOMINEE'</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="READ_CLIENTNOMINEE"/>
|
||||
<column name="entity_name" value="CLIENTNOMINEE"/>
|
||||
<column name="action_name" value="READ"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="CREATE_CLIENTNOMINEE"/>
|
||||
<column name="entity_name" value="CLIENTNOMINEE"/>
|
||||
<column name="action_name" value="CREATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="UPDATE_CLIENTNOMINEE"/>
|
||||
<column name="entity_name" value="CLIENTNOMINEE"/>
|
||||
<column name="action_name" value="UPDATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="DELETE_CLIENTNOMINEE"/>
|
||||
<column name="entity_name" value="CLIENTNOMINEE"/>
|
||||
<column name="action_name" value="DELETE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Add marriage_authority column to m_family_members -->
|
||||
<changeSet author="fineract" id="0242-1" context="default">
|
||||
<addColumn tableName="m_family_members">
|
||||
<column name="marriage_authority" type="VARCHAR(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<!-- Add permissions for family member documents (using existing m_document infrastructure) -->
|
||||
<changeSet author="fineract" id="0242-2" context="default">
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="READ_FAMILYMEMBERDOCUMENT"/>
|
||||
<column name="entity_name" value="FAMILYMEMBERDOCUMENT"/>
|
||||
<column name="action_name" value="READ"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="CREATE_FAMILYMEMBERDOCUMENT"/>
|
||||
<column name="entity_name" value="FAMILYMEMBERDOCUMENT"/>
|
||||
<column name="action_name" value="CREATE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_permission">
|
||||
<column name="grouping" value="portfolio"/>
|
||||
<column name="code" value="DELETE_FAMILYMEMBERDOCUMENT"/>
|
||||
<column name="entity_name" value="FAMILYMEMBERDOCUMENT"/>
|
||||
<column name="action_name" value="DELETE"/>
|
||||
<column name="can_maker_checker" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Add nominee_identifier, account_no, account_type to m_client_nominee -->
|
||||
<changeSet author="fineract" id="0243-1" context="default">
|
||||
<addColumn tableName="m_client_nominee">
|
||||
<column name="nominee_identifier" type="VARCHAR(50)">
|
||||
<constraints nullable="true" unique="true" uniqueConstraintName="uq_nominee_identifier"/>
|
||||
</column>
|
||||
<column name="account_no" type="VARCHAR(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="account_type" type="VARCHAR(10)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0243-2" context="default">
|
||||
<createIndex tableName="m_client_nominee" indexName="idx_nominee_account_no_type">
|
||||
<column name="account_no"/>
|
||||
<column name="account_type"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Add alternate mobile number and its DIP verification fields to m_client -->
|
||||
<changeSet author="fineract" id="0244-1" context="default">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="alternate_mobile_no" type="VARCHAR(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0244-2" context="default">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="alternate_dip_verified" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0244-3" context="default">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="alternate_dip_lookup_log_id" type="BIGINT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0244-4" context="default">
|
||||
<addColumn tableName="m_client">
|
||||
<column name="alternate_dip_timestamp" type="VARCHAR(50)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<!-- Add client_id FK column to m_address -->
|
||||
<changeSet author="fineract" id="0245-1" context="default">
|
||||
<addColumn tableName="m_address">
|
||||
<column name="client_id" type="BIGINT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0245-2" context="default">
|
||||
<addForeignKeyConstraint baseTableName="m_address" baseColumnNames="client_id"
|
||||
referencedTableName="m_client" referencedColumnNames="id"
|
||||
constraintName="fk_m_address_client_id"
|
||||
onDelete="SET NULL" onUpdate="CASCADE"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0245-3" context="default">
|
||||
<createIndex tableName="m_address" indexName="idx_m_address_client_id">
|
||||
<column name="client_id"/>
|
||||
</createIndex>
|
||||
</changeSet>
|
||||
|
||||
<!-- Backfill client_id from m_client_address join table for existing records -->
|
||||
<changeSet author="fineract" id="0245-4" context="default">
|
||||
<sql>
|
||||
UPDATE m_address
|
||||
SET client_id = ca.client_id
|
||||
FROM m_client_address ca
|
||||
WHERE m_address.id = ca.address_id
|
||||
AND m_address.client_id IS NULL;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<!-- Seed ADDRESS_TYPE code values (code id=29) -->
|
||||
<changeSet author="fineract" id="0245-5" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">
|
||||
SELECT COUNT(*) FROM m_code_value WHERE code_id = 29
|
||||
</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueNumeric="29"/>
|
||||
<column name="code_value" value="Permanent Address"/>
|
||||
<column name="order_position" valueNumeric="1"/>
|
||||
<column name="code_description" value="Permanent / Home address"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueNumeric="29"/>
|
||||
<column name="code_value" value="Current / Residential"/>
|
||||
<column name="order_position" valueNumeric="2"/>
|
||||
<column name="code_description" value="Current residential address"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueNumeric="29"/>
|
||||
<column name="code_value" value="Office / Work"/>
|
||||
<column name="order_position" valueNumeric="3"/>
|
||||
<column name="code_description" value="Office or workplace address"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueNumeric="29"/>
|
||||
<column name="code_value" value="Correspondence"/>
|
||||
<column name="order_position" valueNumeric="4"/>
|
||||
<column name="code_description" value="Correspondence / mailing address"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- Enable townVillage, countyDistrict and restore street in field configuration -->
|
||||
<changeSet author="fineract" id="0245-6" context="default">
|
||||
<sql>
|
||||
UPDATE m_field_configuration SET is_enabled = true WHERE field = 'townVillage' AND entity = 'ADDRESS';
|
||||
UPDATE m_field_configuration SET is_enabled = true WHERE field = 'countyDistrict' AND entity = 'ADDRESS';
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0245-7" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<sqlCheck expectedResult="0">
|
||||
SELECT COUNT(*) FROM m_field_configuration WHERE entity = 'ADDRESS' AND field = 'street'
|
||||
</sqlCheck>
|
||||
</preConditions>
|
||||
<insert tableName="m_field_configuration">
|
||||
<column name="entity" value="ADDRESS"/>
|
||||
<column name="subentity" value="CLIENT"/>
|
||||
<column name="field" value="street"/>
|
||||
<column name="is_enabled" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
<column name="validation_regex" value=""/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
<!-- Add address_type_id FK column to m_address -->
|
||||
<changeSet author="fineract" id="0245-8" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><columnExists tableName="m_address" columnName="address_type_id"/></not>
|
||||
</preConditions>
|
||||
<addColumn tableName="m_address">
|
||||
<column name="address_type_id" type="INT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0245-9" context="default">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not><foreignKeyConstraintExists foreignKeyName="fk_m_address_address_type_id"/></not>
|
||||
</preConditions>
|
||||
<addForeignKeyConstraint baseTableName="m_address" baseColumnNames="address_type_id"
|
||||
referencedTableName="m_code_value" referencedColumnNames="id"
|
||||
constraintName="fk_m_address_address_type_id"/>
|
||||
</changeSet>
|
||||
|
||||
<!-- Backfill address_type_id from m_client_address -->
|
||||
<changeSet author="fineract" id="0245-10" context="default">
|
||||
<sql>
|
||||
UPDATE m_address
|
||||
SET address_type_id = ca.address_type_id
|
||||
FROM m_client_address ca
|
||||
WHERE m_address.id = ca.address_id
|
||||
AND m_address.address_type_id IS NULL;
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0246-0" context="mysql,mariadb,postgresql">
|
||||
<comment>Widen pan_aadhaar_seeding_status from VARCHAR(5) to VARCHAR(20) to support LINKED/NOT_LINKED values</comment>
|
||||
<modifyDataType tableName="m_client_kyc" columnName="pan_aadhaar_seeding_status" newDataType="VARCHAR(20)"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="fineract" id="0246-1" context="mysql,mariadb,postgresql">
|
||||
<comment>Add extended KYC fields: enterprise/non-individual, KYC document, AML/compliance, account status, Form 60/15</comment>
|
||||
|
||||
<!-- Enterprise / Non-Individual fields -->
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="enterprise_pan" type="VARCHAR(10)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="cin_number" type="VARCHAR(21)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="incorporation_date" type="DATE"/>
|
||||
</addColumn>
|
||||
|
||||
<!-- KYC Document -->
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="kyc_document_name" type="VARCHAR(100)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="kyc_document_number" type="VARCHAR(50)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="kyc_doc_issue_date" type="DATE"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="kyc_doc_expiry_date" type="DATE"/>
|
||||
</addColumn>
|
||||
|
||||
<!-- AML / Compliance flags -->
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="aml_check_status" type="VARCHAR(20)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="banned_entity_match" type="VARCHAR(10)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="willful_defaulter" type="VARCHAR(10)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="ecgc_sal_flag" type="VARCHAR(10)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="dormant_flag" type="VARCHAR(20)"/>
|
||||
</addColumn>
|
||||
|
||||
<!-- Account status / Freeze -->
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="account_status" type="VARCHAR(20)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="freeze_reason" type="VARCHAR(200)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="freeze_authority" type="VARCHAR(100)"/>
|
||||
</addColumn>
|
||||
|
||||
<!-- Form 60 / Form 15G/15H -->
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="form60_type" type="VARCHAR(20)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="form60_date" type="DATE"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="form15_applicable" type="BOOLEAN"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="form15_type" type="VARCHAR(10)"/>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="form15_fy" type="VARCHAR(10)"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0247-1" context="default">
|
||||
<insert tableName="m_code_value">
|
||||
<column name="code_id" valueNumeric="17"/>
|
||||
<column name="code_value" value="HUF (Hindu Undivided Family)"/>
|
||||
<column name="code_description" value="Hindu Undivided Family"/>
|
||||
<column name="order_position" valueNumeric="1"/>
|
||||
<column name="is_active" valueBoolean="true"/>
|
||||
<column name="is_mandatory" valueBoolean="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0248-1" context="default">
|
||||
<addColumn tableName="m_client_kyc">
|
||||
<column name="cin_verified" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="cin_verified_on" type="date"/>
|
||||
<column name="din_number" type="varchar(8)"/>
|
||||
<column name="din_verified" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="din_verified_on" type="date"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd">
|
||||
|
||||
<changeSet author="fineract" id="0249-1" context="default">
|
||||
<addColumn tableName="m_client_non_person">
|
||||
<column name="authorized_signatory" type="varchar(500)"/>
|
||||
<column name="additional_info" type="varchar(500)"/>
|
||||
<column name="date_of_registration" type="date"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user