From a2be1eb9852ef385f73689778ae9f6c0d9bbddc5 Mon Sep 17 00:00:00 2001 From: rrpawar96 Date: Sat, 12 Feb 2022 01:08:59 +0530 Subject: [PATCH] FINERACT-1500:SavingsHoldAccountLien --- .../savings/SavingsApiConstants.java | 1 + ...avingsAccountTransactionDataValidator.java | 23 +++++- .../savings/domain/SavingsAccount.java | 8 +++ .../ClientSavingsIntegrationTest.java | 70 ++++++++++++++++++- .../common/savings/SavingsAccountHelper.java | 17 ++++- 5 files changed, 113 insertions(+), 6 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java index 04c043700..f02ad81cd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/SavingsApiConstants.java @@ -142,6 +142,7 @@ public class SavingsApiConstants { // transaction parameters public static final String transactionDateParamName = "transactionDate"; + public static final String lienParamName = "lien"; public static final String transactionAmountParamName = "transactionAmount"; public static final String paymentDetailDataParamName = "paymentDetailData"; public static final String runningBalanceParamName = "runningBalance"; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionDataValidator.java index c4759943a..f3acadedf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountTransactionDataValidator.java @@ -23,6 +23,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.activate import static org.apache.fineract.portfolio.savings.SavingsApiConstants.bankNumberParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.checkNumberParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.closedOnDateParamName; +import static org.apache.fineract.portfolio.savings.SavingsApiConstants.lienParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.paymentTypeIdParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.receiptNumberParamName; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.routingCodeParamName; @@ -68,7 +69,7 @@ public class SavingsAccountTransactionDataValidator { private final FromJsonHelper fromApiJsonHelper; private static final Set SAVINGS_ACCOUNT_HOLD_AMOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>( Arrays.asList(transactionDateParamName, SavingsApiConstants.dateFormatParamName, SavingsApiConstants.localeParamName, - transactionAmountParamName)); + transactionAmountParamName, lienParamName)); private final ConfigurationDomainService configurationDomainService; @Autowired @@ -206,6 +207,7 @@ public class SavingsAccountTransactionDataValidator { public SavingsAccountTransaction validateHoldAndAssembleForm(final String json, final SavingsAccount account, final AppUser createdUser, final boolean backdatedTxnsAllowedTill) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } @@ -222,6 +224,7 @@ public class SavingsAccountTransactionDataValidator { final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(transactionAmountParamName, element); baseDataValidator.reset().parameter(transactionAmountParamName).value(amount).notNull().positiveAmount(); final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed(transactionDateParamName, element); + baseDataValidator.reset().parameter(transactionDateParamName).value(transactionDate).notNull(); boolean isActive = account.isActive(); @@ -230,8 +233,22 @@ public class SavingsAccountTransactionDataValidator { .failWithCodeNoParameterAddedToErrorCode(SavingsApiConstants.ERROR_MSG_SAVINGS_ACCOUNT_NOT_ACTIVE); } account.holdAmount(amount); - if (account.getWithdrawableBalance().compareTo(BigDecimal.ZERO) < 0) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient balance", account.getId()); + + if (account.getEnforceMinRequiredBalance()) { + if (account.getWithdrawableBalance().compareTo(BigDecimal.ZERO) < 0) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient balance", account.getId()); + } + } + + Boolean lien = false; + + if (this.fromApiJsonHelper.parameterExists(lienParamName, element)) { + lien = this.fromApiJsonHelper.extractBooleanNamed(lienParamName, element); + if (!lien) { + if (account.getWithdrawableBalanceWithoutMinimumBalance().compareTo(BigDecimal.ZERO) < 0) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("insufficient balance", account.getId()); + } + } } LocalDate lastTransactionDate = null; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index da03341f7..024394306 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -1935,6 +1935,10 @@ public class SavingsAccount extends AbstractPersistableCustom { return this.savingsOfficer; } + public Boolean getEnforceMinRequiredBalance() { + return this.enforceMinRequiredBalance; + } + public void unassignSavingsOfficer() { this.savingsOfficer = null; } @@ -3367,6 +3371,10 @@ public class SavingsAccount extends AbstractPersistableCustom { .subtract(this.getSavingsHoldAmount()); } + public BigDecimal getWithdrawableBalanceWithoutMinimumBalance() { + return getAccountBalance().subtract(this.getOnHoldFunds()).subtract(this.getSavingsHoldAmount()); + } + public TaxGroup getTaxGroup() { return this.taxGroup; } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java index ed26486d8..96802a353 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientSavingsIntegrationTest.java @@ -2156,7 +2156,7 @@ public class ClientSavingsIntegrationTest { assertEquals(Float.parseFloat(DEPOSIT_AMOUNT), depositTransaction.get("amount"), "Verifying Deposit Amount"); Integer holdTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, String.valueOf(balance - 100), - SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID); + false, SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID); error = (List) savingsAccountHelperValidationError.withdrawalFromSavingsAccount(savingsId, "300", SavingsAccountHelper.TRANSACTION_DATE_PLUS_ONE, CommonConstants.RESPONSE_ERROR); assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance", @@ -2175,6 +2175,74 @@ public class ClientSavingsIntegrationTest { } + @SuppressWarnings("unchecked") + @Test + public void testSavingsAccountLienStatus() { + + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + SavingsAccountHelper savingsAccountHelperValidationError = new SavingsAccountHelper(this.requestSpec, + new ResponseSpecBuilder().build()); + + final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build(); + final SavingsAccountHelper validationErrorHelper = new SavingsAccountHelper(this.requestSpec, errorResponse); + + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + + final String minBalanceForInterestCalculation = null; + final String minRequiredBalance = null; + final String enforceMinRequiredBalance = "false"; + final boolean allowOverdraft = false; + final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE, + minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft); + Assertions.assertNotNull(savingsProductID); + + final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL); + Assertions.assertNotNull(savingsProductID); + + HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId); + SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap); + + savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId); + SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap); + + savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId); + SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap); + + List error = (List) savingsAccountHelperValidationError.holdAmountInSavingsAccount(savingsId, "1500", false, + SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR); // lien:false + + assertEquals("validation.msg.savingsaccount.insufficient balance", error.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + + Integer holdTransactionId = (Integer) this.savingsAccountHelper.holdAmountInSavingsAccount(savingsId, "1500", true, + SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID); + + HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId); + + Float balance = Float.parseFloat("-500"); + + assertEquals(balance, summary.get("availableBalance"), "Verifying available Balance is -500"); + + Integer depositTransactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "1000", + SavingsAccountHelper.TRANSACTION_DATE, CommonConstants.RESPONSE_RESOURCE_ID); + DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); + Calendar todaysDate = Calendar.getInstance(); + final String TRANSACTION_DATE = dateFormat.format(todaysDate.getTime()); + + ArrayList savingsAccountErrorData = (ArrayList) validationErrorHelper.withdrawalFromSavingsAccount(savingsId, + "1000", TRANSACTION_DATE, CommonConstants.RESPONSE_ERROR); + + assertEquals("error.msg.savingsaccount.transaction.insufficient.account.balance", + savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + + Integer withdrawTransactionId = (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "500", TRANSACTION_DATE, + CommonConstants.RESPONSE_RESOURCE_ID); + + Assertions.assertNotNull(withdrawTransactionId); + } + /** * incorrect savings account balance when charge transaction is reversed during an overdraft recalculate Daily * Balances diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java index f07695011..a2b36ddea 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/savings/SavingsAccountHelper.java @@ -369,10 +369,11 @@ public class SavingsAccountHelper { getActivatedSavingsAsJSON(), isBlock); } - public Object holdAmountInSavingsAccount(final Integer savingsID, final String amount, String date, String jsonAttributeToGetback) { + public Object holdAmountInSavingsAccount(final Integer savingsID, final String amount, final Boolean lienStatus, String date, + String jsonAttributeToGetback) { LOG.info("--------------------------------- SAVINGS TRANSACTION HOLD AMOUNT--------------------------------"); return performSavingActions(createSavingsTransactionURL(HOLD_AMOUNT_SAVINGS_COMMAND, savingsID), - getSavingsTransactionJSON(amount, date), jsonAttributeToGetback); + getLienSavingsTransactionJSON(amount, date, lienStatus), jsonAttributeToGetback); } public Integer releaseAmount(final Integer savingsId, final Integer transactionId) { @@ -439,6 +440,18 @@ public class SavingsAccountHelper { return savingsAccountWithdrawalJson; } + private String getLienSavingsTransactionJSON(final String amount, final String transactionDate, final Boolean lien) { + final HashMap map = new HashMap<>(); + map.put("locale", CommonConstants.LOCALE); + map.put("dateFormat", CommonConstants.DATE_FORMAT); + map.put("transactionDate", transactionDate); + map.put("transactionAmount", amount); + map.put("lien", lien); + String savingsAccountWithdrawalJson = new Gson().toJson(map); + LOG.info(savingsAccountWithdrawalJson); + return savingsAccountWithdrawalJson; + } + private String getSavingsTransactionPaymentTypeJSON(final String amount, final String transactionDate, final String paymentTypeId) { final HashMap map = new HashMap<>(); map.put("locale", CommonConstants.LOCALE);