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;
     }