diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java index 9cae925d7a6..eb4eb965a85 100644 --- a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java +++ b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java @@ -113,6 +113,7 @@ import org.apache.fineract.client.services.PaymentTypeApi; import org.apache.fineract.client.services.PeriodicAccrualAccountingApi; import org.apache.fineract.client.services.PermissionsApi; +import org.apache.fineract.client.services.ProductsApi; import org.apache.fineract.client.services.ProgressiveLoanApi; import org.apache.fineract.client.services.ProvisioningCategoryApi; import org.apache.fineract.client.services.ProvisioningCriteriaApi; @@ -262,6 +263,7 @@ public final class FineractClient { public final SchedulerJobApi jobs; public final ScoreCardApi surveyScorecards; public final SearchApiApi search; + public final ProductsApi shareProducts; public final ShareAccountApi shareAccounts; public final SpmApiLookUpTableApi surveyLookupTables; public final SpmSurveysApi surveys; @@ -381,6 +383,7 @@ private FineractClient(OkHttpClient okHttpClient, Retrofit retrofit) { jobsScheduler = retrofit.create(SchedulerApi.class); surveyScorecards = retrofit.create(ScoreCardApi.class); search = retrofit.create(SearchApiApi.class); + shareProducts = retrofit.create(ProductsApi.class); shareAccounts = retrofit.create(ShareAccountApi.class); surveyLookupTables = retrofit.create(SpmApiLookUpTableApi.class); surveys = retrofit.create(SpmSurveysApi.class); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java index bb9477fd01c..622e6b67c51 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/serialization/ShareAccountDataSerializer.java @@ -152,13 +152,15 @@ public ShareAccount validateAndCreate(JsonCommand jsonCommand) { baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).notNull() .longGreaterThanZero(); - if (shareProduct.getMinimumClientShares() != null && requestedShares < shareProduct.getMinimumClientShares()) { + if (requestedShares != null && shareProduct.getMinimumClientShares() != null + && requestedShares < shareProduct.getMinimumClientShares()) { baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).failWithCode( "client.can.not.purchase.shares.lessthan.product.definition", "Client can not purchase shares less than product definition"); } - if (shareProduct.getMaximumClientShares() != null && requestedShares > shareProduct.getMaximumClientShares()) { + if (requestedShares != null && shareProduct.getMaximumClientShares() != null + && requestedShares > shareProduct.getMaximumClientShares()) { baseDataValidator.reset().parameter(ShareAccountApiConstants.requestedshares_paramname).value(requestedShares).failWithCode( "client.can.not.purchase.shares.morethan.product.definition", "Client can not purchase shares more than product definition"); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ShareAccountCreationValidationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ShareAccountCreationValidationTest.java new file mode 100644 index 00000000000..16435773d53 --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ShareAccountCreationValidationTest.java @@ -0,0 +1,89 @@ +/** + * 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. + */ +package org.apache.fineract.integrationtests; + +import java.time.format.DateTimeFormatter; +import org.apache.fineract.client.models.AccountRequest; +import org.apache.fineract.client.models.PostAccountsTypeResponse; +import org.apache.fineract.client.models.PostProductsTypeRequest; +import org.apache.fineract.client.models.PostSavingsAccountsAccountIdRequest; +import org.apache.fineract.client.models.PostSavingsAccountsRequest; +import org.apache.fineract.client.models.PostSavingsProductsRequest; +import org.apache.fineract.client.util.Calls; +import org.apache.fineract.integrationtests.client.IntegrationTest; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.junit.jupiter.api.Test; +import retrofit2.Response; + +public class ShareAccountCreationValidationTest extends IntegrationTest { + + private static final String DATE_FORMAT = "dd MMMM yyyy"; + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT); + + @Test + public void shouldReturn400WhenRequiredShareAccountFieldsMissing() { + String today = FORMATTER.format(Utils.getLocalDateOfTenant()); + + Long productId = ok(fineractClient().shareProducts.createProduct("share", + new PostProductsTypeRequest().name(Utils.uniqueRandomStringGenerator("SHARE_PROD_", 6)) + .shortName(Utils.uniqueRandomStringGenerator("", 4)).description(Utils.randomStringGenerator("", 20)) + .currencyCode("USD").digitsAfterDecimal(4).inMultiplesOf(0).locale("en_GB").totalShares(10000).sharesIssued(10000) + .unitPrice(2).nominalShares(20).minimumShares(10).maximumShares(3000) + .allowDividendCalculationForInactiveClients(true).accountingRule(1).minimumActivePeriodForDividends(1) + .minimumactiveperiodFrequencyType(0).lockinPeriodFrequency(1).lockinPeriodFrequencyType(0))) + .getResourceId(); + + Long clientId = ClientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + + Long savingsProductId = ok(fineractClient().savingsProducts + .createSavingsProduct(new PostSavingsProductsRequest().name(Utils.uniqueRandomStringGenerator("SAV_PROD_", 6)) + .shortName(Utils.uniqueRandomStringGenerator("", 4)).currencyCode("USD").digitsAfterDecimal(4).inMultiplesOf(0) + .nominalAnnualInterestRate(10.0).locale("en_GB").interestCompoundingPeriodType(4).interestPostingPeriodType(4) + .interestCalculationType(1).interestCalculationDaysInYearType(365).accountingRule(1))) + .getResourceId(); + + Long savingsId = ok(fineractClient().savingsAccounts.submitSavingsApplication(new PostSavingsAccountsRequest().clientId(clientId) + .productId(savingsProductId).locale("en").dateFormat(DATE_FORMAT).submittedOnDate(today))).getSavingsId(); + + ok(fineractClient().savingsAccounts.handleCommandsSavingsAccount(savingsId, + new PostSavingsAccountsAccountIdRequest().dateFormat(DATE_FORMAT).locale("en").approvedOnDate(today), "approve")); + + ok(fineractClient().savingsAccounts.handleCommandsSavingsAccount(savingsId, + new PostSavingsAccountsAccountIdRequest().dateFormat(DATE_FORMAT).locale("en").activatedOnDate(today), "activate")); + + // missing requestedShares + Response missingShares = Calls + .executeU(fineractClient().shareAccounts.createAccount("share", new AccountRequest().clientId(clientId).productId(productId) + .savingsAccountId(savingsId).submittedDate(today).applicationDate(today).dateFormat(DATE_FORMAT).locale("en_GB"))); + assertThat(missingShares.code()).isEqualTo(400); + + // missing applicationDate + Response missingAppDate = Calls + .executeU(fineractClient().shareAccounts.createAccount("share", new AccountRequest().clientId(clientId).productId(productId) + .savingsAccountId(savingsId).requestedShares(25L).submittedDate(today).dateFormat(DATE_FORMAT).locale("en_GB"))); + assertThat(missingAppDate.code()).isEqualTo(400); + + // missing savingsAccountId + Response missingSavings = Calls + .executeU(fineractClient().shareAccounts.createAccount("share", new AccountRequest().clientId(clientId).productId(productId) + .requestedShares(25L).submittedDate(today).applicationDate(today).dateFormat(DATE_FORMAT).locale("en_GB"))); + assertThat(missingSavings.code()).isEqualTo(400); + } +}