From 90c21d40c1187c662abda5faf989535fe7ce906c Mon Sep 17 00:00:00 2001 From: Aditya Date: Fri, 20 Mar 2026 18:51:26 +0530 Subject: [PATCH] Add CIF number, Occupation Category & Occupation fields to Client module - Added mandatory unique CIF number (auto-generated as CIF+padded ID) - Added Occupation Category dropdown (6 categories: Salaried, Business, Professional, Agriculture, Student, Others) - Added Occupation dropdown (54 occupations mapped to categories) - Both stored in m_code/m_code_value tables (same pattern as Gender) - Visible in client create/update API and personal data view - Includes Liquibase migrations 0227 & 0228 - Added Postman collection for User Management APIs Co-Authored-By: Claude Opus 4.6 (1M context) --- ...ct_User_Management.postman_collection.json | 607 ++++++++++++++++++ .../client/api/ClientApiConstants.java | 11 +- .../portfolio/client/data/ClientData.java | 112 ++-- .../portfolio/client/domain/Client.java | 74 ++- .../data/ClientApiCollectionConstants.java | 8 +- .../portfolio/client/mapper/ClientMapper.java | 22 + .../ClientReadPlatformServiceImpl.java | 53 +- ...ClientTemplateReadPlatformServiceImpl.java | 12 +- ...WritePlatformServiceJpaRepositoryImpl.java | 65 +- .../db/changelog/tenant/changelog-tenant.xml | 2 + .../parts/0227_add_client_cif_number.xml | 58 ++ .../parts/0228_add_occupation_fields.xml | 167 +++++ 12 files changed, 1116 insertions(+), 75 deletions(-) create mode 100644 Fineract_User_Management.postman_collection.json create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0227_add_client_cif_number.xml create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0228_add_occupation_fields.xml diff --git a/Fineract_User_Management.postman_collection.json b/Fineract_User_Management.postman_collection.json new file mode 100644 index 000000000..0612492e4 --- /dev/null +++ b/Fineract_User_Management.postman_collection.json @@ -0,0 +1,607 @@ +{ + "info": { + "_postman_id": "user-mgmt-collection", + "name": "Fineract - User Management", + "description": "Fineract User Management APIs - Login, Create, Update, Change Password, Delete Users", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "auth": { + "type": "basic", + "basic": [ + { + "key": "username", + "value": "{{username}}", + "type": "string" + }, + { + "key": "password", + "value": "{{password}}", + "type": "string" + } + ] + }, + "variable": [ + { + "key": "baseUrl", + "value": "https://localhost:8443/fineract-provider/api" + }, + { + "key": "username", + "value": "mifos" + }, + { + "key": "password", + "value": "password" + }, + { + "key": "tenantId", + "value": "default" + }, + { + "key": "userId", + "value": "1" + } + ], + "item": [ + { + "name": "Authentication", + "item": [ + { + "name": "Login (Self-Service)", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"mifos\",\n \"password\": \"password\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/self/authentication?returnClientList=true", + "host": ["{{baseUrl}}"], + "path": ["v1", "self", "authentication"], + "query": [ + { + "key": "returnClientList", + "value": "true" + } + ] + }, + "description": "Authenticates a user and returns auth token. Use this for self-service users." + } + }, + { + "name": "Login (Basic Auth Test)", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/users/1", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "1"] + }, + "description": "Test basic auth by fetching user details. If credentials are correct, you'll get a 200 response." + } + } + ] + }, + { + "name": "Users", + "item": [ + { + "name": "Get All Users", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/users", + "host": ["{{baseUrl}}"], + "path": ["v1", "users"] + } + } + }, + { + "name": "Get User by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "{{userId}}"] + } + } + }, + { + "name": "Get User Template", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/users/template", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "template"] + }, + "description": "Returns template data (available offices, roles) needed to create a new user." + } + }, + { + "name": "Create User", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"newuser\",\n \"firstname\": \"New\",\n \"lastname\": \"User\",\n \"email\": \"newuser@example.com\",\n \"officeId\": 1,\n \"roles\": [1],\n \"sendPasswordToEmail\": false,\n \"password\": \"Password@123\",\n \"repeatPassword\": \"Password@123\",\n \"isSelfServiceUser\": false,\n \"passwordNeverExpires\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/users", + "host": ["{{baseUrl}}"], + "path": ["v1", "users"] + } + } + }, + { + "name": "Update User (Change Username)", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"updatedUsername\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "{{userId}}"] + } + } + }, + { + "name": "Update User (Change Password via PUT)", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"NewPassword@123\",\n \"repeatPassword\": \"NewPassword@123\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "{{userId}}"] + } + } + }, + { + "name": "Change Password (Dedicated Endpoint)", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"NewPassword@123\",\n \"repeatPassword\": \"NewPassword@123\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/users/{{userId}}/pwd", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "{{userId}}", "pwd"] + }, + "description": "Dedicated password change endpoint. Must provide both password and repeatPassword." + } + }, + { + "name": "Update User (Full Update)", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"username\": \"updatedUser\",\n \"firstname\": \"Updated\",\n \"lastname\": \"User\",\n \"email\": \"updated@example.com\",\n \"officeId\": 1,\n \"roles\": [1],\n \"password\": \"NewPassword@123\",\n \"repeatPassword\": \"NewPassword@123\",\n \"passwordNeverExpires\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "{{userId}}"] + } + } + }, + { + "name": "Delete User", + "request": { + "method": "DELETE", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/users/{{userId}}", + "host": ["{{baseUrl}}"], + "path": ["v1", "users", "{{userId}}"] + } + } + } + ] + }, + { + "name": "Roles", + "item": [ + { + "name": "Get All Roles", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/roles", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles"] + } + } + }, + { + "name": "Get Role by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/roles/1", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1"] + } + } + }, + { + "name": "Get Role Permissions", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/roles/1/permissions", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1", "permissions"] + } + } + }, + { + "name": "Create Role", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"New Role\",\n \"description\": \"A new custom role\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/roles", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles"] + } + } + }, + { + "name": "Update Role", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Updated Role\",\n \"description\": \"Updated description\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/roles/1", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1"] + } + } + }, + { + "name": "Update Role Permissions", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"permissions\": {\n \"READ_USER\": true,\n \"CREATE_USER\": true,\n \"UPDATE_USER\": true\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/roles/1/permissions", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1", "permissions"] + } + } + }, + { + "name": "Enable Role", + "request": { + "method": "POST", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/roles/1?command=enable", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1"], + "query": [ + { + "key": "command", + "value": "enable" + } + ] + } + } + }, + { + "name": "Disable Role", + "request": { + "method": "POST", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/roles/1?command=disable", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1"], + "query": [ + { + "key": "command", + "value": "disable" + } + ] + } + } + }, + { + "name": "Delete Role", + "request": { + "method": "DELETE", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/roles/1", + "host": ["{{baseUrl}}"], + "path": ["v1", "roles", "1"] + } + } + } + ] + }, + { + "name": "Password Preferences", + "item": [ + { + "name": "Get Password Preferences", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/passwordpreferences", + "host": ["{{baseUrl}}"], + "path": ["v1", "passwordpreferences"] + } + } + }, + { + "name": "Get Password Preferences Template", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/passwordpreferences/template", + "host": ["{{baseUrl}}"], + "path": ["v1", "passwordpreferences", "template"] + } + } + }, + { + "name": "Update Password Preferences", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"validationPolicyId\": 2\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/passwordpreferences", + "host": ["{{baseUrl}}"], + "path": ["v1", "passwordpreferences"] + } + } + } + ] + }, + { + "name": "Permissions", + "item": [ + { + "name": "Get All Permissions", + "request": { + "method": "GET", + "header": [ + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "url": { + "raw": "{{baseUrl}}/v1/permissions", + "host": ["{{baseUrl}}"], + "path": ["v1", "permissions"] + } + } + }, + { + "name": "Update Permissions (Maker-Checker)", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "fineract-platform-tenantid", + "value": "{{tenantId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"permissions\": {\n \"CREATE_USER_MAKER\": true,\n \"CREATE_USER_CHECKER\": true\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/v1/permissions", + "host": ["{{baseUrl}}"], + "path": ["v1", "permissions"] + } + } + } + ] + } + ] +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java index 8835b7a4d..ffe4315d3 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java @@ -61,6 +61,9 @@ public class ClientApiConstants { public static final String CLIENT_TYPE = "ClientType"; public static final String CLIENT_CLASSIFICATION = "ClientClassification"; + public static final String OCCUPATION_CATEGORY = "OccupationCategory"; + public static final String OCCUPATION = "Occupation"; + public static final String CLIENT_NON_PERSON_CONSTITUTION = "Constitution"; public static final String CLIENT_NON_PERSON_MAIN_BUSINESS_LINE = "Main Business Line"; @@ -78,6 +81,7 @@ public class ClientApiConstants { public static final String idParamName = "id"; public static final String groupIdParamName = "groupId"; public static final String accountNoParamName = "accountNo"; + public static final String cifNumberParamName = "cifNumber"; public static final String externalIdParamName = "externalId"; public static final String mobileNoParamName = "mobileNo"; public static final String emailAddressParamName = "emailAddress"; @@ -112,6 +116,10 @@ public class ClientApiConstants { public static final String clientTypeParamName = "clientType"; public static final String clientClassificationIdParamName = "clientClassificationId"; public static final String clientClassificationParamName = "clientClassification"; + public static final String occupationCategoryIdParamName = "occupationCategoryId"; + public static final String occupationCategoryParamName = "occupationCategory"; + public static final String occupationIdParamName = "occupationId"; + public static final String occupationParamName = "occupation"; public static final String legalFormIdParamName = "legalFormId"; public static final String legalFormParamName = "legalForm"; // request parameters for payment details @@ -193,11 +201,12 @@ public class ClientApiConstants { * parameters to match those of request parameters. */ protected static final Set CLIENT_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName, accountNoParamName, - externalIdParamName, statusParamName, activeParamName, activationDateParamName, firstnameParamName, middlenameParamName, + cifNumberParamName, externalIdParamName, statusParamName, activeParamName, activationDateParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, displayNameParamName, mobileNoParamName, emailAddressParamName, officeIdParamName, officeNameParamName, transferToOfficeIdParamName, transferToOfficeNameParamName, hierarchyParamName, imageIdParamName, imagePresentParamName, staffIdParamName, staffNameParamName, timelineParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName, dateOfBirthParamName, genderParamName, clientTypeParamName, clientClassificationParamName, + occupationCategoryParamName, occupationParamName, legalFormParamName, clientNonPersonDetailsParamName, isStaffParamName, legalFormParamName)); protected static final Set CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(chargeIdParamName, diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java index e0f0dc3be..194cfcc60 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java @@ -51,6 +51,7 @@ public final class ClientData implements Comparable, Serializable { private Long id; private String accountNo; + private String cifNumber; private ExternalId externalId; private EnumOptionData status; @@ -70,6 +71,8 @@ public final class ClientData implements Comparable, Serializable { private CodeValueData gender; private CodeValueData clientType; private CodeValueData clientClassification; + private CodeValueData occupationCategory; + private CodeValueData occupation; private Boolean isStaff; private Long officeId; @@ -102,6 +105,8 @@ public final class ClientData implements Comparable, Serializable { private Collection genderOptions; private Collection clientTypeOptions; private Collection clientClassificationOptions; + private Collection occupationCategoryOptions; + private Collection occupationOptions; private Collection clientNonPersonConstitutionOptions; private Collection clientNonPersonMainBusinessLineOptions; private List clientLegalFormOptions; @@ -281,6 +286,7 @@ public final class ClientData implements Comparable, Serializable { final Collection staffOptions, final Collection narrations, final Collection genderOptions, final Collection savingProductOptions, final Collection clientTypeOptions, final Collection clientClassificationOptions, + final Collection occupationCategoryOptions, final Collection occupationOptions, final Collection clientNonPersonConstitutionOptions, final Collection clientNonPersonMainBusinessLineOptions, final List clientLegalFormOptions, final ClientFamilyMembersData familyMemberOptions, final Collection address, final Boolean isAddressEnabled, @@ -317,64 +323,83 @@ public final class ClientData implements Comparable, Serializable { final Boolean isStaff = false; final ClientNonPersonData clientNonPersonDetails = null; final Set clientCollateralManagements = null; - return new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, - middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, joinedDate, imageId, - staffId, staffName, officeOptions, groups, staffOptions, narrations, genderOptions, timeline, savingProductOptions, + ClientData data = new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, + firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, joinedDate, + imageId, staffId, staffName, officeOptions, groups, staffOptions, narrations, genderOptions, timeline, savingProductOptions, savingsProductId, savingsProductName, savingsAccountId, savingAccountOptions, clientType, clientClassification, clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientNonPersonDetails, clientLegalFormOptions, familyMemberOptions, legalForm, address, isAddressEnabled, datatables, isStaff, clientCollateralManagements); + data.occupationCategoryOptions = occupationCategoryOptions; + data.occupationOptions = occupationOptions; + return data; } public static ClientData templateOnTop(final ClientData clientData, final ClientData templateData) { final Set clientCollateralManagements = null; - return new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, clientData.officeName, - clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, - clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, - clientData.emailAddress, clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, - clientData.staffId, clientData.staffName, templateData.officeOptions, clientData.groups, templateData.staffOptions, - templateData.narrations, templateData.genderOptions, clientData.timeline, templateData.savingProductOptions, - clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, clientData.savingAccountOptions, - clientData.clientType, clientData.clientClassification, templateData.clientTypeOptions, + ClientData data = new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, + clientData.officeName, clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, + clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, + clientData.mobileNo, clientData.emailAddress, clientData.dateOfBirth, clientData.gender, clientData.activationDate, + clientData.imageId, clientData.staffId, clientData.staffName, templateData.officeOptions, clientData.groups, + templateData.staffOptions, templateData.narrations, templateData.genderOptions, clientData.timeline, + templateData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, + clientData.savingAccountOptions, clientData.clientType, clientData.clientClassification, templateData.clientTypeOptions, templateData.clientClassificationOptions, templateData.clientNonPersonConstitutionOptions, templateData.clientNonPersonMainBusinessLineOptions, clientData.clientNonPersonDetails, templateData.clientLegalFormOptions, templateData.familyMemberOptions, clientData.legalForm, clientData.address, clientData.isAddressEnabled, null, clientData.isStaff, clientCollateralManagements); + data.cifNumber = clientData.cifNumber; + data.occupationCategory = clientData.occupationCategory; + data.occupation = clientData.occupation; + data.occupationCategoryOptions = templateData.occupationCategoryOptions; + data.occupationOptions = templateData.occupationOptions; + return data; } public static ClientData templateWithSavingAccountOptions(final ClientData clientData, final Collection savingAccountOptions) { final Set clientCollateralManagements = null; - return new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, clientData.officeName, - clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, - clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, - clientData.emailAddress, clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, - clientData.staffId, clientData.staffName, clientData.officeOptions, clientData.groups, clientData.staffOptions, - clientData.narrations, clientData.genderOptions, clientData.timeline, clientData.savingProductOptions, - clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, savingAccountOptions, - clientData.clientType, clientData.clientClassification, clientData.clientTypeOptions, + ClientData data = new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, + clientData.officeName, clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, + clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, + clientData.mobileNo, clientData.emailAddress, clientData.dateOfBirth, clientData.gender, clientData.activationDate, + clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, clientData.groups, + clientData.staffOptions, clientData.narrations, clientData.genderOptions, clientData.timeline, + clientData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId, + savingAccountOptions, clientData.clientType, clientData.clientClassification, clientData.clientTypeOptions, clientData.clientClassificationOptions, clientData.clientNonPersonConstitutionOptions, clientData.clientNonPersonMainBusinessLineOptions, clientData.clientNonPersonDetails, clientData.clientLegalFormOptions, clientData.familyMemberOptions, clientData.legalForm, clientData.address, clientData.isAddressEnabled, null, clientData.isStaff, clientCollateralManagements); + data.cifNumber = clientData.cifNumber; + data.occupationCategory = clientData.occupationCategory; + data.occupation = clientData.occupation; + data.occupationCategoryOptions = clientData.occupationCategoryOptions; + data.occupationOptions = clientData.occupationOptions; + return data; } public static ClientData setParentGroups(final ClientData clientData, final Collection parentGroups, final Set clientCollateralManagements) { - return new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, clientData.officeName, - clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename, - clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, - clientData.emailAddress, clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, - clientData.staffId, clientData.staffName, clientData.officeOptions, parentGroups, clientData.staffOptions, null, null, - clientData.timeline, clientData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, - clientData.savingsAccountId, clientData.savingAccountOptions, clientData.clientType, clientData.clientClassification, - clientData.clientTypeOptions, clientData.clientClassificationOptions, clientData.clientNonPersonConstitutionOptions, - clientData.clientNonPersonMainBusinessLineOptions, clientData.clientNonPersonDetails, clientData.clientLegalFormOptions, - clientData.familyMemberOptions, clientData.legalForm, clientData.address, clientData.isAddressEnabled, null, - clientData.isStaff, clientCollateralManagements); + ClientData data = new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, + clientData.officeName, clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, + clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, + clientData.mobileNo, clientData.emailAddress, clientData.dateOfBirth, clientData.gender, clientData.activationDate, + clientData.imageId, clientData.staffId, clientData.staffName, clientData.officeOptions, parentGroups, + clientData.staffOptions, null, null, clientData.timeline, clientData.savingProductOptions, clientData.savingsProductId, + clientData.savingsProductName, clientData.savingsAccountId, clientData.savingAccountOptions, clientData.clientType, + clientData.clientClassification, clientData.clientTypeOptions, clientData.clientClassificationOptions, + clientData.clientNonPersonConstitutionOptions, clientData.clientNonPersonMainBusinessLineOptions, + clientData.clientNonPersonDetails, clientData.clientLegalFormOptions, clientData.familyMemberOptions, clientData.legalForm, + clientData.address, clientData.isAddressEnabled, null, clientData.isStaff, clientCollateralManagements); + data.cifNumber = clientData.cifNumber; + data.occupationCategory = clientData.occupationCategory; + data.occupation = clientData.occupation; + return data; } @@ -484,14 +509,15 @@ public final class ClientData implements Comparable, Serializable { return lookup(id, displayName, officeId, officeName); } - public static ClientData instance(final String accountNo, final EnumOptionData status, final CodeValueData subStatus, - final Long officeId, final String officeName, final Long transferToOfficeId, final String transferToOfficeName, final Long id, - final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, - final ExternalId externalId, final String mobileNo, final String emailAddress, final LocalDate dateOfBirth, - final CodeValueData gender, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final ClientTimelineData timeline, final Long savingsProductId, final String savingsProductName, final Long savingsAccountId, - final CodeValueData clientType, final CodeValueData clientClassification, final EnumOptionData legalForm, - final ClientNonPersonData clientNonPerson, final Boolean isStaff) { + public static ClientData instance(final String accountNo, final String cifNumber, final EnumOptionData status, + final CodeValueData subStatus, final Long officeId, final String officeName, final Long transferToOfficeId, + final String transferToOfficeName, final Long id, final String firstname, final String middlename, final String lastname, + final String fullname, final String displayName, final ExternalId externalId, final String mobileNo, + final String emailAddress, final LocalDate dateOfBirth, final CodeValueData gender, final LocalDate activationDate, + final Long imageId, final Long staffId, final String staffName, final ClientTimelineData timeline, + final Long savingsProductId, final String savingsProductName, final Long savingsAccountId, final CodeValueData clientType, + final CodeValueData clientClassification, final CodeValueData occupationCategory, final CodeValueData occupation, + final EnumOptionData legalForm, final ClientNonPersonData clientNonPerson, final Boolean isStaff) { final Collection allowedOffices = null; final Collection groups = null; @@ -505,12 +531,16 @@ public final class ClientData implements Comparable, Serializable { final Collection clientNonPersonMainBusinessLineOptions = null; final List clientLegalFormOptions = null; final ClientFamilyMembersData familyMemberOptions = null; - return new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname, - middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, activationDate, - imageId, staffId, staffName, allowedOffices, groups, staffOptions, closureReasons, genderOptions, timeline, + ClientData data = new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, + firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, + activationDate, imageId, staffId, staffName, allowedOffices, groups, staffOptions, closureReasons, genderOptions, timeline, savingProductOptions, savingsProductId, savingsProductName, savingsAccountId, null, clientType, clientClassification, clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientNonPerson, clientLegalFormOptions, familyMemberOptions, legalForm, null, null, null, isStaff, null); + data.cifNumber = cifNumber; + data.occupationCategory = occupationCategory; + data.occupation = occupation; + return data; } diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java index 951128679..78e07d935 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java @@ -56,12 +56,16 @@ import org.apache.fineract.useradministration.domain.AppUser; @Getter @Setter @Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE"), // + @UniqueConstraint(columnNames = { "cif_number" }, name = "cif_number_UNIQUE"), // @UniqueConstraint(columnNames = { "mobile_no" }, name = "mobile_no_UNIQUE") }) public class Client extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "account_no", length = 20, unique = true, nullable = false) private String accountNumber; + @Column(name = "cif_number", length = 20, unique = true, nullable = false) + private String cifNumber; + @ManyToOne @JoinColumn(name = "office_id", nullable = false) private Office office; @@ -189,6 +193,14 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom { @JoinColumn(name = "client_classification_cv_id") private CodeValue clientClassification; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "occupation_category_cv_id") + private CodeValue occupationCategory; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "occupation_cv_id") + private CodeValue occupation; + @Column(name = "legal_form_enum") private Integer legalForm; @@ -206,24 +218,26 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom { protected Set identifiers = new HashSet<>(); public static Client instance(final AppUser currentUser, final ClientStatus status, final Office office, final Group clientParentGroup, - final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname, - final LocalDate activationDate, final LocalDate officeJoiningDate, final ExternalId externalId, final String mobileNo, - final String emailAddress, final Staff staff, final LocalDate submittedOnDate, final Long savingsProductId, - final Long savingsAccountId, final LocalDate dateOfBirth, final CodeValue gender, final CodeValue clientType, - final CodeValue clientClassification, final Integer legalForm, final Boolean isStaff) { - return new Client(currentUser, status, office, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, + final String accountNo, final String cifNumber, final String firstname, final String middlename, final String lastname, + final String fullname, final LocalDate activationDate, final LocalDate officeJoiningDate, final ExternalId externalId, + final String mobileNo, final String emailAddress, final Staff staff, final LocalDate submittedOnDate, + final Long savingsProductId, final Long savingsAccountId, final LocalDate dateOfBirth, final CodeValue gender, + final CodeValue clientType, final CodeValue clientClassification, final CodeValue occupationCategory, + final CodeValue occupation, final Integer legalForm, final Boolean isStaff) { + return new Client(currentUser, status, office, clientParentGroup, accountNo, cifNumber, firstname, middlename, lastname, fullname, activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff, submittedOnDate, savingsProductId, - savingsAccountId, dateOfBirth, gender, clientType, clientClassification, legalForm, isStaff); + savingsAccountId, dateOfBirth, gender, clientType, clientClassification, occupationCategory, occupation, legalForm, isStaff); } protected Client() {} private Client(final AppUser currentUser, final ClientStatus status, final Office office, final Group clientParentGroup, - final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname, - final LocalDate activationDate, final LocalDate officeJoiningDate, final ExternalId externalId, final String mobileNo, - final String emailAddress, final Staff staff, final LocalDate submittedOnDate, final Long savingsProductId, - final Long savingsAccountId, final LocalDate dateOfBirth, final CodeValue gender, final CodeValue clientType, - final CodeValue clientClassification, final Integer legalForm, final Boolean isStaff) { + final String accountNo, final String cifNumber, final String firstname, final String middlename, final String lastname, + final String fullname, final LocalDate activationDate, final LocalDate officeJoiningDate, final ExternalId externalId, + final String mobileNo, final String emailAddress, final Staff staff, final LocalDate submittedOnDate, + final Long savingsProductId, final Long savingsAccountId, final LocalDate dateOfBirth, final CodeValue gender, + final CodeValue clientType, final CodeValue clientClassification, final CodeValue occupationCategory, + final CodeValue occupation, final Integer legalForm, final Boolean isStaff) { if (StringUtils.isBlank(accountNo)) { this.accountNumber = new RandomPasswordGenerator(19).generate(); @@ -231,6 +245,12 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom { this.accountNumber = accountNo; } + if (StringUtils.isBlank(cifNumber)) { + this.cifNumber = new RandomPasswordGenerator(19).generate(); + } else { + this.cifNumber = cifNumber; + } + this.submittedOnDate = submittedOnDate; this.status = status.getValue(); @@ -285,6 +305,8 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom { this.clientType = clientType; this.clientClassification = clientClassification; + this.occupationCategory = occupationCategory; + this.occupation = occupation; this.setLegalForm(legalForm); deriveDisplayName(); @@ -324,6 +346,10 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom { this.accountNumber = accountIdentifier; } + public void updateCifNumber(final String cifNumber) { + this.cifNumber = cifNumber; + } + public void activate(final AppUser currentUser, final DateTimeFormatter formatter, final LocalDate activationLocalDate) { if (isActive()) { @@ -646,6 +672,30 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom { this.clientClassification = clientClassification; } + public Long occupationCategoryId() { + Long occupationCategoryId = null; + if (this.occupationCategory != null) { + occupationCategoryId = this.occupationCategory.getId(); + } + return occupationCategoryId; + } + + public void updateOccupationCategory(CodeValue occupationCategory) { + this.occupationCategory = occupationCategory; + } + + public Long occupationId() { + Long occupationId = null; + if (this.occupation != null) { + occupationId = this.occupation.getId(); + } + return occupationId; + } + + public void updateOccupation(CodeValue occupation) { + this.occupation = occupation; + } + public void updateGender(CodeValue gender) { this.gender = gender; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java index bc727f25a..3d16875af 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java @@ -26,10 +26,11 @@ import org.apache.fineract.portfolio.client.api.ClientApiConstants; public class ClientApiCollectionConstants extends ClientApiConstants { protected static final Set CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(familyMembers, address, - localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, mobileNoParamName, + localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, cifNumberParamName, externalIdParamName, mobileNoParamName, emailAddressParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName, staffIdParamName, submittedOnDateParamName, savingsProductIdParamName, dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName, + occupationCategoryIdParamName, occupationIdParamName, clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, datatables, isStaffParamName, kycParamName)); protected static final Set CLIENT_NON_PERSON_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>( @@ -41,8 +42,9 @@ public class ClientApiCollectionConstants extends ClientApiConstants { middlenameParamName, lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName, - dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName, submittedOnDateParamName, - clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, isStaffParamName)); + dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName, + occupationCategoryIdParamName, occupationIdParamName, + submittedOnDateParamName, clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, isStaffParamName)); protected static final Set CLIENT_NON_PERSON_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>( Arrays.asList(localeParamName, dateFormatParamName, incorpNumberParamName, remarksParamName, incorpValidityTillParamName, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java index f8849cfaf..97d46f9c7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java @@ -52,6 +52,10 @@ public interface ClientMapper { @Mapping(target = "savingsAccountId", source = "savingsAccountId") @Mapping(target = "clientType", source = "source", qualifiedByName = "clientTypeCode") @Mapping(target = "clientClassification", source = "source", qualifiedByName = "clientClassificationCode") + @Mapping(target = "occupationCategory", source = "source", qualifiedByName = "occupationCategoryCode") + @Mapping(target = "occupation", source = "source", qualifiedByName = "occupationCode") + @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 = "imagePresent", ignore = true) @@ -100,6 +104,24 @@ public interface ClientMapper { return CodeValueData.instance(code.getId(), code.getLabel()); } + @Named("occupationCategoryCode") + default CodeValueData occupationCategoryCode(Client client) { + final CodeValue code = client.getOccupationCategory(); + if (code == null) { + return null; + } + return CodeValueData.instance(code.getId(), code.getLabel()); + } + + @Named("occupationCode") + default CodeValueData occupationCode(Client client) { + final CodeValue code = client.getOccupation(); + if (code == null) { + return null; + } + return CodeValueData.instance(code.getId(), code.getLabel()); + } + @Named("clientSubStatusCode") default CodeValueData clientSubStatusCode(Client client) { final CodeValue code = client.getSubStatus(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java index 6ddcd3a1d..74cfd91ea 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -296,7 +296,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append( - "c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, "); + "c.id as id, c.account_no as accountNo, c.cif_number as cifNumber, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, "); sqlBuilder.append( "cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, "); sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, "); @@ -313,6 +313,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService sqlBuilder.append("c.client_classification_cv_id as classificationId, "); sqlBuilder.append("cvclassification.code_value as classificationValue, "); sqlBuilder.append("c.legal_form_enum as legalFormEnum, "); + sqlBuilder.append("c.occupation_category_cv_id as occupationCategoryId, "); + 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.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, "); @@ -355,6 +359,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService 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_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 "); @@ -369,6 +375,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLException { final String accountNo = rs.getString("accountNo"); + final String cifNumber = rs.getString("cifNumber"); final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); final EnumOptionData status = ClientEnumerations.status(statusEnum); @@ -438,6 +445,14 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService legalForm = ClientEnumerations.legalForm(legalFormEnum); } + final Long occupationCategoryId = JdbcSupport.getLong(rs, "occupationCategoryId"); + final String occupationCategoryValue = rs.getString("occupationCategoryValue"); + final CodeValueData occupationCategory = CodeValueData.instance(occupationCategoryId, occupationCategoryValue); + + final Long occupationId = JdbcSupport.getLong(rs, "occupationId"); + final String occupationValue = rs.getString("occupationValue"); + final CodeValueData occupation = CodeValueData.instance(occupationId, occupationValue); + final Long constitutionId = JdbcSupport.getLong(rs, "constitutionId"); final String constitutionValue = rs.getString("constitutionValue"); final CodeValueData constitution = CodeValueData.instance(constitutionId, constitutionValue); @@ -455,10 +470,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService submittedByLastname, activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname); - return ClientData.instance(accountNo, 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, legalForm, clientNonPerson, isStaff); + return 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); } } @@ -577,7 +592,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final Collection clientNonPersonMainBusinessLineOptions = null; final List clientLegalFormOptions = null; return ClientData.template(null, null, null, null, narrations, null, null, clientTypeOptions, clientClassificationOptions, - clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, null, null, null, null); + null, null, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, null, null, + null, null); } @Override @@ -609,7 +625,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final StringBuilder builder = new StringBuilder(400); builder.append( - "c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, "); + "c.id as id, c.account_no as accountNo, c.cif_number as cifNumber, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, "); builder.append( "cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, "); builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, "); @@ -626,6 +642,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService builder.append("c.client_classification_cv_id as classificationId, "); builder.append("cvclassification.code_value as classificationValue, "); builder.append("c.legal_form_enum as legalFormEnum, "); + builder.append("c.occupation_category_cv_id as occupationCategoryId, "); + 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.submittedon_date as submittedOnDate, "); builder.append("sbu.username as submittedByUsername, "); @@ -667,6 +687,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService 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_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 "); @@ -681,6 +703,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLException { final String accountNo = rs.getString("accountNo"); + final String cifNumber = rs.getString("cifNumber"); final Integer statusEnum = JdbcSupport.getInteger(rs, "statusEnum"); final EnumOptionData status = ClientEnumerations.status(statusEnum); @@ -749,6 +772,14 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService legalForm = ClientEnumerations.legalForm(legalFormEnum); } + final Long occupationCategoryId = JdbcSupport.getLong(rs, "occupationCategoryId"); + final String occupationCategoryValue = rs.getString("occupationCategoryValue"); + final CodeValueData occupationCategory = CodeValueData.instance(occupationCategoryId, occupationCategoryValue); + + final Long occupationId = JdbcSupport.getLong(rs, "occupationId"); + final String occupationValue = rs.getString("occupationValue"); + final CodeValueData occupation = CodeValueData.instance(occupationId, occupationValue); + final Long constitutionId = JdbcSupport.getLong(rs, "constitutionId"); final String constitutionValue = rs.getString("constitutionValue"); final CodeValueData constitution = CodeValueData.instance(constitutionId, constitutionValue); @@ -766,10 +797,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService submittedByLastname, activationDate, activatedByUsername, activatedByFirstname, activatedByLastname, closedOnDate, closedByUsername, closedByFirstname, closedByLastname); - return ClientData.instance(accountNo, 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, legalForm, clientNonPerson, isStaff); + return 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); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java index 7bb563cee..78a82aca6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java @@ -105,6 +105,12 @@ public class ClientTemplateReadPlatformServiceImpl implements ClientTemplateRead final List clientClassificationOptions = new ArrayList<>( this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_CLASSIFICATION)); + final List occupationCategoryOptions = new ArrayList<>( + this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.OCCUPATION_CATEGORY)); + + final List occupationOptions = new ArrayList<>( + this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.OCCUPATION)); + final List clientNonPersonConstitutionOptions = new ArrayList<>( this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_NON_PERSON_CONSTITUTION)); @@ -117,9 +123,9 @@ public class ClientTemplateReadPlatformServiceImpl implements ClientTemplateRead EntityTables.CLIENT.getName(), null); return ClientData.template(defaultOfficeId, LocalDate.now(DateUtils.getDateTimeZoneOfTenant()), offices, staffOptions, null, - genderOptions, savingsProductDatas, clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions, - clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, familyMemberOptions, - new ArrayList(Arrays.asList(address)), isAddressEnabled, datatableTemplates); + genderOptions, savingsProductDatas, clientTypeOptions, clientClassificationOptions, occupationCategoryOptions, + occupationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, + familyMemberOptions, new ArrayList(Arrays.asList(address)), isAddressEnabled, datatableTemplates); } private Long defaultToUsersOfficeIfNull(final Long officeId) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index b092ee0e8..089e6ffb5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -171,6 +171,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final String accountNo = command.stringValueOfParameterNamed("accountNo"); throw new PlatformDataIntegrityException("error.msg.client.duplicate.accountNo", "Client with accountNo `" + accountNo + "` already exists", "accountNo", accountNo); + } else if (realCause.getMessage().contains("cif_number_UNIQUE") || realCause.getMessage().contains("cif_number")) { + final String cifNumber = command.stringValueOfParameterNamed("cifNumber"); + throw new PlatformDataIntegrityException("error.msg.client.duplicate.cifNumber", + "Client with cifNumber `" + cifNumber + "` already exists", "cifNumber", cifNumber); } else if (realCause.getMessage().contains("mobile_no")) { final String mobileNo = command.stringValueOfParameterNamed("mobileNo"); throw new PlatformDataIntegrityException("error.msg.client.duplicate.mobileNo", @@ -230,6 +234,20 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP .findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_CLASSIFICATION, clientClassificationId); } + CodeValue occupationCategory = null; + final Long occupationCategoryId = command.longValueOfParameterNamed(ClientApiConstants.occupationCategoryIdParamName); + if (occupationCategoryId != null) { + occupationCategory = this.codeValueRepository + .findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.OCCUPATION_CATEGORY, occupationCategoryId); + } + + CodeValue occupation = null; + final Long occupationId = command.longValueOfParameterNamed(ClientApiConstants.occupationIdParamName); + if (occupationId != null) { + occupation = this.codeValueRepository + .findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.OCCUPATION, occupationId); + } + final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName); if (savingsProductId != null) { this.savingsProductRepository.findById(savingsProductId) @@ -250,6 +268,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 emailAddress = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName); final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName); @@ -284,10 +303,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final ExternalId externalId = externalIdFactory.createFromCommand(command, ClientApiConstants.externalIdParamName); - final Client newClient = Client.instance(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, - lastname, fullname, activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff, submittedOnDate, - savingsProductId, savingsAccountId, dataOfBirth, gender, clientType, clientClassification, legalForm.getValue(), - isStaff); + 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); // Account Number generation this.clientRepository.saveAndFlush(newClient); @@ -297,6 +316,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP this.clientRepository.saveAndFlush(newClient); } + // CIF Number generation - auto-generate with CIF prefix if not provided + if (StringUtils.isBlank(cifNumber)) { + final String generatedCif = "CIF" + StringUtils.leftPad(newClient.getId().toString(), 10, '0'); + newClient.updateCifNumber(generatedCif); + this.clientRepository.saveAndFlush(newClient); + } + boolean rollbackTransaction = false; if (newClient.isActive()) { validateParentGroupRulesBeforeClientActivation(newClient); @@ -508,6 +534,17 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP changes.put(ClientApiConstants.clientClassificationIdParamName, newValue); } + if (command.isChangeInLongParameterNamed(ClientApiConstants.occupationCategoryIdParamName, + clientForUpdate.occupationCategoryId())) { + final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.occupationCategoryIdParamName); + changes.put(ClientApiConstants.occupationCategoryIdParamName, newValue); + } + + if (command.isChangeInLongParameterNamed(ClientApiConstants.occupationIdParamName, clientForUpdate.occupationId())) { + final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.occupationIdParamName); + changes.put(ClientApiConstants.occupationIdParamName, newValue); + } + if (command.isChangeInIntegerParameterNamed(ClientApiConstants.legalFormIdParamName, clientForUpdate.getLegalForm())) { final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName); if (newValue != null) { @@ -625,6 +662,26 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP clientForUpdate.updateClientClassification(newCodeVal); } + if (changes.containsKey(ClientApiConstants.occupationCategoryIdParamName)) { + final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.occupationCategoryIdParamName); + CodeValue newCodeVal = null; + if (newValue != null) { + newCodeVal = this.codeValueRepository + .findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.OCCUPATION_CATEGORY, newValue); + } + clientForUpdate.updateOccupationCategory(newCodeVal); + } + + if (changes.containsKey(ClientApiConstants.occupationIdParamName)) { + final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.occupationIdParamName); + CodeValue newCodeVal = null; + if (newValue != null) { + newCodeVal = this.codeValueRepository + .findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.OCCUPATION, newValue); + } + clientForUpdate.updateOccupation(newCodeVal); + } + if (!changes.isEmpty()) { this.clientRepository.saveAndFlush(clientForUpdate); } diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 990322cd2..a361f1e7e 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -245,4 +245,6 @@ + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0227_add_client_cif_number.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0227_add_client_cif_number.xml new file mode 100644 index 000000000..50581fdc7 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0227_add_client_cif_number.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + UPDATE m_client SET cif_number = CONCAT('CIF', LPAD(CAST(id AS VARCHAR), 10, '0')) WHERE cif_number IS NULL + + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0228_add_occupation_fields.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0228_add_occupation_fields.xml new file mode 100644 index 000000000..f5989fe73 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0228_add_occupation_fields.xml @@ -0,0 +1,167 @@ + + + + + + + + SELECT COUNT(*) FROM m_code WHERE code_name = 'OccupationCategory' + + + + + + + + + + SELECT COUNT(*) FROM m_code_value WHERE code_id = (SELECT id FROM m_code WHERE code_name = 'OccupationCategory') + + + 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 + ('Salaried / Service', 1), + ('Business / Self Employed', 2), + ('Professional', 3), + ('Agriculture', 4), + ('Student / Non-Earning', 5), + ('Others', 6) + ) AS t(val, pos) + WHERE code_name = 'OccupationCategory'; + + + + + + + SELECT COUNT(*) FROM m_code WHERE code_name = 'Occupation' + + + + + + + + + + SELECT COUNT(*) FROM m_code_value WHERE code_id = (SELECT id FROM m_code WHERE code_name = 'Occupation') + + + 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 + ('Government Employee', 1), + ('Public Sector Undertaking (PSU) Employee', 2), + ('Private Sector Employee', 3), + ('Defence / Armed Forces Personnel', 4), + ('Police / Paramilitary', 5), + ('Teacher / Professor', 6), + ('Doctor (Salaried)', 7), + ('Engineer (Salaried)', 8), + ('Bank Employee', 9), + ('Railway Employee', 10), + ('Business Owner / Proprietor', 11), + ('Trader / Merchant', 12), + ('Manufacturer', 13), + ('Contractor', 14), + ('Real Estate / Property Dealer', 15), + ('Retailer', 16), + ('Wholesaler', 17), + ('Importer / Exporter', 18), + ('Commission Agent', 19), + ('Transport / Logistics Owner', 20), + ('Doctor (Private Practice)', 21), + ('Lawyer / Advocate', 22), + ('Chartered Accountant', 23), + ('Company Secretary', 24), + ('Architect', 25), + ('Consultant', 26), + ('Engineer (Self Employed)', 27), + ('IT Professional / Freelancer', 28), + ('Financial Advisor', 29), + ('Auditor', 30), + ('Farmer (Own Land)', 31), + ('Tenant Farmer', 32), + ('Agricultural Labourer', 33), + ('Horticulture / Floriculture', 34), + ('Dairy / Animal Husbandry', 35), + ('Fisherman', 36), + ('Poultry Farmer', 37), + ('Agri Allied Activities', 38), + ('Student', 39), + ('Housewife / Homemaker', 40), + ('Dependent / Minor', 41), + ('Unemployed', 42), + ('Retired (Pension)', 43), + ('Retired (Non-Pension)', 44), + ('NRI (Non-Resident Indian)', 45), + ('PIO / OCI', 46), + ('Politician / Elected Representative', 47), + ('Religious / Trust Worker', 48), + ('NGO / Social Worker', 49), + ('Artisan / Craftsman', 50), + ('Labourer / Daily Wage', 51), + ('Domestic Worker', 52), + ('Driver / Operator', 53), + ('Others', 54) + ) AS t(val, pos) + WHERE code_name = 'Occupation'; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +