This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 9dc417efb10f60f08f0d3f8d4ba9c55f3ff3314d Author: Mkkor <mu...@korconnection.com> AuthorDate: Wed Jul 12 13:51:18 2023 +0530 FINERACT-1911 Assign datatable to Savings transaction --- .../DatatableCommandFromApiJsonDeserializer.java | 34 ++- .../dataqueries/data/EntityTables.java | 11 +- ...ityDatatableChecksWritePlatformServiceImpl.java | 5 +- .../service/ReadWriteNonCoreDataServiceImpl.java | 159 ++++++------- ...AccountTransactionDatatableIntegrationTest.java | 249 +++++++++++++++++++++ 5 files changed, 357 insertions(+), 101 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java index 9d3c666b0..2bea7c024 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/DatatableCommandFromApiJsonDeserializer.java @@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -55,15 +56,6 @@ public class DatatableCommandFromApiJsonDeserializer { public static final String DROP_COLUMNS = "dropColumns"; public static final String AFTER = "after"; public static final String NEW_CODE = "newCode"; - public static final String M_LOAN = "m_loan"; - public static final String M_SAVINGS_ACCOUNT = "m_savings_account"; - public static final String M_CLIENT = "m_client"; - public static final String M_GROUP = "m_group"; - public static final String M_CENTER = "m_center"; - public static final String M_OFFICE = "m_office"; - public static final String M_SAVINGS_PRODUCT = "m_savings_product"; - public static final String M_PRODUCT_LOAN = "m_product_loan"; - public static final String M_SHARE_PRODUCT = "m_share_product"; public static final String NEW_NAME = "newName"; public static final String STRING = "string"; public static final String NUMBER = "number"; @@ -92,8 +84,6 @@ public class DatatableCommandFromApiJsonDeserializer { Arrays.asList(NAME, NEW_NAME, LENGTH, MANDATORY, AFTER, CODE, NEW_CODE, UNIQUE, INDEXED)); private static final Set<String> SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS = new HashSet<>(List.of(NAME)); private static final Object[] SUPPORTED_COLUMN_TYPES = { STRING, NUMBER, BOOLEAN, DECIMAL, DATE, DATETIME, TEXT, DROPDOWN }; - private static final Object[] SUPPORTED_APPTABLE_NAMES = { M_LOAN, M_SAVINGS_ACCOUNT, M_CLIENT, M_GROUP, M_CENTER, M_OFFICE, - M_SAVINGS_PRODUCT, M_PRODUCT_LOAN, M_SHARE_PRODUCT }; private final FromJsonHelper fromApiJsonHelper; @@ -158,12 +148,9 @@ public class DatatableCommandFromApiJsonDeserializer { final String apptableName = this.fromApiJsonHelper.extractStringNamed(APPTABLE_NAME, element); baseDataValidator.reset().parameter(APPTABLE_NAME).value(apptableName).notBlank().notExceedingLengthOf(50) - .isOneOfTheseValues(SUPPORTED_APPTABLE_NAMES); + .isOneOfTheseStringValues(EntityTables.getEntitiesList()); - if (M_CLIENT.equals(apptableName)) { - String entitySubType = this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element); - baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank(); // Person or Entity - } + validateEntitySubType(baseDataValidator, element, apptableName); final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : ""; final Boolean multiRow = this.fromApiJsonHelper.extractBooleanNamed(MULTI_ROW, element); @@ -194,6 +181,14 @@ public class DatatableCommandFromApiJsonDeserializer { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + private void validateEntitySubType(final DataValidatorBuilder baseDataValidator, final JsonElement element, final String apptableName) { + EntityTables entityTable = EntityTables.fromName(apptableName); + if (entityTable != null && EntityTables.CLIENT == entityTable) { + String entitySubType = this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element); + baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank(); // Person or Entity + } + } + public void validateForUpdate(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); @@ -218,12 +213,9 @@ public class DatatableCommandFromApiJsonDeserializer { final JsonElement element = this.fromApiJsonHelper.parse(json); final String apptableName = this.fromApiJsonHelper.extractStringNamed(APPTABLE_NAME, element); baseDataValidator.reset().parameter(APPTABLE_NAME).value(apptableName).ignoreIfNull().notBlank() - .isOneOfTheseValues(SUPPORTED_APPTABLE_NAMES); + .isOneOfTheseStringValues(EntityTables.getEntitiesList()); - if (M_CLIENT.equals(apptableName)) { - String entitySubType = this.fromApiJsonHelper.extractStringNamed(ENTITY_SUB_TYPE, element); - baseDataValidator.reset().parameter(ENTITY_SUB_TYPE).value(entitySubType).notBlank(); // Person or Entity - } + validateEntitySubType(baseDataValidator, element, apptableName); final String fkColumnName = (apptableName != null) ? apptableName.substring(2) + "_id" : ""; diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java index a41c632b2..bd0b5a399 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/EntityTables.java @@ -36,7 +36,12 @@ public enum EntityTables { ImmutableList.of(StatusEnum.CREATE.getCode(), StatusEnum.APPROVE.getCode(), StatusEnum.ACTIVATE.getCode(), StatusEnum.WITHDRAWN.getCode(), StatusEnum.REJECTED.getCode(), StatusEnum.CLOSE.getCode()), - "savings_account_id"); + "savings_account_id"), SAVINGS_TRANSACTION("m_savings_account_transaction", ImmutableList.of(), + "savings_account_transcation_id"), OFFICE("m_office", ImmutableList.of(), + "office_id"), PRODUCT_LOAN("m_product_loan", ImmutableList.of(), + "product_loan_id"), SAVINGS_PRODUCT("m_savings_product", ImmutableList.of(), + "savings_product_id"), SHARE_PRODUCT("m_share_product", + ImmutableList.of(), "share_product_id"); private static final Map<String, EntityTables> lookup = new HashMap<String, EntityTables>(); @@ -77,6 +82,10 @@ public enum EntityTables { return ImmutableList.of(); } + public static EntityTables fromName(String name) { + return name == null ? null : lookup.get(name.toLowerCase()); + } + public ImmutableList<Integer> getCodes() { return this.codes; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java index 7ff3c824f..9279a12c5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformServiceImpl.java @@ -105,10 +105,11 @@ public class EntityDatatableChecksWritePlatformServiceImpl implements EntityData throw new EntityDatatableCheckAlreadyExistsException(entity, status, datatableName); } } else { - if (entity.equals("m_loan")) { + EntityTables entityTable = EntityTables.fromName(entity); + if (EntityTables.LOAN == entityTable) { // if invalid loan product id, throws exception this.loanProductReadPlatformService.retrieveLoanProduct(productId); - } else if (entity.equals("m_savings_account")) { + } else if (EntityTables.SAVING == entityTable) { // if invalid savings product id, throws exception this.savingsProductReadPlatformService.retrieveOne(productId); } else { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index 06f2f0521..ae5f88700 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -70,6 +70,7 @@ import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeReso import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant; import org.apache.fineract.infrastructure.dataqueries.data.DataTableValidator; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; +import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData; import org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeaderData; import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData; @@ -737,7 +738,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } validateDatatableName(datatableName); - validateAppTable(apptableName); + validateAppTable(actualAppTableName); final boolean isConstraintApproach = this.configurationDomainService.isConstraintApproachEnabledForDatatables(); final String fkColumnName = apptableName.substring(2) + "_id"; final String dataTableNameAlias = datatableName.toLowerCase().replaceAll("\\s", "_"); @@ -1714,98 +1715,102 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ * unfortunately have to, one way or another, be able to restrict data to the users office hierarchy. Here, a * few key tables are done. But if additional fields are needed on other tables the same pattern applies */ - final AppUser currentUser = this.context.authenticatedUser(); + String currentUserOfficeHierarchy = currentUser.getOffice().getHierarchy(); + String officeHierarchyCondition = " o.hierarchy like '" + currentUserOfficeHierarchy + "%'"; String scopedSQL = null; /* * m_loan and m_savings_account are connected to an m_office thru either an m_client or an m_group If both it * means it relates to an m_client that is in a group (still an m_client account) */ - if (appTable.equalsIgnoreCase("m_loan")) { - scopedSQL = "select distinct x.* from (" - + " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId from m_loan l " - + " join m_client c on c.id = l.client_id " + " join m_office o on o.id = c.office_id and o.hierarchy like '" - + currentUser.getOffice().getHierarchy() + "%'" + " where l.id = " + appTableId + ")" + " union all " - + " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId from m_loan l " - + " join m_group g on g.id = l.group_id " + " join m_office o on o.id = g.office_id and o.hierarchy like '" - + currentUser.getOffice().getHierarchy() + "%'" + " where l.id = " + appTableId + ")" + " ) as x"; - } - if (appTable.equalsIgnoreCase("m_savings_account")) { - scopedSQL = "select distinct x.* from (" - + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId from m_savings_account s " - + " join m_client c on c.id = s.client_id " + " join m_office o on o.id = c.office_id and o.hierarchy like '" - + currentUser.getOffice().getHierarchy() + "%'" + " where s.id = " + appTableId + ")" + " union all " - + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId from m_savings_account s " - + " join m_group g on g.id = s.group_id " + " join m_office o on o.id = g.office_id and o.hierarchy like '" - + currentUser.getOffice().getHierarchy() + "%'" + " where s.id = " + appTableId + ")" + " ) as x"; - } - if (appTable.equalsIgnoreCase("m_client")) { - scopedSQL = "select o.id as officeId, null as groupId, c.id as clientId, null as savingsId, null as loanId, null as entityId from m_client c " - + " join m_office o on o.id = c.office_id and o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'" - + " where c.id = " + appTableId; - } - if (appTable.equalsIgnoreCase("m_group") || appTable.equalsIgnoreCase("m_center")) { - scopedSQL = "select o.id as officeId, g.id as groupId, null as clientId, null as savingsId, null as loanId, null as entityId from m_group g " - + " join m_office o on o.id = g.office_id and o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'" - + " where g.id = " + appTableId; - } - if (appTable.equalsIgnoreCase("m_office")) { - scopedSQL = "select o.id as officeId, null as groupId, null as clientId, null as savingsId, null as loanId, null as entityId from m_office o " - + " where o.hierarchy like '" + currentUser.getOffice().getHierarchy() + "%'" + " and o.id = " + appTableId; - } - - if (appTable.equalsIgnoreCase("m_product_loan") || appTable.equalsIgnoreCase("m_savings_product") - || appTable.equalsIgnoreCase("m_share_product")) { - scopedSQL = "select null as officeId, null as groupId, null as clientId, null as savingsId, null as loanId, p.id as entityId from " - + appTable + " as p WHERE p.id = " + appTableId; - } - - if (scopedSQL == null) { - throw new PlatformDataIntegrityException("error.msg.invalid.dataScopeCriteria", - "Application Table: " + appTable + " not catered for in data Scoping"); + EntityTables entityTable = EntityTables.fromName(appTable); + switch (entityTable) { + case LOAN: + scopedSQL = "select distinct x.* from (" + + " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId from m_loan l " + + getClientOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "l") + " where l.id = " + appTableId + ")" + + " union all " + + " (select o.id as officeId, l.group_id as groupId, l.client_id as clientId, null as savingsId, l.id as loanId, null as entityId from m_loan l " + + getGroupOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "l") + " where l.id = " + appTableId + ")" + + " ) as x"; + break; + case SAVING: + scopedSQL = "select distinct x.* from (" + + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId from m_savings_account s " + + getClientOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " where s.id = " + appTableId + ")" + + " union all " + + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, null as entityId from m_savings_account s " + + getGroupOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " where s.id = " + appTableId + ")" + + " ) as x"; + break; + case SAVINGS_TRANSACTION: + scopedSQL = "select distinct x.* from (" + + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, t.id as entityId from m_savings_account_transaction t" + + " join m_savings_account s on t.savings_account_id = s.id " + + getClientOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " where t.id = " + appTableId + ")" + + " union all " + + " (select o.id as officeId, s.group_id as groupId, s.client_id as clientId, s.id as savingsId, null as loanId, t.id as entityId from m_savings_account_transaction t " + + " join m_savings_account s on t.savings_account_id = s.id " + + getGroupOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "s") + " where t.id = " + appTableId + ")" + + " ) as x"; + break; + case CLIENT: + scopedSQL = "select o.id as officeId, null as groupId, c.id as clientId, null as savingsId, null as loanId, null as entityId from m_client c " + + getOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "c") + " where c.id = " + appTableId; + break; + case GROUP: + scopedSQL = "select o.id as officeId, g.id as groupId, null as clientId, null as savingsId, null as loanId, null as entityId from m_group g " + + getOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "g") + " where g.id = " + appTableId; + break; + case OFFICE: + scopedSQL = "select o.id as officeId, null as groupId, null as clientId, null as savingsId, null as loanId, null as entityId from m_office o " + + " where " + officeHierarchyCondition + " and o.id = " + appTableId; + break; + case PRODUCT_LOAN: + case SAVINGS_PRODUCT: + case SHARE_PRODUCT: + scopedSQL = "select null as officeId, null as groupId, null as clientId, null as savingsId, null as loanId, p.id as entityId from " + + appTable + " as p WHERE p.id = " + appTableId; + break; + default: + throw new PlatformDataIntegrityException("error.msg.invalid.dataScopeCriteria", + "Application Table: " + appTable + " not catered for in data Scoping"); } - return scopedSQL; } - private void validateAppTable(final String appTable) { + private String getClientOfficeJoinCondition(String currentUserOfficeHierarchy, Long appTableId, String appTableAlias) { + StringBuilder clientOfficeJoinBuilder = new StringBuilder(); + clientOfficeJoinBuilder.append(" join m_client c on c.id = ").append(appTableAlias).append(".client_id ") + .append(getOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "c")); + return clientOfficeJoinBuilder.toString(); + } - if (appTable.equalsIgnoreCase("m_loan")) { - return; - } - if (appTable.equalsIgnoreCase("m_savings_account")) { - return; - } - if (appTable.equalsIgnoreCase("m_client")) { - return; - } - if (appTable.equalsIgnoreCase("m_group")) { - return; - } - if (appTable.equalsIgnoreCase("m_center")) { - return; - } - if (appTable.equalsIgnoreCase("m_office")) { - return; - } - if (appTable.equalsIgnoreCase("m_product_loan")) { - return; - } - if (appTable.equalsIgnoreCase("m_savings_product")) { - return; - } - if (appTable.equalsIgnoreCase("m_share_product")) { - return; - } + private String getGroupOfficeJoinCondition(String currentUserOfficeHierarchy, Long appTableId, String appTableAlias) { + StringBuilder clientOfficeJoinBuilder = new StringBuilder(); + clientOfficeJoinBuilder.append(" join m_group g on g.id = ").append(appTableAlias).append(".client_id ") + .append(getOfficeJoinCondition(currentUserOfficeHierarchy, appTableId, "g")); + return clientOfficeJoinBuilder.toString(); + } - throw new PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid Application Table: " + appTable, "name", - appTable); + private String getOfficeJoinCondition(String currentUserOfficeHierarchy, Long appTableId, String joiningTableAlias) { + StringBuilder officeJoinBuilder = new StringBuilder(); + officeJoinBuilder.append(" join m_office o on o.id = ").append(joiningTableAlias).append(".office_id and ") + .append(" o.hierarchy like '").append(currentUserOfficeHierarchy).append("%' "); + return officeJoinBuilder.toString(); + } + + private void validateAppTable(final String appTable) { + if (EntityTables.fromName(appTable) == null) { + throw new PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid Application Table: " + appTable, + "name", appTable); + } } private String mapToActualAppTable(final String appTable) { - if (appTable.equalsIgnoreCase("m_center")) { - return "m_group"; + if ("m_center".equalsIgnoreCase(appTable)) { + return EntityTables.GROUP.getName(); } return appTable; } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountTransactionDatatableIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountTransactionDatatableIntegrationTest.java new file mode 100644 index 000000000..52ca3180d --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SavingsAccountTransactionDatatableIntegrationTest.java @@ -0,0 +1,249 @@ +/** + * 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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.google.gson.Gson; +import io.restassured.builder.RequestSpecBuilder; +import io.restassured.builder.ResponseSpecBuilder; +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; +import io.restassured.specification.ResponseSpecification; +import java.util.HashMap; +import java.util.List; +import org.apache.fineract.client.models.GetDataTablesResponse; +import org.apache.fineract.client.models.PostColumnHeaderData; +import org.apache.fineract.client.models.PostDataTablesRequest; +import org.apache.fineract.client.models.PostDataTablesResponse; +import org.apache.fineract.client.models.PutDataTablesRequest; +import org.apache.fineract.client.models.PutDataTablesRequestAddColumns; +import org.apache.fineract.client.models.PutDataTablesResponse; +import org.apache.fineract.client.models.ResultsetColumnHeaderData; +import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.CommonConstants; +import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper; +import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; +import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker; +import org.apache.fineract.integrationtests.common.system.DatatableHelper; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SavingsAccountTransactionDatatableIntegrationTest { + + private static final String SAVINGS_TRANSACTION_APP_TABLE_NAME = EntityTables.SAVINGS_TRANSACTION.getName(); + public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL"; + public static final String DEFAULT_DATE_FORMAT = "dd MMM yyyy"; + final String startDate = "01 Jun 2023"; + final String firstDepositDate = "05 Jun 2023"; + final String secondDepositDate = "09 Jun 2023"; + private RequestSpecification requestSpec; + private ResponseSpecification responseSpec; + private DatatableHelper datatableHelper; + private SavingsProductHelper savingsProductHelper; + private SavingsAccountHelper savingsAccountHelper; + + @BeforeEach + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.datatableHelper = new DatatableHelper(this.requestSpec, this.responseSpec); + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + this.savingsProductHelper = new SavingsProductHelper(); + } + + @Test + public void testDatatableCreateReadUpdateDeleteForSavingsAccountTransaction() { + // create dataTable + String datatableName = Utils.uniqueRandomStringGenerator("dt_savings_transaction_", 5).toLowerCase().toLowerCase(); + String column1Name = "aNumber"; + String column2Name = "aString"; + String column3Name = "aBoolean"; + + PostDataTablesRequest request = new PostDataTablesRequest(); + request.setDatatableName(datatableName); + request.setApptableName(SAVINGS_TRANSACTION_APP_TABLE_NAME); + request.setMultiRow(false); + + PostColumnHeaderData column1HeaderRequestData = new PostColumnHeaderData(); + column1HeaderRequestData.setName(column1Name); + column1HeaderRequestData.setType("Number"); + column1HeaderRequestData.setMandatory(false); + column1HeaderRequestData.setLength(10L); + column1HeaderRequestData.setCode(""); + column1HeaderRequestData.setUnique(false); + column1HeaderRequestData.setIndexed(false); + + request.addColumnsItem(column1HeaderRequestData); + + PostColumnHeaderData column2HeaderRequestData = new PostColumnHeaderData(); + column2HeaderRequestData.setName(column2Name); + column2HeaderRequestData.setType("String"); + column2HeaderRequestData.setMandatory(false); + column2HeaderRequestData.setLength(10L); + column2HeaderRequestData.setCode(""); + column2HeaderRequestData.setUnique(false); + column2HeaderRequestData.setIndexed(false); + + request.addColumnsItem(column2HeaderRequestData); + + PostDataTablesResponse response = datatableHelper.createDatatable(request); + assertNotNull(response.getResourceIdentifier()); + + // update datatable + PutDataTablesRequest putRequest = new PutDataTablesRequest(); + putRequest.setApptableName(SAVINGS_TRANSACTION_APP_TABLE_NAME); + PutDataTablesRequestAddColumns column3HeaderPutRequestData = new PutDataTablesRequestAddColumns(); + column3HeaderPutRequestData.setName(column3Name); + column3HeaderPutRequestData.setType("Boolean"); + column3HeaderPutRequestData.setMandatory(false); + + putRequest.addAddColumnsItem(column3HeaderPutRequestData); + + PutDataTablesResponse updateResponse = datatableHelper.updateDatatable(datatableName, putRequest); + assertNotNull(updateResponse.getResourceIdentifier()); + + // verify Datatable got created + GetDataTablesResponse dataTable = datatableHelper.getDataTableDetails(datatableName); + + // verfify columns + List<ResultsetColumnHeaderData> columnHeaderData = dataTable.getColumnHeaderData(); + assertNotNull(columnHeaderData); + + // two columns with 1 primary key and 2 audit columns created + assertEquals(columnHeaderData.size(), 6); + + // deleting the datatable + String deletedDataTableName = this.datatableHelper.deleteDatatable(datatableName); + assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING THE DATATABLE"); + } + + @Test + public void testDatatableCreateReadUpdateDeleteEntryForSavingsAccountTransaction() { + // Create Client + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate); + Assertions.assertNotNull(clientID); + // Create savings product and account + final Integer savingsId = createSavingsAccountDailyPosting(clientID, startDate); + + final Integer transactionId = (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, "100", firstDepositDate, + CommonConstants.RESPONSE_RESOURCE_ID); + + assertNotNull(transactionId); + + // create dataTable + String datatableName = Utils.uniqueRandomStringGenerator("dt_savings_transaction_", 5).toLowerCase().toLowerCase(); + String column1Name = "aNumber"; + + PostDataTablesRequest request = new PostDataTablesRequest(); + request.setDatatableName(datatableName); + request.setApptableName(SAVINGS_TRANSACTION_APP_TABLE_NAME); + request.setMultiRow(true); + + PostColumnHeaderData column1HeaderRequestData = new PostColumnHeaderData(); + column1HeaderRequestData.setName(column1Name); + column1HeaderRequestData.setType("Number"); + column1HeaderRequestData.setMandatory(false); + column1HeaderRequestData.setLength(10L); + column1HeaderRequestData.setCode(""); + column1HeaderRequestData.setUnique(false); + column1HeaderRequestData.setIndexed(false); + + request.addColumnsItem(column1HeaderRequestData); + + PostDataTablesResponse response = datatableHelper.createDatatable(request); + + assertNotNull(response); + + String datatableId = response.getResourceIdentifier(); + + // add entries + final HashMap<String, Object> datatableEntryMap = new HashMap<>(); + datatableEntryMap.put(column1Name, Utils.randomNumberGenerator(5)); + datatableEntryMap.put("locale", "en"); + datatableEntryMap.put("dateFormat", "yyyy-MM-dd"); + + String datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap); + + final boolean genericResultSet = true; + + HashMap<String, Object> datatableEntryResponseFirst = this.datatableHelper.createDatatableEntry(datatableId, transactionId, + genericResultSet, datatabelEntryRequestJsonString); + + assertNotNull(datatableEntryResponseFirst.get("resourceId")); + + // Read the Datatable entry generated with genericResultSet + HashMap<String, Object> items = this.datatableHelper.readDatatableEntry(datatableId, transactionId, genericResultSet, null, ""); + assertNotNull(items); + assertEquals(1, ((List) items.get("data")).size()); + + // update datatable entry + datatableEntryMap.put(column1Name, 100); + datatableEntryMap.put("locale", "en"); + datatableEntryMap.put("dateFormat", "yyyy-MM-dd"); + datatabelEntryRequestJsonString = new Gson().toJson(datatableEntryMap); + HashMap<String, Object> updatedDatatableEntryResponse = this.datatableHelper.updateDatatableEntry(datatableName, transactionId, + false, datatabelEntryRequestJsonString); + + assertEquals(transactionId, updatedDatatableEntryResponse.get("resourceId")); + + // deleting datatable entries + Integer appTableId = this.datatableHelper.deleteDatatableEntries(datatableName, transactionId, "resourceId"); + assertEquals(transactionId, appTableId, "ERROR IN DELETING THE DATATABLE ENTRIES"); + + // deleting the datatable + String deletedDataTableName = this.datatableHelper.deleteDatatable(datatableName); + assertEquals(datatableName, deletedDataTableName, "ERROR IN DELETING THE DATATABLE"); + } + + private Integer createSavingsAccountDailyPosting(final Integer clientID, final String startDate) { + final Integer savingsProductID = createSavingsProductDailyPosting(); + Assertions.assertNotNull(savingsProductID); + final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, savingsProductID, + ACCOUNT_TYPE_INDIVIDUAL, startDate); + Assertions.assertNotNull(savingsId); + HashMap savingsStatusHashMap = this.savingsAccountHelper.approveSavingsOnDate(savingsId, startDate); + SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap); + savingsStatusHashMap = this.savingsAccountHelper.activateSavingsAccount(savingsId, startDate); + SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap); + return savingsId; + } + + private Integer createSavingsProductDailyPosting() { + final String savingsProductJSON = this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily() + .withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build(); + return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec); + } + + // Reset configuration fields + @AfterEach + public void tearDown() { + GlobalConfigurationHelper.resetAllDefaultGlobalConfigurations(this.requestSpec, this.responseSpec); + GlobalConfigurationHelper.verifyAllDefaultGlobalConfigurations(this.requestSpec, this.responseSpec); + } +}