FINERACT-1393:RandomAccountNumber

This commit is contained in:
rrpawar96
2021-12-31 15:53:22 +08:00
committed by Petri Tuomola
parent ccff5d8ad2
commit 3f1df7780c
5 changed files with 117 additions and 9 deletions
@@ -18,8 +18,10 @@
*/
package org.apache.fineract.portfolio.client.domain;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat;
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.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -44,6 +48,7 @@ public class AccountNumberGenerator {
private static final int maxLength = 9;
private static final String ID = "id";
private static final String ENTITY_TYPE = "entityType";
private static final String CLIENT_TYPE = "clientType";
private static final String OFFICE_NAME = "officeName";
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 final AccountNumberFormatRepository accountNumberFormatRepository;
private final ConfigurationReadPlatformService configurationReadPlatformService;
private final ClientRepository clientRepository;
private final LoanRepository loanRepository;
private final SavingsAccountRepository savingsAccountRepository;
@Autowired
public AccountNumberGenerator(final ConfigurationReadPlatformService configurationReadPlatformService,
final AccountNumberFormatRepository accountNumberFormatRepository) {
final AccountNumberFormatRepository accountNumberFormatRepository, final ClientRepository clientRepository,
final LoanRepository loanRepository, final SavingsAccountRepository savingsAccountRepository) {
this.configurationReadPlatformService = configurationReadPlatformService;
this.accountNumberFormatRepository = accountNumberFormatRepository;
this.clientRepository = clientRepository;
this.loanRepository = loanRepository;
this.savingsAccountRepository = savingsAccountRepository;
}
public String generate(Client client, AccountNumberFormat accountNumberFormat) {
Map<String, String> propertyMap = new HashMap<>();
propertyMap.put(ID, client.getId().toString());
propertyMap.put(OFFICE_NAME, client.getOffice().getName());
propertyMap.put(ENTITY_TYPE, "client");
CodeValue clientType = client.clientType();
if (clientType != null) {
propertyMap.put(CLIENT_TYPE, clientType.label());
@@ -76,6 +89,7 @@ public class AccountNumberGenerator {
propertyMap.put(ID, loan.getId().toString());
propertyMap.put(OFFICE_NAME, loan.getOffice().getName());
propertyMap.put(LOAN_PRODUCT_SHORT_NAME, loan.loanProduct().getShortName());
propertyMap.put(ENTITY_TYPE, "loan");
return generateAccountNumber(propertyMap, accountNumberFormat);
}
@@ -84,6 +98,7 @@ public class AccountNumberGenerator {
propertyMap.put(ID, savingsAccount.getId().toString());
propertyMap.put(OFFICE_NAME, savingsAccount.office().getName());
propertyMap.put(SAVINGS_PRODUCT_SHORT_NAME, savingsAccount.savingsProduct().getShortName());
propertyMap.put(ENTITY_TYPE, "savingsAccount");
return generateAccountNumber(propertyMap, accountNumberFormat);
}
@@ -96,6 +111,7 @@ public class AccountNumberGenerator {
private String generateAccountNumber(Map<String, String> propertyMap, AccountNumberFormat accountNumberFormat) {
int accountMaxLength = AccountNumberGenerator.maxLength;
String accountNumber = StringUtils.leftPad(propertyMap.get(ID), accountMaxLength, '0');
// find if the custom length is defined
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) {
AccountNumberPrefixType accountNumberPrefixType = AccountNumberPrefixType.fromInt(accountNumberFormat.getPrefixEnum());
String prefix = null;
@@ -143,17 +166,66 @@ public class AccountNumberGenerator {
}
if (accountNumberPrefixType.getValue().equals(AccountNumberPrefixType.PREFIX_SHORT_NAME.getValue())) {
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 {
accountNumber = StringUtils.leftPad(accountNumber, Integer.valueOf(propertyMap.get(ID).length()), '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;
}
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,
AccountNumberFormat accountNumberFormat) {
@@ -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 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_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber";
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType);
@@ -150,10 +152,13 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
@Query(FIND_BY_ACCOUNT_NUMBER)
@Query(FIND_NON_CLOSED_BY_ACCOUNT_NUMBER)
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
@Query(FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT)
Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId);
@Query(FIND_BY_ACCOUNT_NUMBER)
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber);
}
@@ -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) ")
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);
SavingsAccount findByExternalId(String externalId);
@@ -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');
@@ -89,9 +89,9 @@ public class GlobalConfigurationHelper {
ArrayList<HashMap> expectedGlobalConfigurations = getAllDefaultGlobalConfigurations();
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
// There are currently 36 global configurations.
Assertions.assertEquals(36, expectedGlobalConfigurations.size());
Assertions.assertEquals(36, actualGlobalConfigurations.size());
// There are currently 37 global configurations.
Assertions.assertEquals(37, expectedGlobalConfigurations.size());
Assertions.assertEquals(37, actualGlobalConfigurations.size());
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
@@ -415,6 +415,14 @@ public class GlobalConfigurationHelper {
isClientAccountNumberLengthModify.put("trapDoor", false);
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;
}