budaidev commented on code in PR #6084:
URL: https://github.com/apache/fineract/pull/6084#discussion_r3530302196


##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java:
##########
@@ -95,9 +122,61 @@ public Long createSimpleLoanProduct() {
         return createLoanProduct(request);
     }
 
-    public Long createLoanProduct(PostLoanProductsRequest request) {
-        PostLoanProductsResponse response = ok(() -> 
fineractClient.loanProducts().createLoanProduct(request));
-        return response.getResourceId();
+    public PostLoanProductsResponse createLoanProduct(PostLoanProductsRequest 
request) {
+        return ok(() -> 
fineractClient.loanProducts().createLoanProduct(request));
+    }
+
+    /**
+     * WARNING: This method uses ObjectMapperFactory which silences unknown 
property errors. Do not use this method in
+     * tests expecting strict deserialization.
+     */
+    public Long createLoanProductFromJson(String loanProductJson) {
+        try {
+            String sanitizedJson = 
loanProductJson.replaceAll("(?<=\\d),(?=\\d{3})", "");
+            PostLoanProductsRequest request = 
ObjectMapperFactory.getShared().readValue(sanitizedJson, 
PostLoanProductsRequest.class);
+            return createLoanProduct(request).getResourceId();
+        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
+            throw new IllegalArgumentException("Invalid loan product json", e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T extractErrorAttribute(CallFailedRuntimeException exception, 
String jsonAttributeToGetBack) {
+        if (!(exception.getCause() instanceof 
org.apache.fineract.client.feign.FeignException feignException)) {
+            throw new IllegalStateException("Expected FeignException cause");
+        }
+        try {
+            Map<String, Object> body = 
ObjectMapperFactory.getShared().readValue(feignException.responseBodyAsString(),
 Map.class);
+            return (T) body.get(jsonAttributeToGetBack);
+        } catch (com.fasterxml.jackson.core.JsonProcessingException e) {
+            throw new IllegalStateException("Failed to parse error response 
for attribute " + jsonAttributeToGetBack, e);
+        }
+    }
+
+    public <T> T getLoanProductError(String loanProductJson, String 
jsonAttributeToGetBack) {
+        try {
+            String sanitizedJson = 
loanProductJson.replaceAll("(?<=\\d),(?=\\d{3})", "");

Review Comment:
   Here, any numeric array/sequence where a value with ≥3 digits follows a 
comma gets silently merged. For example:  {"loanIds":[5,1000]} → 
{"loanIds":[51000]}. I don't think this is the indended behaviour here.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java:
##########
@@ -95,9 +122,61 @@ public Long createSimpleLoanProduct() {
         return createLoanProduct(request);
     }
 
-    public Long createLoanProduct(PostLoanProductsRequest request) {
-        PostLoanProductsResponse response = ok(() -> 
fineractClient.loanProducts().createLoanProduct(request));
-        return response.getResourceId();
+    public PostLoanProductsResponse createLoanProduct(PostLoanProductsRequest 
request) {
+        return ok(() -> 
fineractClient.loanProducts().createLoanProduct(request));
+    }
+
+    /**
+     * WARNING: This method uses ObjectMapperFactory which silences unknown 
property errors. Do not use this method in
+     * tests expecting strict deserialization.
+     */
+    public Long createLoanProductFromJson(String loanProductJson) {
+        try {
+            String sanitizedJson = 
loanProductJson.replaceAll("(?<=\\d),(?=\\d{3})", "");

Review Comment:
   Here, any numeric array/sequence where a value with ≥3 digits follows a 
comma gets silently merged. For example:  {"loanIds":[5,1000]} → 
{"loanIds":[51000]}. I don't think this is the indended behaviour here.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanUndoChargeOffReverseExternalIdTest.java:
##########
@@ -47,33 +48,36 @@
 import org.junit.jupiter.api.extension.ExtendWith;
 
 @ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanUndoChargeOffReverseExternalIdTest {
+public class LoanUndoChargeOffReverseExternalIdTest extends FeignLoanTestBase {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private ClientHelper clientHelper;
-    private LoanTransactionHelper loanTransactionHelper;
-    private AccountHelper accountHelper;
-    private Account assetAccount;
-    private Account incomeAccount;
-    private Account expenseAccount;
-    private Account overpaymentAccount;
+    protected ResponseSpecification responseSpec;
+    protected RequestSpecification requestSpec;
 
     @BeforeEach
-    public void setup() {
+    public void setupREST() {
         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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+
         this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
         this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
         this.assetAccount = this.accountHelper.createAssetAccount();
         this.incomeAccount = this.accountHelper.createIncomeAccount();
         this.expenseAccount = this.accountHelper.createExpenseAccount();
         this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
     }
 
+    private ClientHelper clientHelper;
+    private LoanTransactionHelper loanTransactionHelper;
+
+    private AccountHelper accountHelper;

Review Comment:
   unused here, it shadows the parent classes field (same as above)



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/FeignLoanTestBase.java:
##########
@@ -114,12 +155,11 @@ public static void setupHelpers() {
         codeHelper = new FeignCodeHelper(client);
         globalConfigurationHelper = new FeignGlobalConfigurationHelper(client);
         schedulerHelper = new FeignSchedulerHelper(client);
+        externalEventHelper = new FeignExternalEventHelper(client);
+        accounts = new LoanTestAccounts(accountHelper);

Review Comment:
    accounts = new LoanTestAccounts(accountHelper) is now eager in @BeforeAll 
(was lazy in getAccounts()). That's ~21 create-GL-account calls and 21 junk 
accounts per test class — for all 56 classes this PR moves onto the base, 
including ones that never touch accounting. Please restore lazy initialization.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java:
##########
@@ -68,39 +69,45 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
+public class LoanChargeOffAccountingTest extends FeignLoanTestBase {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private ClientHelper clientHelper;
-    private LoanTransactionHelper loanTransactionHelper;
-    private JournalEntryHelper journalEntryHelper;
-    private AccountHelper accountHelper;
-    private Account assetAccount;
-    private Account incomeAccount;
-    private Account expenseAccount;
-    private Account overpaymentAccount;
-    private DateTimeFormatter dateFormatter = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
-    private InlineLoanCOBHelper inlineLoanCOBHelper;
+    protected RequestSpecification requestSpec;
+    protected ResponseSpecification responseSpec;
+    protected LoanTransactionHelper loanTransactionHelper;
+    protected AccountHelper accountHelper;
+    protected JournalEntryHelper journalEntryHelper;
 
     @BeforeEach
-    public void setup() {
+    @SuppressWarnings("removal")
+    public void setupREST() {
         Utils.initializeRESTAssured();
+
         this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
         this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
         this.requestSpec.header("Fineract-Platform-TenantId", "default");
         this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+
         this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
         this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
-        this.assetAccount = this.accountHelper.createAssetAccount();
-        this.incomeAccount = this.accountHelper.createIncomeAccount();
-        this.expenseAccount = this.accountHelper.createExpenseAccount();
-        this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
         this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
-        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
         this.inlineLoanCOBHelper = new InlineLoanCOBHelper(this.requestSpec, 
this.responseSpec);
+
+        this.assetAccount = getAccounts().getLoansReceivableAccount();
+        this.incomeAccount = getAccounts().getInterestIncomeAccount();
+        this.expenseAccount = getAccounts().getChargeOffExpenseAccount();
+        this.overpaymentAccount = getAccounts().getOverpaymentAccount();
     }
 
+    private ClientHelper clientHelper;

Review Comment:
   unused here, no init no usage



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAuditingIntegrationTest.java:
##########
@@ -33,46 +35,37 @@
 import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
+import java.util.Map;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
-import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignRawHttpHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import org.apache.fineract.integrationtests.common.accounting.Account;
-import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
-import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
 import 
org.apache.fineract.integrationtests.useradministration.users.UserHelper;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanTransactionAuditingIntegrationTest {
+public class LoanTransactionAuditingIntegrationTest extends FeignLoanTestBase {

Review Comment:
   Dropped assertions: verifyLoanIsPending/Approved/Active after each lifecycle 
step and verifyClientCreatedOnServer. The Feign base helpers don't assert 
status, so these four checks vanished. Re-add via 
getLoanDetails(loanId).getStatus().
   



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountBackdatedDisbursementTest.java:
##########
@@ -38,39 +39,49 @@
 import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
 import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
 import 
org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
 import org.apache.fineract.integrationtests.common.BusinessDateHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.CommonConstants;
 import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import 
org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-public class LoanAccountBackdatedDisbursementTest extends 
BaseLoanIntegrationTest {
+public class LoanAccountBackdatedDisbursementTest extends FeignLoanTestBase {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private LoanTransactionHelper loanTransactionHelper;
-    private ClientHelper clientHelper;
+    protected RequestSpecification requestSpec;
+    protected ResponseSpecification responseSpec;
+    protected LoanTransactionHelper loanTransactionHelper;
+    protected AccountHelper accountHelper;
+    protected JournalEntryHelper journalEntryHelper;
 
     @BeforeEach
-    public void setup() {
+    @SuppressWarnings("removal")
+    public void setupREST() {
         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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
+        this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
     }
 
+    private ClientHelper clientHelper;

Review Comment:
   declared but never used



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanChargeExternalIntegrationTest.java:
##########
@@ -18,131 +18,74 @@
  */
 package org.apache.fineract.integrationtests;
 
-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.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.Utils;
-import org.apache.fineract.integrationtests.common.accounting.Account;
-import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
+import org.apache.fineract.client.models.GetLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdChargesRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdChargesResponse;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
-import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-@SuppressWarnings({ "rawtypes", "unchecked" })
-@ExtendWith(LoanTestLifecycleExtension.class)
-public class ClientLoanChargeExternalIntegrationTest {
+public class ClientLoanChargeExternalIntegrationTest extends FeignLoanTestBase 
{
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(ClientLoanChargeExternalIntegrationTest.class);
-
-    public static final String MINIMUM_OPENING_BALANCE = "1000.0";
-    public static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
     private static final String NONE = "1";
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private LoanTransactionHelper loanTransactionHelper;
+    private static Long clientId;
 
     @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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        clientId = createClient("20 September 2011");
     }
 
     @Test
     public void checkNewClientLoanChargeSavesExternalId() {
 
-        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
-        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientID);
-
-        final Integer loanProductID = createLoanProduct(false, NONE);
+        final Long loanProductId = createLoanProduct(false, NONE);
 
-        List<HashMap> collaterals = new ArrayList<>();
-        final Integer loanID = applyForLoanApplication(clientID, 
loanProductID, null, null, "12,000.00", collaterals);
-        HashMap loanStatusHashMap = this.loanTransactionHelper.approveLoan("20 
September 2011", loanID);
-        LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
-        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("20 September 
2011", loanID, "12,000.00");
-        LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+        final Long loanId = applyForLoanApplication(clientId, loanProductId, 
"12,000.00");
+        approveLoan(loanId, approveLoanRequest(12000.0, "20 September 2011"));
+        disburseLoanWithNetDisbursalAmount(loanId, "20 September 2011", 
"12,000.00");
 
-        final Integer charge = ChargesHelper.createCharges(requestSpec, 
responseSpec, ChargesHelper
-                
.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT_AND_INTEREST,
 "1", false));
+        final Long chargeDefId = 
chargesHelper.createLoanSpecifiedDueDatePercentageAmountAndInterestFee(1.0).getResourceId();
 
-        final float amount = 1.0f;
-        final String externalId = "extId" + loanID.toString();
-        final Integer chargeID = 
this.loanTransactionHelper.addChargesForLoan(loanID,
-                getLoanChargeAsJSON(String.valueOf(charge), "22 September 
2011", String.valueOf(amount), externalId));
-        Assertions.assertNotNull(chargeID);
-
-        HashMap loanChargeMap = 
this.loanTransactionHelper.getLoanCharge(loanID, chargeID);
-        Assertions.assertNotNull(loanChargeMap.get("externalId"));
-        Assertions.assertEquals(loanChargeMap.get("externalId"), externalId, 
"Incorrect External Id Saved");
+        final String externalId = "extId" + loanId.toString();
+        PostLoansLoanIdChargesResponse chargeResponse = addLoanCharge(loanId, 
new PostLoansLoanIdChargesRequest().chargeId(chargeDefId)
+                .amount(1.0).dueDate("22 September 
2011").externalId(externalId).dateFormat("dd MMMM yyyy").locale("en"));
+        assertNotNull(chargeResponse);
+        Long chargeId = chargeResponse.getResourceId();
 
+        GetLoansLoanIdChargesChargeIdResponse loanCharge = 
getLoanCharge(loanId, chargeId);
+        assertNotNull(loanCharge.getExternalId());
+        assertEquals(externalId, loanCharge.getExternalId(), "Incorrect 
External Id Saved");
     }
 
     @Test
     public void checkNewClientLoanChargeFindsDuplicateExternalId() {
 
-        final Integer clientID = ClientHelper.createClient(this.requestSpec, 
this.responseSpec);
-        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientID);
-
-        final Integer loanProductID = createLoanProduct(false, NONE);
+        final Long loanProductId = createLoanProduct(false, NONE);
 
-        List<HashMap> collaterals = new ArrayList<>();
-        final Integer loanID = applyForLoanApplication(clientID, 
loanProductID, null, null, "12,000.00", collaterals);
-        HashMap loanStatusHashMap = this.loanTransactionHelper.approveLoan("20 
September 2011", loanID);
-        LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
-        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("20 September 
2011", loanID, "12,000.00");
-        LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+        final Long loanId = applyForLoanApplication(clientId, loanProductId, 
"12,000.00");
+        approveLoan(loanId, approveLoanRequest(12000.0, "20 September 2011"));
+        disburseLoanWithNetDisbursalAmount(loanId, "20 September 2011", 
"12,000.00");
 
-        final Integer charge = ChargesHelper.createCharges(requestSpec, 
responseSpec, ChargesHelper
-                
.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT_AND_INTEREST,
 "1", false));
+        final Long chargeDefId = 
chargesHelper.createLoanSpecifiedDueDatePercentageAmountAndInterestFee(1.0).getResourceId();
 
-        final String externalId = "extId" + loanID.toString();
-        final float amount = 1.0f;
-        final Integer chargeID = 
this.loanTransactionHelper.addChargesForLoan(loanID,
-                getLoanChargeAsJSON(String.valueOf(charge), "22 September 
2011", String.valueOf(amount), externalId));
-        Assertions.assertNotNull(chargeID);
+        final String externalId = "extId" + loanId.toString();
+        PostLoansLoanIdChargesResponse chargeResponse = addLoanCharge(loanId, 
new PostLoansLoanIdChargesRequest().chargeId(chargeDefId)
+                .amount(1.0).dueDate("22 September 
2011").externalId(externalId).dateFormat("dd MMMM yyyy").locale("en"));
+        assertNotNull(chargeResponse);
 
-        final float amount2 = 2.0f;
-        ResponseSpecification responseSpec403 = new 
ResponseSpecBuilder().expectStatusCode(403).build();
-        final Integer chargeID2 = 
this.loanTransactionHelper.addChargesForLoan(loanID,
-                getLoanChargeAsJSON(String.valueOf(charge), "23 September 
2011", String.valueOf(amount2), externalId), responseSpec403);
-
-        Assertions.assertNull(chargeID2);
-    }
-
-    private static String getLoanChargeAsJSON(final String chargeId, final 
String dueDate, final String amount, final String externalId) {
-        final HashMap<String, String> map = new HashMap<>();
-        map.put("locale", "en_GB");
-        map.put("dateFormat", "dd MMMM yyyy");
-        map.put("amount", amount);
-        map.put("dueDate", dueDate);
-        map.put("chargeId", chargeId);
-        map.put("externalId", externalId);
-        String json = new Gson().toJson(map);
-        LOG.info("{}", json);
-        return json;
+        assertThrows(CallFailedRuntimeException.class, () -> 
addLoanCharge(loanId, new PostLoansLoanIdChargesRequest().chargeId(chargeDefId)

Review Comment:
   he duplicate-external-id case previously asserted HTTP 403; now a bare 
assertThrows(CallFailedRuntimeException.class, ...) passes on any error. 
Capture the exception and assert getStatus() == 403 (+ the globalisation code), 
as done elsewhere in this PR.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountDisbursementToSavingsWithAutoDownPaymentTest.java:
##########
@@ -22,77 +22,93 @@
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
 import java.math.BigDecimal;
 import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.time.Month;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.UUID;
 import org.apache.fineract.accounting.common.AccountingConstants;
-import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
-import org.apache.fineract.client.models.GetSavingsAccountTransactionsPageItem;
+import org.apache.fineract.client.feign.FineractFeignClient;
+import 
org.apache.fineract.client.models.DeleteFinancialActivityAccountsResponse;
+import org.apache.fineract.client.models.GetFinancialActivityAccountsResponse;
+import org.apache.fineract.client.models.PostFinancialActivityAccountsRequest;
+import org.apache.fineract.client.models.PostFinancialActivityAccountsResponse;
 import org.apache.fineract.client.models.PostLoanProductsRequest;
-import org.apache.fineract.client.models.PostLoanProductsResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdResponse;
-import 
org.apache.fineract.client.models.SavingsAccountTransactionsSearchResponse;
+import org.apache.fineract.client.models.SavingsAccountData;
+import org.apache.fineract.client.models.SavingsAccountTransactionData;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
 import 
org.apache.fineract.infrastructure.event.external.data.ExternalEventResponse;
-import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.CommonConstants;
-import 
org.apache.fineract.integrationtests.common.ExternalEventConfigurationHelper;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignRawHttpHelper;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignSavingsHelper;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignSavingsProductHelper;
+import 
org.apache.fineract.integrationtests.client.feign.modules.LoanRequestBuilders;
+import 
org.apache.fineract.integrationtests.client.feign.modules.SavingsRequestBuilders;
+import org.apache.fineract.integrationtests.common.FineractFeignClientHelper;
 import 
org.apache.fineract.integrationtests.common.accounting.FinancialActivityAccountHelper;
-import 
org.apache.fineract.integrationtests.common.externalevents.ExternalEventHelper;
-import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
-import 
org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
-import 
org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
-import org.hamcrest.Matchers;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
-public class LoanAccountDisbursementToSavingsWithAutoDownPaymentTest extends 
BaseLoanIntegrationTest {
+public class LoanAccountDisbursementToSavingsWithAutoDownPaymentTest extends 
FeignLoanTestBase {
 
     public static final BigDecimal DOWN_PAYMENT_PERCENTAGE = new 
BigDecimal(25);
+    private static final String LOAN_BALANCE_CHANGED_EVENT = 
"LoanBalanceChangedBusinessEvent";
+
+    private static FeignSavingsHelper savingsHelper;
+    private static FeignSavingsProductHelper savingsProductHelper;
+    private static FinancialActivityAccountHelper 
financialActivityAccountHelper;
+
+    @BeforeAll
+    public static void setupSavingsAndFinancialActivityHelpers() {
+        FineractFeignClient client = 
FineractFeignClientHelper.getFineractFeignClient();
+        savingsHelper = new FeignSavingsHelper(client);
+        savingsProductHelper = new FeignSavingsProductHelper(client);
+        financialActivityAccountHelper = new 
FinancialActivityAccountHelper(null);
+    }
 
     @Test
     public void 
loanDisbursementToSavingsWithAutoDownPaymentAndStandingInstructionsTest() {
         runAt("01 March 2023", () -> {
-            enableLoanBalanceChangedBusinessEvent();
-            ExternalEventHelper.deleteAllExternalEvents(requestSpec, 
createResponseSpecification(Matchers.is(204)));
+            
externalEventHelper.enableBusinessEvent(LOAN_BALANCE_CHANGED_EVENT);
+            deleteAllExternalEvents();
 
-            // loan external Id
             String loanExternalIdStr = UUID.randomUUID().toString();
 
-            // Create Client
-            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            Long clientId = createClient();
 
-            // Create Loan Product
             Long loanProductId = 
createLoanProductWithMultiDisbursalAndRepaymentsWithEnableDownPayment();
 
-            SavingsAccountHelper savingsAccountHelper = new 
SavingsAccountHelper(requestSpec, responseSpec);
-
-            // Create approve and activate savings account
-            Integer savingsAccountId = 
createApproveActivateSavingsAccountDailyPosting(clientId.intValue(), "01 March 
2023",
-                    savingsAccountHelper);
+            Long savingsAccountId = 
createApproveActivateSavingsAccountDailyPosting(clientId, "01 March 2023");
 
-            // create Financial Activity Mapping for Liability Transfer
             mapLiabilityTransferFinancialActivity(loanProductId);
 
-            // Apply and Approve Loan
-            Long loanId = 
createLoanWithLinkedAccountAndStandingInstructions(clientId.intValue(), 
loanProductId, savingsAccountId,
-                    loanExternalIdStr);
+            String loanApplicationJSON = new 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder()
+                    
.withPrincipal("1000").withLoanTermFrequency("45").withLoanTermFrequencyAsDays().withNumberOfRepayments("3")
+                    
.withRepaymentEveryAfter("15").withRepaymentFrequencyTypeAsDays().withInterestRatePerPeriod("0")
+                    
.withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()

Review Comment:
   withInterestTypeAsDecliningBalance() → withInterestTypeAsFlatBalance() — 
assertion-neutral at 0% interest, but an unexplained semantic change, if it is 
possible keep the original configuration



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java:
##########
@@ -43,170 +38,94 @@
 import 
org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings;
 import org.apache.fineract.client.models.GetLoanTransactionRelation;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
 import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.JournalEntryTransactionItem;
 import org.apache.fineract.client.models.LoanProductChargeData;
 import org.apache.fineract.client.models.LoanProductChargeToGLAccountMapper;
 import org.apache.fineract.client.models.PaymentTypeCreateRequest;
-import org.apache.fineract.client.models.PostClientsResponse;
 import org.apache.fineract.client.models.PostLoanProductsRequest;
-import org.apache.fineract.client.models.PostLoanProductsResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
-import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import org.apache.fineract.integrationtests.client.feign.modules.LoanTestData;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.accounting.Account;
-import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
-import 
org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
-import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
-import org.apache.fineract.integrationtests.common.funds.FundsHelper;
+import org.apache.fineract.integrationtests.common.accounting.JournalEntry;
 import org.apache.fineract.integrationtests.common.funds.FundsResourceHandler;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanProductHelper;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import 
org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
-import org.apache.fineract.integrationtests.common.system.CodeHelper;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-public class LoanAccountChargeOffWithAdvancedPaymentAllocationTest extends 
BaseLoanIntegrationTest {
+public class LoanAccountChargeOffWithAdvancedPaymentAllocationTest extends 
FeignLoanTestBase {
 
     private static final DateTimeFormatter DATE_FORMATTER = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private ClientHelper clientHelper;
-    private LoanTransactionHelper loanTransactionHelper;
-    private JournalEntryHelper journalEntryHelper;
-    private AccountHelper accountHelper;
-    private LoanProductHelper loanProductHelper;
-    private PaymentTypeHelper paymentTypeHelper;
-    private final BusinessDateHelper businessDateHelper = new 
BusinessDateHelper();
-    private static final String DATETIME_PATTERN = "dd MMMM yyyy";
-    // asset
-    private Account loansReceivable;
-    private Account interestFeeReceivable;
-    private Account suspenseAccount;
-    private Account fundReceivables;
-    // liability
-    private Account suspenseClearingAccount;
-    private Account overpaymentAccount;
-    // income
-    private Account interestIncome;
-    private Account feeIncome;
-    private Account feeChargeOff;
-    private Account recoveries;
-    private Account interestIncomeChargeOff;
-    // expense
-    private Account creditLossBadDebt;
-    private Account creditLossBadDebtFraud;
-    private Account writtenOff;
-    private Account goodwillExpenseAccount;
-
-    @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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
-        this.loanProductHelper = new LoanProductHelper();
-        this.paymentTypeHelper = new PaymentTypeHelper();
-
-        // Asset
-        this.loansReceivable = this.accountHelper.createAssetAccount();
-        this.interestFeeReceivable = this.accountHelper.createAssetAccount();
-        this.suspenseAccount = this.accountHelper.createAssetAccount();
-        this.fundReceivables = this.accountHelper.createAssetAccount();
-
-        // Liability
-        this.suspenseClearingAccount = 
this.accountHelper.createLiabilityAccount();
-        this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
-
-        // income
-        this.interestIncome = this.accountHelper.createIncomeAccount();
-        this.feeIncome = this.accountHelper.createIncomeAccount();
-        this.feeChargeOff = this.accountHelper.createIncomeAccount();
-        this.recoveries = this.accountHelper.createIncomeAccount();
-        this.interestIncomeChargeOff = 
this.accountHelper.createIncomeAccount();
-
-        // expense
-        this.creditLossBadDebt = this.accountHelper.createExpenseAccount();
-        this.creditLossBadDebtFraud = 
this.accountHelper.createExpenseAccount();
-        this.writtenOff = this.accountHelper.createExpenseAccount();
-        this.goodwillExpenseAccount = 
this.accountHelper.createExpenseAccount();
-
-        this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
-        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
-    }
 
     // Charge-off accounting and balances
     @Test
     public void loanChargeOffWithAdvancedPaymentStrategyTest() {
         runAt("10 September 2022", () -> {
             String loanExternalIdStr = UUID.randomUUID().toString();
-            final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
-            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
-            final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+            final Long loanProductId = 
createLoanProductWithPeriodicAccrualAccountingAndAdvancedPaymentAllocationStrategy();
+            final Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            final Long loanId = createLoanAccount(clientId, loanProductId, 
loanExternalIdStr);
 
             // apply charges
-            Integer feeCharge = ChargesHelper.createCharges(requestSpec, 
responseSpec,
-                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+            Long feeCharge = createLoanSpecifiedDueDateCharge(10.0);
 
-            LocalDate targetDate = LocalDate.of(2022, 9, 5);
+            LocalDate targetDate = LocalDate.of(2022, Month.SEPTEMBER, 5);
             final String feeCharge1AddedDate = 
DATE_FORMATTER.format(targetDate);
-            Integer feeLoanChargeId = 
loanTransactionHelper.addChargesForLoan(loanId,
-                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+            addLoanCharge(loanId, feeCharge, feeCharge1AddedDate, 10.0);
 
             // apply penalty
-            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
-                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
-
+            Long penalty = createLoanSpecifiedDueDateCharge(10.0);

Review Comment:
   dead code



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanReschedulingWithinCenterTest.java:
##########
@@ -19,382 +19,279 @@
 package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import com.google.gson.Gson;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.builder.ResponseSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.path.json.JsonPath;
-import io.restassured.specification.RequestSpecification;
-import io.restassured.specification.ResponseSpecification;
+import com.google.gson.JsonObject;
 import java.math.BigDecimal;
 import java.sql.Timestamp;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.UUID;
+import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostClientsRequest;
+import org.apache.fineract.client.models.PostLoansDisbursementData;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignGroupCenterHelper;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignOfficeHelper;
+import 
org.apache.fineract.integrationtests.client.feign.modules.LoanRequestBuilders;
+import org.apache.fineract.integrationtests.client.feign.modules.LoanTestData;
 import org.apache.fineract.integrationtests.common.CalendarHelper;
-import org.apache.fineract.integrationtests.common.CenterDomain;
-import org.apache.fineract.integrationtests.common.CenterHelper;
-import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
-import org.apache.fineract.integrationtests.common.GroupHelper;
-import org.apache.fineract.integrationtests.common.OfficeHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import org.apache.fineract.integrationtests.common.accounting.Account;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
-import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class LoanReschedulingWithinCenterTest extends BaseLoanIntegrationTest {
+public class LoanReschedulingWithinCenterTest extends FeignLoanTestBase {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(LoanReschedulingWithinCenterTest.class);
-    private RequestSpecification requestSpec;
-    private ResponseSpecification responseSpec;
-    private LoanTransactionHelper loanTransactionHelper;
-    private ResponseSpecification generalResponseSpec;
-    private LoanApplicationApprovalTest loanApplicationApprovalTest;
-
-    @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.requestSpec.header("Fineract-Platform-TenantId", "default");
-        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.loanApplicationApprovalTest = new LoanApplicationApprovalTest();
-        this.generalResponseSpec = new ResponseSpecBuilder().build();
-
-        globalConfigurationHelper.verifyAllDefaultGlobalConfigurations();
-    }
+    private static final Long LEGAL_FORM_PERSON = 1L;
 
-    @SuppressWarnings("rawtypes")
     @Test
     public void testCenterReschedulingLoansWithInterestRecalculationEnabled() {
-
-        Integer officeId = new OfficeHelper().createOffice(LocalDate.of(2007, 
7, 1)).getResourceId().intValue();
+        Long officeId = new 
FeignOfficeHelper(fineractClient()).createOffice(LocalDate.of(2007, Month.JULY, 
1)).getResourceId();
         String name = "TestFullCreation" + new Timestamp(new 
java.util.Date().getTime());
         String externalId = UUID.randomUUID().toString();
-        int staffId = StaffHelper.createStaff(requestSpec, responseSpec);
-        int[] groupMembers = generateGroupMembers(1, officeId);
+        int staffId = 
FeignGroupCenterHelper.createStaff(officeId.intValue()).intValue();
+        long groupId = FeignGroupCenterHelper.createGroup(officeId.intValue());
         final String centerActivationDate = "01 July 2007";
-        Integer centerId = CenterHelper.createCenter(name, officeId, 
externalId, staffId, groupMembers, centerActivationDate, requestSpec,
-                responseSpec);
-        CenterDomain center = CenterHelper.retrieveByID(centerId, requestSpec, 
responseSpec);
-        Integer groupId = groupMembers[0];
-        Assertions.assertNotNull(center);
-        Assertions.assertTrue(center.getStaffId() == staffId);
-        Assertions.assertTrue(center.isActive() == true);
+        Long centerId = FeignGroupCenterHelper.createCenter(name, 
officeId.intValue(), externalId, staffId, new long[] { groupId },
+                centerActivationDate);
+        JsonObject center = FeignGroupCenterHelper.retrieveCenter(centerId);
+        assertNotNull(center);
+        assertEquals(staffId, center.get("staffId").getAsInt());
+        assertTrue(center.get("active").getAsBoolean());
 
         Long calendarId = createCalendarMeeting(centerId);
 
-        Integer clientId = createClient(officeId);
+        Long clientId = createClient(officeId.intValue(), "01 July 2014");
 
-        associateClientsToGroup(groupId, clientId);
+        FeignGroupCenterHelper.associateClientToGroup(groupId, clientId);
 
         DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", 
Locale.US);
         dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
         Calendar today = Calendar.getInstance(Utils.getTimeZoneOfTenant());
         today.add(Calendar.DAY_OF_MONTH, -14);
-        // CREATE A LOAN PRODUCT
         final String disbursalDate = dateFormat.format(today.getTime());
         final String recalculationRestFrequencyDate = "01 January 2012";
         final boolean isMultiTrancheLoan = false;
 
-        List<HashMap> collaterals = new ArrayList<>();
+        Long collateralId = FeignGroupCenterHelper.createCollateralProduct();
+        assertNotNull(collateralId);
+        Long clientCollateralId = 
FeignGroupCenterHelper.createClientCollateral(clientId, collateralId);
+        assertNotNull(clientCollateralId);
 
-        final Integer collateralId = 
CollateralManagementHelper.createCollateralProduct(this.requestSpec, 
this.responseSpec);
-        Assertions.assertNotNull(collateralId);
-        final Integer clientCollateralId = 
CollateralManagementHelper.createClientCollateral(this.requestSpec, 
this.responseSpec,
-                String.valueOf(clientId), collateralId);
-        Assertions.assertNotNull(clientCollateralId);
-        addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
+        List<HashMap> collaterals = new ArrayList<>();
+        collaterals.add(collateral(clientCollateralId.intValue(), 
BigDecimal.valueOf(1)));
 
-        // CREATE LOAN MULTIDISBURSAL PRODUCT WITH INTEREST RECALCULATION
-        final Integer loanProductID = 
createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY,
+        Long loanProductId = 
createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY,
                 LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
                 
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
                 LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, 
"0", recalculationRestFrequencyDate,
-                
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null, 
isMultiTrancheLoan, null, null);
+                
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, 
isMultiTrancheLoan);
 
-        // APPLY FOR TRANCHE LOAN WITH INTEREST RECALCULATION
-        final Integer loanId = 
applyForLoanApplicationForInterestRecalculation(clientId, groupId, calendarId, 
loanProductID, disbursalDate,
-                recalculationRestFrequencyDate, 
LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList<HashMap>(0), null,
-                collaterals);
+        Long loanId = 
applyForLoanApplicationForInterestRecalculation(clientId, groupId, calendarId, 
loanProductId, disbursalDate,
+                recalculationRestFrequencyDate, 
LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, collaterals);
 
-        // Test for loan account is created
-        Assertions.assertNotNull(loanId);
-        HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanId);
+        assertNotNull(loanId);
+        verifyLoanStatus(loanId, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL);
 
-        // Test for loan account is created, can be approved
-        this.loanTransactionHelper.approveLoan(disbursalDate, loanId);
-        loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanId);
-        LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+        approveLoan(loanId, approveLoanRequest(10000.0, disbursalDate));
+        verifyLoanStatus(loanId, LoanStatus.APPROVED);
 
-        // Test for loan account approved can be disbursed
-        String loanDetails = 
this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, 
loanId);
-        
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(disbursalDate, 
loanId,
-                
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
-        loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanId);
-        LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+        GetLoansLoanIdResponse approvedLoan = getLoanDetails(loanId);
+        disburseLoanWithNetDisbursalAmount(loanId, disbursalDate, 
approvedLoan.getNetDisbursalAmount().toPlainString());
+        verifyLoanStatus(loanId, LoanStatus.ACTIVE);
 
         LOG.info("---------------------------------CHANGING GROUP MEETING DATE 
------------------------------------------");
-        Calendar todaysdate = 
Calendar.getInstance(Utils.getTimeZoneOfTenant());
-        todaysdate.add(Calendar.DAY_OF_MONTH, 14);
-        String oldMeetingDate = dateFormat.format(todaysdate.getTime());
-        todaysdate.add(Calendar.DAY_OF_MONTH, 1);
-        final String centerMeetingNewStartDate = 
dateFormat.format(todaysdate.getTime());
-        CalendarHelper.updateMeetingCalendarForCenter(centerId.longValue(), 
calendarId.toString(), oldMeetingDate,
-                centerMeetingNewStartDate);
-
-        ArrayList loanRepaymnetSchedule = 
this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, 
generalResponseSpec, loanId);
-        // VERIFY RESCHEDULED DATE
-        ArrayList dueDateLoanSchedule = (ArrayList) ((HashMap) 
loanRepaymnetSchedule.get(2)).get("dueDate");
-        assertEquals(getDateAsArray(todaysdate, 0), dueDateLoanSchedule);
-
-        // VERIFY THE INTEREST
-        Float interestDue = (Float) ((HashMap) 
loanRepaymnetSchedule.get(2)).get("interestDue");
-        assertEquals("90.82", String.valueOf(interestDue));
-    }
-
-    private void addCollaterals(List<HashMap> collaterals, Integer 
collateralId, BigDecimal amount) {
-        collaterals.add(collaterals(collateralId, amount));
-    }
-
-    private HashMap<String, String> collaterals(Integer collateralId, 
BigDecimal amount) {
-        HashMap<String, String> collateral = new HashMap<String, String>(1);
-        collateral.put("clientCollateralId", collateralId.toString());
-        collateral.put("amount", amount.toString());
-        return collateral;
-    }
-
-    private void associateClientsToGroup(Integer groupId, Integer clientId) {
-        // Associate client to the group
-        GroupHelper.associateClient(this.requestSpec, this.responseSpec, 
groupId.toString(), clientId.toString());
-        GroupHelper.verifyGroupMembers(this.requestSpec, this.responseSpec, 
groupId, clientId);
+        Calendar rescheduledDate = 
Calendar.getInstance(Utils.getTimeZoneOfTenant());
+        rescheduledDate.add(Calendar.DAY_OF_MONTH, 14);
+        String oldMeetingDate = dateFormat.format(rescheduledDate.getTime());
+        rescheduledDate.add(Calendar.DAY_OF_MONTH, 1);
+        final String centerMeetingNewStartDate = 
dateFormat.format(rescheduledDate.getTime());
+        CalendarHelper.updateMeetingCalendarForCenter(centerId, 
calendarId.toString(), oldMeetingDate, centerMeetingNewStartDate);
+
+        GetLoansLoanIdResponse loanDetails = getLoanDetails(loanId);
+        GetLoansLoanIdRepaymentPeriod installment = 
loanDetails.getRepaymentSchedule().getPeriods().get(2);
+        assertEquals(toLocalDate(rescheduledDate), installment.getDueDate());
+        assertEquals(0, new 
BigDecimal("90.82").compareTo(installment.getInterestDue()));
     }
 
-    private Integer createClient(Integer officeId) {
-        // CREATE CLIENT
-        final String clientActivationDate = "01 July 2014";
-        Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, clientActivationDate, officeId.toString());
-        ClientHelper.verifyClientCreatedOnServer(this.requestSpec, 
this.responseSpec, clientId);
-        return clientId;
-    }
-
-    private Long createCalendarMeeting(Integer centerId) {
-        DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", 
Locale.US);
-        dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
-        Calendar today = Calendar.getInstance(Utils.getTimeZoneOfTenant());
-        final String startDate = dateFormat.format(today.getTime());
-        final String frequency = "2"; // 2:Weekly
-        final String interval = "2"; // Every one week
-        Integer repeatsOnDay = today.get(Calendar.DAY_OF_WEEK) - 1;
-
-        if (repeatsOnDay.intValue() == 0) {
-            repeatsOnDay = 7;
-        }
-
-        Long calendarId = CalendarHelper
-                .createMeetingForGroup(centerId.longValue(), startDate, 
frequency, interval, repeatsOnDay.toString()).getResourceId();
-        LOG.info("calendarId {}", calendarId);
-        return calendarId;
-    }
-
-    @SuppressWarnings("rawtypes")
     @Test
     public void 
testCenterReschedulingMultiTrancheLoansWithInterestRecalculationEnabled() {
-
-        Integer officeId = new OfficeHelper().createOffice(LocalDate.of(2007, 
7, 1)).getResourceId().intValue();
+        Long officeId = new 
FeignOfficeHelper(fineractClient()).createOffice(LocalDate.of(2007, Month.JULY, 
1)).getResourceId();

Review Comment:
   Direct new FeignOfficeHelper(fineractClient()) in the test (helpers belong 
in the base per the test-infra conventions)



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanUndoChargeOffReverseExternalIdTest.java:
##########
@@ -47,33 +48,36 @@
 import org.junit.jupiter.api.extension.ExtendWith;
 
 @ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanUndoChargeOffReverseExternalIdTest {
+public class LoanUndoChargeOffReverseExternalIdTest extends FeignLoanTestBase {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private ClientHelper clientHelper;
-    private LoanTransactionHelper loanTransactionHelper;
-    private AccountHelper accountHelper;
-    private Account assetAccount;
-    private Account incomeAccount;
-    private Account expenseAccount;
-    private Account overpaymentAccount;
+    protected ResponseSpecification responseSpec;
+    protected RequestSpecification requestSpec;
 
     @BeforeEach
-    public void setup() {
+    public void setupREST() {
         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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+
         this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
         this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
         this.assetAccount = this.accountHelper.createAssetAccount();
         this.incomeAccount = this.accountHelper.createIncomeAccount();
         this.expenseAccount = this.accountHelper.createExpenseAccount();
         this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
     }
 
+    private ClientHelper clientHelper;

Review Comment:
   unused here, it shadows the parent classes field



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/BlockTransactionsOnClosedOverpaidLoansTest.java:
##########
@@ -43,7 +44,7 @@
 import org.junit.jupiter.api.Test;
 
 @Slf4j
-public class BlockTransactionsOnClosedOverpaidLoansTest {
+public class BlockTransactionsOnClosedOverpaidLoansTest extends 
FeignLoanTestBase {

Review Comment:
   We are extending the base Feign test class which is a good addition. However 
we don't use anything from there.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionSummaryTest.java:
##########
@@ -55,24 +57,25 @@
 
 @Slf4j
 @ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanTransactionSummaryTest {
+public class LoanTransactionSummaryTest extends FeignLoanTestBase {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private ClientHelper clientHelper;
-    private LoanTransactionHelper loanTransactionHelper;
-    private DateTimeFormatter dateFormatter = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
+    protected ResponseSpecification responseSpec;
+    protected RequestSpecification requestSpec;
 
     @BeforeEach
-    public void setup() {
+    public void setupREST() {
         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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
         this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
     }
 
+    private ClientHelper clientHelper;

Review Comment:
   Looks like shadowing here as well



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWithWaiveInterestAndWriteOffIntegrationTest.java:
##########
@@ -48,11 +49,22 @@
  */
 @SuppressWarnings({ "rawtypes" })
 @ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanWithWaiveInterestAndWriteOffIntegrationTest {
+public class LoanWithWaiveInterestAndWriteOffIntegrationTest extends 
FeignLoanTestBase {

Review Comment:
   We are extending the base Feign test class which is a good addition. However 
we don't use anything from there. 



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountRepaymentCalculationTest.java:
##########
@@ -28,45 +28,56 @@
 import io.restassured.specification.ResponseSpecification;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.UUID;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
 import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
 import 
org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
 import org.apache.fineract.integrationtests.common.BusinessDateHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
+import 
org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-public class LoanAccountRepaymentCalculationTest extends 
BaseLoanIntegrationTest {
+public class LoanAccountRepaymentCalculationTest extends FeignLoanTestBase {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
-    private LoanTransactionHelper loanTransactionHelper;
-    private ClientHelper clientHelper;
+    protected RequestSpecification requestSpec;
+    protected ResponseSpecification responseSpec;
+    protected LoanTransactionHelper loanTransactionHelper;
+    protected AccountHelper accountHelper;
+    protected JournalEntryHelper journalEntryHelper;
 
     @BeforeEach
-    public void setup() {
+    @SuppressWarnings("removal")
+    public void setupREST() {
         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.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+        this.accountHelper = new AccountHelper(this.requestSpec, 
this.responseSpec);
+        this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, 
this.responseSpec);
     }
 
+    private ClientHelper clientHelper;

Review Comment:
   declared but never used



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionSummaryTest.java:
##########
@@ -55,24 +57,25 @@
 
 @Slf4j
 @ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanTransactionSummaryTest {
+public class LoanTransactionSummaryTest extends FeignLoanTestBase {

Review Comment:
   We are extending the base Feign test class which is a good addition. However 
we don't use anything from there.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDownPaymentTransactionChargebackTest.java:
##########
@@ -64,20 +60,16 @@ public void loanDownPaymentTransactionChargebackTest() {
                     installment(250.0, false, "15 April 2023")//
             );
 
-            // make down payment
-            final PostLoansLoanIdTransactionsResponse downPaymentTransaction_1 
= loanTransactionHelper.makeLoanDownPayment(loanId,
-                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("01 March 2023").locale("en")
-                            .transactionAmount(250.0));
+            final PostLoansLoanIdTransactionsResponse downPaymentTransaction_1 
= makeLoanDownPayment(loanId,
+                    new 
PostLoansLoanIdTransactionsRequest().dateFormat(LoanTestData.DATETIME_PATTERN).transactionDate("01
 March 2023")
+                            
.locale(LoanTestData.LOCALE).transactionAmount(250.0));
             assertNotNull(downPaymentTransaction_1);
 
-            // chargeback down payment transaction
-            final Long chargebackTransactionId = 
loanTransactionHelper.applyChargebackTransaction(loanId.intValue(),
-                    downPaymentTransaction_1.getResourceId(), "50.00", 0, 
responseSpec);
+            final Long chargebackTransactionId = 
applyChargebackTransaction(loanId, downPaymentTransaction_1.getResourceId(), 
50.0, 1L);

Review Comment:
   the original passed payment-type index 0 (resolved via GET /paymenttypes), 
this hardcodes id 1L; they coincide only if the first listed type has id 1. Use 
the idx-preserving overload applyChargebackTransaction(loanId, txId, "50.00", 
0) already in FeignLoanTestBase.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/reamortization/LoanReAmortizationIntegrationTest.java:
##########
@@ -1120,20 +1115,15 @@ private Long 
createLoanProductWithMultiDisbursalAndRepaymentsWithEnableDownPayme
         product.setEnableAutoRepaymentForDownPayment(true);
         product.setInstallmentAmountInMultiplesOf(null);
 
-        PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
-        GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = 
loanProductHelper
-                .retrieveLoanProductById(loanProductResponse.getResourceId());
-        assertNotNull(getLoanProductsProductIdResponse);
-        return loanProductResponse.getResourceId();
+        Long loanProductId = createLoanProduct(product);
+        GetLoanProductsProductIdResponse retrievedProduct = 
retrieveLoanProduct(loanProductId);
+        assertNotNull(retrievedProduct);
+        return loanProductId;
     }
 
     private Long addChargeWithCurrency(Long loanId, boolean isPenalty, double 
amount, String dueDate, String currencyCode) {

Review Comment:
   ignores isPenalty and always creates a fee. Branch to the penalty builder or 
drop the parameter



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanReschedulingWithinCenterTest.java:
##########
@@ -19,382 +19,279 @@
 package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import com.google.gson.Gson;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.builder.ResponseSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.path.json.JsonPath;
-import io.restassured.specification.RequestSpecification;
-import io.restassured.specification.ResponseSpecification;
+import com.google.gson.JsonObject;
 import java.math.BigDecimal;
 import java.sql.Timestamp;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.Month;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.UUID;
+import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostClientsRequest;
+import org.apache.fineract.client.models.PostLoansDisbursementData;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignGroupCenterHelper;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignOfficeHelper;
+import 
org.apache.fineract.integrationtests.client.feign.modules.LoanRequestBuilders;
+import org.apache.fineract.integrationtests.client.feign.modules.LoanTestData;
 import org.apache.fineract.integrationtests.common.CalendarHelper;
-import org.apache.fineract.integrationtests.common.CenterDomain;
-import org.apache.fineract.integrationtests.common.CenterHelper;
-import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
-import org.apache.fineract.integrationtests.common.GroupHelper;
-import org.apache.fineract.integrationtests.common.OfficeHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import org.apache.fineract.integrationtests.common.accounting.Account;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
-import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class LoanReschedulingWithinCenterTest extends BaseLoanIntegrationTest {
+public class LoanReschedulingWithinCenterTest extends FeignLoanTestBase {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(LoanReschedulingWithinCenterTest.class);
-    private RequestSpecification requestSpec;
-    private ResponseSpecification responseSpec;
-    private LoanTransactionHelper loanTransactionHelper;
-    private ResponseSpecification generalResponseSpec;
-    private LoanApplicationApprovalTest loanApplicationApprovalTest;
-
-    @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.requestSpec.header("Fineract-Platform-TenantId", "default");
-        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.loanApplicationApprovalTest = new LoanApplicationApprovalTest();
-        this.generalResponseSpec = new ResponseSpecBuilder().build();
-
-        globalConfigurationHelper.verifyAllDefaultGlobalConfigurations();
-    }
+    private static final Long LEGAL_FORM_PERSON = 1L;
 
-    @SuppressWarnings("rawtypes")
     @Test
     public void testCenterReschedulingLoansWithInterestRecalculationEnabled() {
-
-        Integer officeId = new OfficeHelper().createOffice(LocalDate.of(2007, 
7, 1)).getResourceId().intValue();
+        Long officeId = new 
FeignOfficeHelper(fineractClient()).createOffice(LocalDate.of(2007, Month.JULY, 
1)).getResourceId();

Review Comment:
   Direct new FeignOfficeHelper(fineractClient()) in the test (helpers belong 
in the base per the test-infra conventions)



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackOnPaymentTypeRepaymentTransactionsTest.java:
##########
@@ -22,315 +22,198 @@
 import static 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-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.Arrays;
-import java.util.HashMap;
-import java.util.List;
 import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Stream;
-import org.apache.fineract.client.models.AdvancedPaymentData;
-import org.apache.fineract.client.models.DelinquencyBucketResponse;
-import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.feign.util.CallFailedRuntimeException;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
-import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
-import org.apache.fineract.client.models.PaymentAllocationOrder;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import org.apache.fineract.client.models.PostLoansRequest;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import 
org.apache.fineract.integrationtests.client.feign.modules.LoanRequestBuilders;
+import org.apache.fineract.integrationtests.client.feign.modules.LoanTestData;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
-import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import 
org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import 
org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
-import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Named;
-import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
-@ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanChargebackOnPaymentTypeRepaymentTransactionsTest {
-
-    private ResponseSpecification responseSpec;
-    private ResponseSpecification responseSpecErr503;
-    private RequestSpecification requestSpec;
-    private ClientHelper clientHelper;
-    private LoanTransactionHelper loanTransactionHelper;
-
-    @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.responseSpecErr503 = new 
ResponseSpecBuilder().expectStatusCode(503).build();
-        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
-        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
-    }
+public class LoanChargebackOnPaymentTypeRepaymentTransactionsTest extends 
FeignLoanTestBase {
 
     @ParameterizedTest
     @MethodSource("loanProductFactory")
-    public void 
loanTransactionChargebackForPaymentTypeRepaymentTransactionTest(LoanProductTestBuilder
 loanProductTestBuilder) {
-        // Loan ExternalId
+    public void 
loanTransactionChargebackForPaymentTypeRepaymentTransactionTest(String 
strategyCode, boolean advancedAllocation) {
         String loanExternalIdStr = UUID.randomUUID().toString();
 
-        // Delinquency Bucket
         final Long delinquencyBucketId = 
DelinquencyBucketsHelper.createDefaultBucket();
-        final DelinquencyBucketResponse delinquencyBucket = 
DelinquencyBucketsHelper.getBucket(delinquencyBucketId);
-
-        // Client and Loan account creation
 
-        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper,
-                delinquencyBucketId, loanProductTestBuilder);
-        assertNotNull(getLoanProductsProductResponse);
+        final Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+        final Long loanProductId = createLoanProduct(strategyCode, 
advancedAllocation, delinquencyBucketId);
 
-        final Integer loanId = createLoanAccount(clientId, 
getLoanProductsProductResponse.getId(), loanExternalIdStr,
-                loanProductTestBuilder.getTransactionProcessingStrategyCode());
+        final Long loanId = createLoanAccount(clientId, loanProductId, 
loanExternalIdStr, strategyCode);
 
-        // make Repayment
-        final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
-                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("5 September 2022").locale("en")
-                        .transactionAmount(500.0));
+        final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = 
makeLoanRepayment(loanExternalIdStr,
+                new 
PostLoansLoanIdTransactionsRequest().dateFormat(LoanTestData.DATETIME_PATTERN).transactionDate("5
 September 2022")
+                        .locale(LoanTestData.LOCALE).transactionAmount(500.0));
 
-        // verify transaction relation and outstanding balance
         reviewLoanTransactionRelations(loanId, 
repaymentTransaction_1.getResourceId(), 0, Double.valueOf("500.00"));
 
-        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails((long) loanId);
+        GetLoansLoanIdResponse loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(500.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
 
-        // chargeback on Repayment
-        PostLoansLoanIdTransactionsResponse chargebackTransactionResponse = 
loanTransactionHelper.chargebackLoanTransaction(
-                loanExternalIdStr, repaymentTransaction_1.getResourceId(),
-                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(500.0).paymentTypeId(1L));
+        PostLoansLoanIdTransactionsResponse chargebackTransactionResponse = 
chargebackLoanTransaction(loanExternalIdStr,
+                repaymentTransaction_1.getResourceId(), new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale(LoanTestData.LOCALE)
+                        .transactionAmount(500.0).paymentTypeId(1L));
 
-        // verify transaction relation and outstanding balance
         assertNotNull(chargebackTransactionResponse);
         reviewLoanTransactionRelations(loanId, 
repaymentTransaction_1.getResourceId(), 1, Double.valueOf("500.00"));
         reviewLoanTransactionRelations(loanId, 
chargebackTransactionResponse.getResourceId(), 0, Double.valueOf("1000.00"));
-        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(1000.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
 
-        // Goodwill Credit
-        final PostLoansLoanIdTransactionsResponse goodwillCredit_1 = 
loanTransactionHelper.makeGoodwillCredit((long) loanId,
-                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("6 September 2022").locale("en")
-                        .transactionAmount(200.0));
+        final PostLoansLoanIdTransactionsResponse goodwillCredit_1 = 
makeGoodwillCredit(loanId,
+                new 
PostLoansLoanIdTransactionsRequest().dateFormat(LoanTestData.DATETIME_PATTERN).transactionDate("6
 September 2022")
+                        .locale(LoanTestData.LOCALE).transactionAmount(200.0));
 
-        // verify transaction relation and outstanding balance
         reviewLoanTransactionRelations(loanId, 
goodwillCredit_1.getResourceId(), 0, Double.valueOf("300.00"));
 
-        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(800.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
 
-        // chargeback on Goodwill Credit Transaction
-        chargebackTransactionResponse = 
loanTransactionHelper.chargebackLoanTransaction(loanExternalIdStr, 
goodwillCredit_1.getResourceId(),
-                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(200.0).paymentTypeId(1L));
+        chargebackTransactionResponse = 
chargebackLoanTransaction(loanExternalIdStr, goodwillCredit_1.getResourceId(),
+                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale(LoanTestData.LOCALE).transactionAmount(200.0)
+                        .paymentTypeId(1L));
 
-        // verify transaction relation and outstanding balance
         assertNotNull(chargebackTransactionResponse);
         reviewLoanTransactionRelations(loanId, 
goodwillCredit_1.getResourceId(), 1, Double.valueOf("300.00"));
         reviewLoanTransactionRelations(loanId, 
chargebackTransactionResponse.getResourceId(), 0, Double.valueOf("1000.00"));
 
-        // Payout Refund
-
-        final PostLoansLoanIdTransactionsResponse payoutRefund_1 = 
loanTransactionHelper.makePayoutRefund((long) loanId,
-                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("7 September 2022").locale("en")
-                        .transactionAmount(300.0));
+        final PostLoansLoanIdTransactionsResponse payoutRefund_1 = 
makePayoutRefund(loanId,
+                new 
PostLoansLoanIdTransactionsRequest().dateFormat(LoanTestData.DATETIME_PATTERN).transactionDate("7
 September 2022")
+                        .locale(LoanTestData.LOCALE).transactionAmount(300.0));
 
-        // verify transaction relation and outstanding balance
         reviewLoanTransactionRelations(loanId, payoutRefund_1.getResourceId(), 
0, Double.valueOf("0.00"));
 
-        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(700.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
 
-        // chargeback on Payout Refund Transaction
-        chargebackTransactionResponse = 
loanTransactionHelper.chargebackLoanTransaction(loanExternalIdStr, 
payoutRefund_1.getResourceId(),
-                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(300.0).paymentTypeId(1L));
+        chargebackTransactionResponse = 
chargebackLoanTransaction(loanExternalIdStr, payoutRefund_1.getResourceId(),
+                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale(LoanTestData.LOCALE).transactionAmount(300.0)
+                        .paymentTypeId(1L));
 
-        // verify transaction relation and outstanding balance
         assertNotNull(chargebackTransactionResponse);
         reviewLoanTransactionRelations(loanId, payoutRefund_1.getResourceId(), 
1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, 
chargebackTransactionResponse.getResourceId(), 0, Double.valueOf("1000.00"));
 
-        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(1000.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
 
-        // Merchant Issued Refund
+        final PostLoansLoanIdTransactionsResponse merchantIssuedRefund_1 = 
makeMerchantIssuedRefund(loanId,
+                new 
PostLoansLoanIdTransactionsRequest().dateFormat(LoanTestData.DATETIME_PATTERN).transactionDate("8
 September 2022")
+                        .locale(LoanTestData.LOCALE).transactionAmount(100.0));
 
-        final PostLoansLoanIdTransactionsResponse merchantIssuedRefund_1 = 
loanTransactionHelper.makeMerchantIssuedRefund((long) loanId,
-                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("8 September 2022").locale("en")
-                        .transactionAmount(100.0));
-
-        // verify transaction relation and outstanding balance
         reviewLoanTransactionRelations(loanId, 
merchantIssuedRefund_1.getResourceId(), 0, Double.valueOf("0.00"));
 
-        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(900.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
 
-        // chargeback on Merchant Issued Refund Transaction
-        chargebackTransactionResponse = 
loanTransactionHelper.chargebackLoanTransaction(loanExternalIdStr,
-                merchantIssuedRefund_1.getResourceId(),
-                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(100.0));
+        chargebackTransactionResponse = 
chargebackLoanTransaction(loanExternalIdStr, 
merchantIssuedRefund_1.getResourceId(),
+                new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale(LoanTestData.LOCALE).transactionAmount(100.0));
 
-        // verify transaction relation and outstanding balance
         assertNotNull(chargebackTransactionResponse);
         reviewLoanTransactionRelations(loanId, 
merchantIssuedRefund_1.getResourceId(), 1, Double.valueOf("0.00"));
         reviewLoanTransactionRelations(loanId, 
chargebackTransactionResponse.getResourceId(), 0, Double.valueOf("1000.00"));
 
-        loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
+        loanDetails = getLoanDetails(loanId);
         assertNotNull(loanDetails);
         assertTrue(loanDetails.getStatus().getActive());
         assertNotNull(loanDetails.getSummary());
         assertEquals(1000.0, 
Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding()));
-
     }
 
     @ParameterizedTest
     @MethodSource("loanProductFactory")
-    public void 
loanChargebackNotAllowedForReversedPaymentTypeRepaymentTest(LoanProductTestBuilder
 loanProductTestBuilder) {
-        // Loan ExternalId
+    public void 
loanChargebackNotAllowedForReversedPaymentTypeRepaymentTest(String 
strategyCode, boolean advancedAllocation) {
         String loanExternalIdStr = UUID.randomUUID().toString();
 
-        // Delinquency Bucket
         final Long delinquencyBucketId = 
DelinquencyBucketsHelper.createDefaultBucket();
-        final DelinquencyBucketResponse delinquencyBucket = 
DelinquencyBucketsHelper.getBucket(delinquencyBucketId);
-
-        // Client and Loan account creation
 
-        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper,
-                delinquencyBucketId, loanProductTestBuilder);
-        assertNotNull(getLoanProductsProductResponse);
+        final Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+        final Long loanProductId = createLoanProduct(strategyCode, 
advancedAllocation, delinquencyBucketId);
 
-        final Integer loanId = createLoanAccount(clientId, 
getLoanProductsProductResponse.getId(), loanExternalIdStr,
-                loanProductTestBuilder.getTransactionProcessingStrategyCode());
+        final Long loanId = createLoanAccount(clientId, loanProductId, 
loanExternalIdStr, strategyCode);
 
-        // Merchant Refund
-        final PostLoansLoanIdTransactionsResponse merchantIssuedRefund_2 = 
loanTransactionHelper.makeMerchantIssuedRefund((long) loanId,
-                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("8 September 2022").locale("en")
-                        .transactionAmount(50.0));
+        final PostLoansLoanIdTransactionsResponse merchantIssuedRefund_2 = 
makeMerchantIssuedRefund(loanId,
+                new 
PostLoansLoanIdTransactionsRequest().dateFormat(LoanTestData.DATETIME_PATTERN).transactionDate("8
 September 2022")
+                        .locale(LoanTestData.LOCALE).transactionAmount(50.0));
 
-        // reverse Merchant Refund
-        loanTransactionHelper.reverseRepayment(loanId, 
merchantIssuedRefund_2.getResourceId().intValue(), "8 September 2022");
-
-        // apply Chargeback should give 503 error
-        final Long chargebackTransactionId = 
loanTransactionHelper.applyChargebackTransaction(loanId,
-                merchantIssuedRefund_2.getResourceId(), "50.00", 1, 
responseSpecErr503);
+        reverseRepayment(loanId, merchantIssuedRefund_2.getResourceId(), "8 
September 2022");
 
+        CallFailedRuntimeException exception = 
assertThrows(CallFailedRuntimeException.class,
+                () -> applyChargebackTransaction(loanId, 
merchantIssuedRefund_2.getResourceId(), 50.0, 1L));

Review Comment:
   Same idx/id conflation, but here the original used the second payment type 
(idx 1) and this hardcodes id 1L (effectively the first). The 503 assertion 
still passes, but the payload is no longer equivalent to the original scenario.



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargePaymentWithAdvancedPaymentAllocationTest.java:
##########
@@ -20,195 +20,113 @@
 
 import static 
org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity.LIABILITY_TRANSFER;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 
-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.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.Month;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.feign.FineractFeignClient;
 import org.apache.fineract.client.models.AdvancedPaymentData;
-import org.apache.fineract.client.models.BusinessDateUpdateRequest;
+import 
org.apache.fineract.client.models.DeleteFinancialActivityAccountsResponse;
+import org.apache.fineract.client.models.GetFinancialActivityAccountsResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
-import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PostFinancialActivityAccountsRequest;
+import org.apache.fineract.client.models.PostFinancialActivityAccountsResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdRequest;
-import org.apache.fineract.client.models.PostLoansRequest;
-import org.apache.fineract.client.models.PostLoansResponse;
-import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
-import 
org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
-import org.apache.fineract.integrationtests.common.BusinessDateHelper;
-import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
-import org.apache.fineract.integrationtests.common.CommonConstants;
-import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
+import org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignSavingsHelper;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignSavingsProductHelper;
+import 
org.apache.fineract.integrationtests.client.feign.helpers.FeignSavingsTransactionHelper;
+import 
org.apache.fineract.integrationtests.client.feign.modules.ChargeRequestBuilders;
+import 
org.apache.fineract.integrationtests.client.feign.modules.SavingsRequestBuilders;
+import org.apache.fineract.integrationtests.common.FineractFeignClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.accounting.Account;
-import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
 import 
org.apache.fineract.integrationtests.common.accounting.FinancialActivityAccountHelper;
-import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
 import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
 import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
-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.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
-import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 @Slf4j
-public class LoanChargePaymentWithAdvancedPaymentAllocationTest extends 
BaseLoanIntegrationTest {
+public class LoanChargePaymentWithAdvancedPaymentAllocationTest extends 
FeignLoanTestBase {
 
     private static final String DATETIME_PATTERN = "dd MMMM yyyy";
-    private static final String ACCOUNT_TYPE_INDIVIDUAL = "INDIVIDUAL";
     private static final DateTimeFormatter DATE_FORMATTER = new 
DateTimeFormatterBuilder().appendPattern(DATETIME_PATTERN).toFormatter();
-    private static RequestSpecification requestSpec;
-    private static ResponseSpecification responseSpec;
-    private static LoanTransactionHelper loanTransactionHelper;
-    private static AccountHelper accountHelper;
-    private static Integer commonLoanProductId;
-    private static PostClientsResponse client;
-    private static BusinessDateHelper businessDateHelper;
-    private static SchedulerJobHelper scheduleJobHelper;
-    private static SavingsAccountHelper savingsAccountHelper;
-    private static SavingsProductHelper savingsProductHelper;
+
+    private static FeignSavingsHelper savingsHelper;
+    private static FeignSavingsProductHelper savingsProductHelper;
+    private static FeignSavingsTransactionHelper savingsTransactionHelper;
     private static FinancialActivityAccountHelper 
financialActivityAccountHelper;
-    private static Integer financialActivityAccountId;
-    private static Account liabilityTransferAccount;
 
     @BeforeAll
-    public static void setup() {
-        Utils.initializeRESTAssured();
-        ClientHelper clientHelper = new ClientHelper(requestSpec, 
responseSpec);
-        requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
-        requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
-        requestSpec.header("Fineract-Platform-TenantId", "default");
-        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
-
-        loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
-        accountHelper = new AccountHelper(requestSpec, responseSpec);
-        final Account assetAccount = accountHelper.createAssetAccount();
-        final Account incomeAccount = accountHelper.createIncomeAccount();
-        final Account expenseAccount = accountHelper.createExpenseAccount();
-        final Account overpaymentAccount = 
accountHelper.createLiabilityAccount();
-        client = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
-        businessDateHelper = new BusinessDateHelper();
-        scheduleJobHelper = new SchedulerJobHelper(requestSpec);
-        savingsAccountHelper = new SavingsAccountHelper(requestSpec, 
responseSpec);
-        savingsProductHelper = new SavingsProductHelper();
-        commonLoanProductId = createLoanProduct("500", "15", "4", 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
-        financialActivityAccountHelper = new 
FinancialActivityAccountHelper(requestSpec);
-
-        List<HashMap> financialActivities = 
financialActivityAccountHelper.getAllFinancialActivityAccounts(responseSpec);
-        if (financialActivities.isEmpty()) {
-            /** Setup liability transfer account **/
-            /** Create a Liability and an Asset Transfer Account **/
-            liabilityTransferAccount = accountHelper.createLiabilityAccount();
-            Assertions.assertNotNull(liabilityTransferAccount);
-
-            /*** Create A Financial Activity to Account Mapping **/
-            financialActivityAccountId = (Integer) 
financialActivityAccountHelper.createFinancialActivityAccount(
-                    LIABILITY_TRANSFER.getValue(), 
liabilityTransferAccount.getAccountID(), responseSpec,
-                    CommonConstants.RESPONSE_RESOURCE_ID);
-            Assertions.assertNotNull(financialActivityAccountId);
-        } else {
-            boolean existFinancialActivity = false;
-            for (HashMap financialActivity : financialActivities) {
-                HashMap financialActivityData = (HashMap) 
financialActivity.get("financialActivityData");
-                if 
(financialActivityData.get("id").equals(FinancialActivityAccountsTest.LIABILITY_TRANSFER_FINANCIAL_ACTIVITY_ID))
 {
-                    HashMap glAccountData = (HashMap) 
financialActivity.get("glAccountData");
-                    liabilityTransferAccount = new Account((Integer) 
glAccountData.get("id"), Account.AccountType.LIABILITY);
-                    financialActivityAccountId = (Integer) 
financialActivity.get("id");
-                    existFinancialActivity = true;
-                    break;
-                }
-            }
-            if (!existFinancialActivity) {
-                liabilityTransferAccount = 
accountHelper.createLiabilityAccount();
-                Assertions.assertNotNull(liabilityTransferAccount);
-
-                /*** Create A Financial Activity to Account Mapping **/
-                financialActivityAccountId = (Integer) 
financialActivityAccountHelper.createFinancialActivityAccount(
-                        LIABILITY_TRANSFER.getValue(), 
liabilityTransferAccount.getAccountID(), responseSpec,
-                        CommonConstants.RESPONSE_RESOURCE_ID);
-                Assertions.assertNotNull(financialActivityAccountId);
-            }
-        }
+    public static void setupSavingsAndFinancialActivityHelpers() {
+        FineractFeignClient client = 
FineractFeignClientHelper.getFineractFeignClient();
+        savingsHelper = new FeignSavingsHelper(client);
+        savingsProductHelper = new FeignSavingsProductHelper(client);
+        savingsTransactionHelper = new FeignSavingsTransactionHelper(client);
+        financialActivityAccountHelper = new 
FinancialActivityAccountHelper(null);
     }
 
-    @AfterAll
-    public static void tearDown() {
-        Integer deletedFinancialActivityAccountId = 
financialActivityAccountHelper
-                .deleteFinancialActivityAccount(financialActivityAccountId, 
responseSpec, CommonConstants.RESPONSE_RESOURCE_ID);
-        Assertions.assertNotNull(deletedFinancialActivityAccountId);
-        Assertions.assertEquals(financialActivityAccountId, 
deletedFinancialActivityAccountId);
+    @AfterEach
+    public void tearDownFinancialActivityAccounts() {

Review Comment:
   AfterEach now deletes all financial-activity mappings after every test 
(original: one mapping once in AfterAll) and 
mapLiabilityTransferFinancialActivity(Long) ignores its parameter; new 
FinancialActivityAccountHelper(null) is fragile. Also the savings product lost 
withMinimumOpenningBalance("10000.0").



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java:
##########
@@ -367,26 +447,296 @@ public PutLoansAvailableDisbursementAmountResponse 
modifyAvailableDisbursementAm
         return ok(() -> 
fineractClient.loans().modifyLoanAvailableDisbursementAmount(loanId, request));
     }
 
-    public Long createRescheduleRequest(PostCreateRescheduleLoansRequest 
request) {
-        PostCreateRescheduleLoansResponse response = ok(() -> 
fineractClient.rescheduleLoans().createLoanRescheduleRequest(request));
-        return response.getResourceId();
+    public PostCreateRescheduleLoansResponse 
createRescheduleRequest(PostCreateRescheduleLoansRequest request) {
+        if (request instanceof 
LoanRequestBuilders.RescheduleRequestWithRecalculateInterest recalcRequest
+                && 
Boolean.TRUE.equals(recalcRequest.getRecalculateInterest())) {
+            return new 
PostCreateRescheduleLoansResponse().resourceId(createRescheduleRequestFromJson(toRescheduleJson(request,
 true)));
+        }
+        return ok(() -> 
fineractClient.rescheduleLoans().createLoanRescheduleRequest(request));
+    }
+
+    public CallFailedRuntimeException 
createRescheduleRequestExpectingError(PostCreateRescheduleLoansRequest request) 
{

Review Comment:
   createRescheduleRequestExpectingError is dead code with a latent bug: the 
recalc path throws AssertionError (RestAssured expectStatusCode(200)), which 
FeignCalls.fail() doesn't catch — it can never return the expected exception. 
Remove or implement via Feign



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to