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) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-03-20 18:51:26 +05:30
co-authored by Claude Opus 4.6
parent ba5365d4af
commit 90c21d40c1
12 changed files with 1116 additions and 75 deletions
@@ -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<String> 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<String> CLIENT_CHARGES_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(chargeIdParamName,
@@ -51,6 +51,7 @@ public final class ClientData implements Comparable<ClientData>, 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<ClientData>, 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<ClientData>, Serializable {
private Collection<CodeValueData> genderOptions;
private Collection<CodeValueData> clientTypeOptions;
private Collection<CodeValueData> clientClassificationOptions;
private Collection<CodeValueData> occupationCategoryOptions;
private Collection<CodeValueData> occupationOptions;
private Collection<CodeValueData> clientNonPersonConstitutionOptions;
private Collection<CodeValueData> clientNonPersonMainBusinessLineOptions;
private List<EnumOptionData> clientLegalFormOptions;
@@ -281,6 +286,7 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
final Collection<StaffData> staffOptions, final Collection<CodeValueData> narrations,
final Collection<CodeValueData> genderOptions, final Collection<SavingsProductData> savingProductOptions,
final Collection<CodeValueData> clientTypeOptions, final Collection<CodeValueData> clientClassificationOptions,
final Collection<CodeValueData> occupationCategoryOptions, final Collection<CodeValueData> occupationOptions,
final Collection<CodeValueData> clientNonPersonConstitutionOptions,
final Collection<CodeValueData> clientNonPersonMainBusinessLineOptions, final List<EnumOptionData> clientLegalFormOptions,
final ClientFamilyMembersData familyMemberOptions, final Collection<AddressData> address, final Boolean isAddressEnabled,
@@ -317,64 +323,83 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
final Boolean isStaff = false;
final ClientNonPersonData clientNonPersonDetails = null;
final Set<ClientCollateralManagementData> 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<ClientCollateralManagementData> 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<SavingsAccountData> savingAccountOptions) {
final Set<ClientCollateralManagementData> 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<GroupGeneralData> parentGroups,
final Set<ClientCollateralManagementData> 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<ClientData>, 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<OfficeData> allowedOffices = null;
final Collection<GroupGeneralData> groups = null;
@@ -505,12 +531,16 @@ public final class ClientData implements Comparable<ClientData>, Serializable {
final Collection<CodeValueData> clientNonPersonMainBusinessLineOptions = null;
final List<EnumOptionData> 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;
}
@@ -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<Long> {
@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<Long> {
@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<Long> {
protected Set<ClientIdentifier> 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<Long> {
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<Long> {
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<Long> {
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<Long> {
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;
}