FINERACT-1393:RandomAccountNumber
This commit is contained in:
+76
-4
@@ -18,8 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.client.domain;
|
package org.apache.fineract.portfolio.client.domain;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat;
|
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat;
|
||||||
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatEnumerations.AccountNumberPrefixType;
|
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatEnumerations.AccountNumberPrefixType;
|
||||||
@@ -29,7 +31,9 @@ import org.apache.fineract.infrastructure.configuration.data.GlobalConfiguration
|
|||||||
import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService;
|
import org.apache.fineract.infrastructure.configuration.service.ConfigurationReadPlatformService;
|
||||||
import org.apache.fineract.portfolio.group.domain.Group;
|
import org.apache.fineract.portfolio.group.domain.Group;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
|
||||||
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
|
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -44,6 +48,7 @@ public class AccountNumberGenerator {
|
|||||||
private static final int maxLength = 9;
|
private static final int maxLength = 9;
|
||||||
|
|
||||||
private static final String ID = "id";
|
private static final String ID = "id";
|
||||||
|
private static final String ENTITY_TYPE = "entityType";
|
||||||
private static final String CLIENT_TYPE = "clientType";
|
private static final String CLIENT_TYPE = "clientType";
|
||||||
private static final String OFFICE_NAME = "officeName";
|
private static final String OFFICE_NAME = "officeName";
|
||||||
private static final String LOAN_PRODUCT_SHORT_NAME = "loanProductShortName";
|
private static final String LOAN_PRODUCT_SHORT_NAME = "loanProductShortName";
|
||||||
@@ -52,18 +57,26 @@ public class AccountNumberGenerator {
|
|||||||
private static final String PREFIX_SHORT_NAME = "prefixShortName";
|
private static final String PREFIX_SHORT_NAME = "prefixShortName";
|
||||||
private final AccountNumberFormatRepository accountNumberFormatRepository;
|
private final AccountNumberFormatRepository accountNumberFormatRepository;
|
||||||
private final ConfigurationReadPlatformService configurationReadPlatformService;
|
private final ConfigurationReadPlatformService configurationReadPlatformService;
|
||||||
|
private final ClientRepository clientRepository;
|
||||||
|
private final LoanRepository loanRepository;
|
||||||
|
private final SavingsAccountRepository savingsAccountRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public AccountNumberGenerator(final ConfigurationReadPlatformService configurationReadPlatformService,
|
public AccountNumberGenerator(final ConfigurationReadPlatformService configurationReadPlatformService,
|
||||||
final AccountNumberFormatRepository accountNumberFormatRepository) {
|
final AccountNumberFormatRepository accountNumberFormatRepository, final ClientRepository clientRepository,
|
||||||
|
final LoanRepository loanRepository, final SavingsAccountRepository savingsAccountRepository) {
|
||||||
this.configurationReadPlatformService = configurationReadPlatformService;
|
this.configurationReadPlatformService = configurationReadPlatformService;
|
||||||
this.accountNumberFormatRepository = accountNumberFormatRepository;
|
this.accountNumberFormatRepository = accountNumberFormatRepository;
|
||||||
|
this.clientRepository = clientRepository;
|
||||||
|
this.loanRepository = loanRepository;
|
||||||
|
this.savingsAccountRepository = savingsAccountRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generate(Client client, AccountNumberFormat accountNumberFormat) {
|
public String generate(Client client, AccountNumberFormat accountNumberFormat) {
|
||||||
Map<String, String> propertyMap = new HashMap<>();
|
Map<String, String> propertyMap = new HashMap<>();
|
||||||
propertyMap.put(ID, client.getId().toString());
|
propertyMap.put(ID, client.getId().toString());
|
||||||
propertyMap.put(OFFICE_NAME, client.getOffice().getName());
|
propertyMap.put(OFFICE_NAME, client.getOffice().getName());
|
||||||
|
propertyMap.put(ENTITY_TYPE, "client");
|
||||||
CodeValue clientType = client.clientType();
|
CodeValue clientType = client.clientType();
|
||||||
if (clientType != null) {
|
if (clientType != null) {
|
||||||
propertyMap.put(CLIENT_TYPE, clientType.label());
|
propertyMap.put(CLIENT_TYPE, clientType.label());
|
||||||
@@ -76,6 +89,7 @@ public class AccountNumberGenerator {
|
|||||||
propertyMap.put(ID, loan.getId().toString());
|
propertyMap.put(ID, loan.getId().toString());
|
||||||
propertyMap.put(OFFICE_NAME, loan.getOffice().getName());
|
propertyMap.put(OFFICE_NAME, loan.getOffice().getName());
|
||||||
propertyMap.put(LOAN_PRODUCT_SHORT_NAME, loan.loanProduct().getShortName());
|
propertyMap.put(LOAN_PRODUCT_SHORT_NAME, loan.loanProduct().getShortName());
|
||||||
|
propertyMap.put(ENTITY_TYPE, "loan");
|
||||||
return generateAccountNumber(propertyMap, accountNumberFormat);
|
return generateAccountNumber(propertyMap, accountNumberFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +98,7 @@ public class AccountNumberGenerator {
|
|||||||
propertyMap.put(ID, savingsAccount.getId().toString());
|
propertyMap.put(ID, savingsAccount.getId().toString());
|
||||||
propertyMap.put(OFFICE_NAME, savingsAccount.office().getName());
|
propertyMap.put(OFFICE_NAME, savingsAccount.office().getName());
|
||||||
propertyMap.put(SAVINGS_PRODUCT_SHORT_NAME, savingsAccount.savingsProduct().getShortName());
|
propertyMap.put(SAVINGS_PRODUCT_SHORT_NAME, savingsAccount.savingsProduct().getShortName());
|
||||||
|
propertyMap.put(ENTITY_TYPE, "savingsAccount");
|
||||||
return generateAccountNumber(propertyMap, accountNumberFormat);
|
return generateAccountNumber(propertyMap, accountNumberFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +111,7 @@ public class AccountNumberGenerator {
|
|||||||
|
|
||||||
private String generateAccountNumber(Map<String, String> propertyMap, AccountNumberFormat accountNumberFormat) {
|
private String generateAccountNumber(Map<String, String> propertyMap, AccountNumberFormat accountNumberFormat) {
|
||||||
int accountMaxLength = AccountNumberGenerator.maxLength;
|
int accountMaxLength = AccountNumberGenerator.maxLength;
|
||||||
|
String accountNumber = StringUtils.leftPad(propertyMap.get(ID), accountMaxLength, '0');
|
||||||
|
|
||||||
// find if the custom length is defined
|
// find if the custom length is defined
|
||||||
final GlobalConfigurationPropertyData customLength = this.configurationReadPlatformService
|
final GlobalConfigurationPropertyData customLength = this.configurationReadPlatformService
|
||||||
@@ -108,7 +124,14 @@ public class AccountNumberGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String accountNumber = StringUtils.leftPad(propertyMap.get(ID), accountMaxLength, '0');
|
final GlobalConfigurationPropertyData randomAccountNumber = this.configurationReadPlatformService
|
||||||
|
.retrieveGlobalConfiguration("random-account-number");
|
||||||
|
|
||||||
|
if (randomAccountNumber.isEnabled()) {
|
||||||
|
accountNumber = randomNumberGenerator(accountMaxLength, propertyMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
accountNumber = StringUtils.leftPad(accountNumber, accountMaxLength, '0');
|
||||||
if (accountNumberFormat != null && accountNumberFormat.getPrefixEnum() != null) {
|
if (accountNumberFormat != null && accountNumberFormat.getPrefixEnum() != null) {
|
||||||
AccountNumberPrefixType accountNumberPrefixType = AccountNumberPrefixType.fromInt(accountNumberFormat.getPrefixEnum());
|
AccountNumberPrefixType accountNumberPrefixType = AccountNumberPrefixType.fromInt(accountNumberFormat.getPrefixEnum());
|
||||||
String prefix = null;
|
String prefix = null;
|
||||||
@@ -143,17 +166,66 @@ public class AccountNumberGenerator {
|
|||||||
}
|
}
|
||||||
if (accountNumberPrefixType.getValue().equals(AccountNumberPrefixType.PREFIX_SHORT_NAME.getValue())) {
|
if (accountNumberPrefixType.getValue().equals(AccountNumberPrefixType.PREFIX_SHORT_NAME.getValue())) {
|
||||||
Integer prefixLength = prefix.length();
|
Integer prefixLength = prefix.length();
|
||||||
Integer numberLength = accountMaxLength - prefixLength;
|
|
||||||
accountNumber = StringUtils.leftPad(propertyMap.get(ID), numberLength, '0');
|
if (randomAccountNumber.isEnabled()) {
|
||||||
|
accountNumber = accountNumber.substring(prefixLength);
|
||||||
|
} else {
|
||||||
|
Integer numberLength = accountMaxLength - prefixLength;
|
||||||
|
accountNumber = StringUtils.leftPad(propertyMap.get(ID), numberLength, '0');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
accountNumber = StringUtils.leftPad(accountNumber, Integer.valueOf(propertyMap.get(ID).length()), '0');
|
accountNumber = StringUtils.leftPad(accountNumber, Integer.valueOf(propertyMap.get(ID).length()), '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
accountNumber = StringUtils.overlay(accountNumber, prefix, 0, 0);
|
accountNumber = StringUtils.overlay(accountNumber, prefix, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (randomAccountNumber.isEnabled()) { // calling the main function itself until new randomNo.
|
||||||
|
Boolean randomNumberConflict = checkAccountNumberConflict(propertyMap, accountNumberFormat, accountNumber);
|
||||||
|
if (randomNumberConflict) {
|
||||||
|
accountNumber = generateAccountNumber(propertyMap, accountNumberFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
return accountNumber;
|
return accountNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String randomNumberGenerator(int accountMaxLength, Map<String, String> propertyMap) {
|
||||||
|
String randomNumber = RandomStringUtils.random(accountMaxLength, false, true);
|
||||||
|
|
||||||
|
BigInteger accNumber = new BigInteger(randomNumber);
|
||||||
|
if (accNumber.equals(BigInteger.ZERO)) { // to avoid account no. 00 in randomisation
|
||||||
|
randomNumber = randomNumberGenerator(accountMaxLength, propertyMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
String accountNumber = randomNumber.substring(0, accountMaxLength);
|
||||||
|
return accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean checkAccountNumberConflict(Map<String, String> propertyMap, AccountNumberFormat accountNumberFormat,
|
||||||
|
String accountNumber) {
|
||||||
|
|
||||||
|
String entityType = propertyMap.get(ENTITY_TYPE);
|
||||||
|
Boolean randomNumberConflict = false;
|
||||||
|
if (entityType.equals("client")) { // avoid duplication it will loop until it finds new random account no.
|
||||||
|
|
||||||
|
Client client = this.clientRepository.getClientByAccountNumber(accountNumber);
|
||||||
|
if (client != null) {
|
||||||
|
randomNumberConflict = true;
|
||||||
|
}
|
||||||
|
} else if (entityType.equals("loan")) {
|
||||||
|
Loan loan = this.loanRepository.findLoanAccountByAccountNumber(accountNumber);
|
||||||
|
if (loan != null) {
|
||||||
|
randomNumberConflict = true;
|
||||||
|
}
|
||||||
|
} else if (entityType.equals("savingsAccount")) {
|
||||||
|
SavingsAccount savingsAccount = this.savingsAccountRepository.findSavingsAccountByAccountNumber(accountNumber);
|
||||||
|
if (savingsAccount != null) {
|
||||||
|
randomNumberConflict = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return randomNumberConflict;
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, String> generatePrefix(Map<String, String> propertyMap, String accountNumber, Integer accountMaxLength,
|
private Map<String, String> generatePrefix(Map<String, String> propertyMap, String accountNumber, Integer accountMaxLength,
|
||||||
AccountNumberFormat accountNumberFormat) {
|
AccountNumberFormat accountNumberFormat) {
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -67,10 +67,12 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
|||||||
|
|
||||||
String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)";
|
String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)";
|
||||||
|
|
||||||
String FIND_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)";
|
String FIND_NON_CLOSED_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)";
|
||||||
|
|
||||||
String FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus = 300 and loan.client.id = :clientId";
|
String FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus = 300 and loan.client.id = :clientId";
|
||||||
|
|
||||||
|
String FIND_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber";
|
||||||
|
|
||||||
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
||||||
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
|
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
|
||||||
@Param("loanType") Integer loanType);
|
@Param("loanType") Integer loanType);
|
||||||
@@ -150,10 +152,13 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
|||||||
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
|
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
|
||||||
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
|
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
|
||||||
|
|
||||||
@Query(FIND_BY_ACCOUNT_NUMBER)
|
@Query(FIND_NON_CLOSED_BY_ACCOUNT_NUMBER)
|
||||||
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
|
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
|
||||||
|
|
||||||
@Query(FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT)
|
@Query(FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT)
|
||||||
Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId);
|
Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId);
|
||||||
|
|
||||||
|
@Query(FIND_BY_ACCOUNT_NUMBER)
|
||||||
|
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -60,6 +60,9 @@ public interface SavingsAccountRepository extends JpaRepository<SavingsAccount,
|
|||||||
@Query("select sa from SavingsAccount sa where sa.accountNumber = :accountNumber and sa.status in (100, 200, 300, 303, 304) ")
|
@Query("select sa from SavingsAccount sa where sa.accountNumber = :accountNumber and sa.status in (100, 200, 300, 303, 304) ")
|
||||||
SavingsAccount findNonClosedAccountByAccountNumber(@Param("accountNumber") String accountNumber);
|
SavingsAccount findNonClosedAccountByAccountNumber(@Param("accountNumber") String accountNumber);
|
||||||
|
|
||||||
|
@Query("select sa from SavingsAccount sa where sa.accountNumber = :accountNumber ")
|
||||||
|
SavingsAccount findSavingsAccountByAccountNumber(@Param("accountNumber") String accountNumber);
|
||||||
|
|
||||||
Page<SavingsAccount> findByStatus(Integer status, Pageable pageable);
|
Page<SavingsAccount> findByStatus(Integer status, Pageable pageable);
|
||||||
|
|
||||||
SavingsAccount findByExternalId(String externalId);
|
SavingsAccount findByExternalId(String externalId);
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
--
|
||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
--
|
||||||
|
|
||||||
|
INSERT INTO c_configuration ( name, description) VALUES ( 'random-account-number', 'if enabled, the client accounts, saving accounts, loan accounts will be created with Random Account Number');
|
||||||
+11
-3
@@ -89,9 +89,9 @@ public class GlobalConfigurationHelper {
|
|||||||
ArrayList<HashMap> expectedGlobalConfigurations = getAllDefaultGlobalConfigurations();
|
ArrayList<HashMap> expectedGlobalConfigurations = getAllDefaultGlobalConfigurations();
|
||||||
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
|
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
|
||||||
|
|
||||||
// There are currently 36 global configurations.
|
// There are currently 37 global configurations.
|
||||||
Assertions.assertEquals(36, expectedGlobalConfigurations.size());
|
Assertions.assertEquals(37, expectedGlobalConfigurations.size());
|
||||||
Assertions.assertEquals(36, actualGlobalConfigurations.size());
|
Assertions.assertEquals(37, actualGlobalConfigurations.size());
|
||||||
|
|
||||||
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
|
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
|
||||||
|
|
||||||
@@ -415,6 +415,14 @@ public class GlobalConfigurationHelper {
|
|||||||
isClientAccountNumberLengthModify.put("trapDoor", false);
|
isClientAccountNumberLengthModify.put("trapDoor", false);
|
||||||
defaults.add(isClientAccountNumberLengthModify);
|
defaults.add(isClientAccountNumberLengthModify);
|
||||||
|
|
||||||
|
HashMap<String, Object> isAccountNumberRandomGenerated = new HashMap<>();
|
||||||
|
isAccountNumberRandomGenerated.put("id", 41);
|
||||||
|
isAccountNumberRandomGenerated.put("name", "random-account-number");
|
||||||
|
isAccountNumberRandomGenerated.put("value", 0);
|
||||||
|
isAccountNumberRandomGenerated.put("enabled", false);
|
||||||
|
isAccountNumberRandomGenerated.put("trapDoor", false);
|
||||||
|
defaults.add(isAccountNumberRandomGenerated);
|
||||||
|
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user