You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by pt...@apache.org on 2021/12/31 07:53:34 UTC
[fineract] branch develop updated: FINERACT-1393:RandomAccountNumber
This is an automated email from the ASF dual-hosted git repository.
ptuomola pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 3f1df77 FINERACT-1393:RandomAccountNumber
3f1df77 is described below
commit 3f1df7780cbb0961730b370f9b3614d636ed5076
Author: rrpawar96 <rr...@gmail.com>
AuthorDate: Sat Dec 25 23:09:53 2021 +0530
FINERACT-1393:RandomAccountNumber
---
.../client/domain/AccountNumberGenerator.java | 80 ++++++++++++++++++++--
.../loanaccount/domain/LoanRepository.java | 9 ++-
.../savings/domain/SavingsAccountRepository.java | 3 +
...80__configuration_for_random_account_number.sql | 20 ++++++
.../common/GlobalConfigurationHelper.java | 14 +++-
5 files changed, 117 insertions(+), 9 deletions(-)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java
index 1195bd6..0e185fa 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java
@@ -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) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
index 6587560..16c8216 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
@@ -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);
+
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java
index f7454b5..514238a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java
@@ -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);
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V380__configuration_for_random_account_number.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V380__configuration_for_random_account_number.sql
new file mode 100644
index 0000000..034ec4c
--- /dev/null
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V380__configuration_for_random_account_number.sql
@@ -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');
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
index 99f08bf..9ff751e 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/GlobalConfigurationHelper.java
@@ -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;
}