This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 15c968bc1b FINERACT-2181: Fix fully paid check on EmiModel
15c968bc1b is described below

commit 15c968bc1bf35501d1f3ce13721c3d689c35f5c7
Author: Adam Saghy <[email protected]>
AuthorDate: Wed May 21 10:43:18 2025 +0200

    FINERACT-2181: Fix fully paid check on EmiModel
---
 .../api/LoanTransactionsApiResourceSwagger.java    |   2 +
 .../domain/LoanRepaymentScheduleInstallment.java   |   7 +-
 .../loanproduct/calc/data/RepaymentPeriod.java     |   2 +-
 .../api/InternalLoanInformationApiResource.java    |  43 +---
 .../adjustment/LoanAdjustmentServiceImpl.java      |   2 +-
 .../integrationtests/BaseLoanIntegrationTest.java  |   4 +-
 .../integrationtests/LoanInterestPauseApiTest.java |  22 +-
 .../LoanInterestRecalculationCOBTest.java          |  12 +-
 .../LoanRescheduleRequestTest.java                 |   2 +-
 .../integrationtests/common/BatchHelper.java       |   2 +-
 .../common/BusinessDateHelper.java                 |  20 +-
 .../common/loans/LoanProductTestBuilder.java       |   4 +-
 .../common/loans/LoanTestLifecycleExtension.java   |  87 ++++---
 .../common/loans/LoanTransactionHelper.java        |   4 +
 .../common/organisation/CampaignsTest.java         | 266 +++++++++++----------
 .../src/test/resources/jsv-messages.properties     |  92 +++++++
 16 files changed, 349 insertions(+), 222 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
index 1ee205ae80..9014fd8c92 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResourceSwagger.java
@@ -108,6 +108,8 @@ final class LoanTransactionsApiResourceSwagger {
         public List<GetLoanProductsChargeOffReasonOptions> 
chargeOffReasonOptions;
 
         public List<GetPaymentTypeOptions> paymentTypeOptions;
+        @Schema(example = "200.000000")
+        public Double netDisbursalAmount;
     }
 
     public static final class GetLoanCurrency {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index 86b828bc11..5789c37dac 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -30,6 +30,7 @@ import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import lombok.Getter;
 import lombok.Setter;
@@ -1025,7 +1026,7 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
     }
 
     public void copyFrom(final LoanRepaymentScheduleInstallment installment) {
-        if (getId().equals(installment.getId())) {
+        if (nonNullAndEqual(getId(), installment.getId())) {
             return;
         }
         // Reset balances
@@ -1149,4 +1150,8 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
         }
         
setLoanTransactionToRepaymentScheduleMappings(retainedTransactionRepaymentScheduleMapping);
     }
+
+    private static boolean nonNullAndEqual(Object a, Object b) {
+        return a != null && b != null && Objects.equals(a, b);
+    }
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
index a921139389..e3faf47910 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
@@ -253,7 +253,7 @@ public final class RepaymentPeriod {
     }
 
     public boolean isFullyPaid() {
-        return getEmi().isEqualTo(getTotalPaidAmount());
+        return getEmiPlusChargeback().isEqualTo(getTotalPaidAmount());
     }
 
     /**
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/InternalLoanInformationApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/InternalLoanInformationApiResource.java
index ee37e4972a..67f399160f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/InternalLoanInformationApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/InternalLoanInformationApiResource.java
@@ -18,12 +18,6 @@
  */
 package org.apache.fineract.portfolio.loanaccount.api;
 
-import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_BY;
-import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE;
-import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY;
-import static 
org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE;
-
-import com.google.gson.Gson;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.GET;
@@ -33,17 +27,13 @@ import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.UriInfo;
-import java.util.HashMap;
+import java.time.OffsetDateTime;
 import java.util.List;
-import java.util.Map;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
 import org.apache.fineract.infrastructure.core.boot.FineractProfiles;
-import 
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
-import 
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import 
org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
@@ -62,12 +52,8 @@ public class InternalLoanInformationApiResource implements 
InitializingBean {
 
     private final LoanRepositoryWrapper loanRepositoryWrapper;
     private final LoanTransactionRepository loanTransactionRepository;
-    private final ToApiJsonSerializer<Map> toApiJsonSerializerForMap;
-    private final ToApiJsonSerializer<List> toApiJsonSerializerForList;
     private final ApiRequestParameterHelper apiRequestParameterHelper;
     private final AdvancedPaymentDataMapper advancedPaymentDataMapper;
-    private final LoanAccountDomainService loanAccountDomainService;
-    private final Gson gson = new Gson();
 
     @Override
     @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
@@ -87,7 +73,7 @@ public class InternalLoanInformationApiResource implements 
InitializingBean {
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
-    public String getLoanAuditFields(@Context final UriInfo uriInfo, 
@PathParam("loanId") Long loanId) {
+    public AuditData getLoanAuditFields(@Context final UriInfo uriInfo, 
@PathParam("loanId") Long loanId) {
         
log.warn("------------------------------------------------------------");
         log.warn("                                                            
");
         log.warn("Fetching loan with {}", loanId);
@@ -95,11 +81,8 @@ public class InternalLoanInformationApiResource implements 
InitializingBean {
         
log.warn("------------------------------------------------------------");
 
         final Loan loan = 
loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
-        Map<String, Object> auditFields = new HashMap<>(
-                Map.of(CREATED_BY, loan.getCreatedBy().orElse(null), 
CREATED_DATE, loan.getCreatedDate().orElse(null), LAST_MODIFIED_BY,
-                        loan.getLastModifiedBy().orElse(null), 
LAST_MODIFIED_DATE, loan.getLastModifiedDate().orElse(null)));
-        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
-        return this.toApiJsonSerializerForMap.serialize(settings, auditFields);
+        return new AuditData(loan.getCreatedBy().orElse(null), 
loan.getCreatedDate().orElse(null), loan.getLastModifiedBy().orElse(null),
+                loan.getLastModifiedDate().orElse(null));
     }
 
     @GET
@@ -107,7 +90,7 @@ public class InternalLoanInformationApiResource implements 
InitializingBean {
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
-    public String getLoanTransactionAuditFields(@Context final UriInfo 
uriInfo, @PathParam("loanId") Long loanId,
+    public AuditData getLoanTransactionAuditFields(@Context final UriInfo 
uriInfo, @PathParam("loanId") Long loanId,
             @PathParam("transactionId") Long transactionId) {
         
log.warn("------------------------------------------------------------");
         log.warn("                                                            
");
@@ -116,11 +99,8 @@ public class InternalLoanInformationApiResource implements 
InitializingBean {
         
log.warn("------------------------------------------------------------");
 
         final LoanTransaction transaction = 
loanTransactionRepository.findById(transactionId).orElseThrow();
-        Map<String, Object> auditFields = new HashMap<>(Map.of(CREATED_BY, 
transaction.getCreatedBy().orElse(null), CREATED_DATE,
-                transaction.getCreatedDate().orElse(null), LAST_MODIFIED_BY, 
transaction.getLastModifiedBy().orElse(null),
-                LAST_MODIFIED_DATE, 
transaction.getLastModifiedDate().orElse(null)));
-        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
-        return this.toApiJsonSerializerForMap.serialize(settings, auditFields);
+        return new AuditData(transaction.getCreatedBy().orElse(null), 
transaction.getCreatedDate().orElse(null),
+                transaction.getLastModifiedBy().orElse(null), 
transaction.getLastModifiedDate().orElse(null));
     }
 
     @GET
@@ -128,16 +108,14 @@ public class InternalLoanInformationApiResource 
implements InitializingBean {
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @SuppressFBWarnings("SLF4J_SIGN_ONLY_FORMAT")
-    public String getLoansByStatus(@Context final UriInfo uriInfo, 
@PathParam("statusId") Integer statusId) {
+    public List<Long> getLoansByStatus(@Context final UriInfo uriInfo, 
@PathParam("statusId") Integer statusId) {
         
log.warn("------------------------------------------------------------");
         log.warn("                                                            
");
         log.warn("Fetching loans by status {}", statusId);
         log.warn("                                                            
");
         
log.warn("------------------------------------------------------------");
 
-        final List<Long> loanIds = 
loanRepositoryWrapper.findLoanIdsByStatusId(statusId);
-        final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
-        return this.toApiJsonSerializerForList.serialize(settings, loanIds);
+        return loanRepositoryWrapper.findLoanIdsByStatusId(statusId);
     }
 
     @GET
@@ -156,4 +134,7 @@ public class InternalLoanInformationApiResource implements 
InitializingBean {
         final Loan loan = 
loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
         return 
advancedPaymentDataMapper.mapLoanPaymentAllocationRule(loan.getPaymentAllocationRules());
     }
+
+    private record AuditData(Long createdBy, OffsetDateTime createdDate, Long 
lastModifiedBy, OffsetDateTime lastModifiedDate) {
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java
index 339d4f3580..1992eef926 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java
@@ -279,7 +279,7 @@ public class LoanAdjustmentServiceImpl implements 
LoanAdjustmentService {
             writeOffTransaction.reverse();
         }
 
-        if (newTransactionDetail.isRepaymentLikeType() || 
newTransactionDetail.isInterestWaiver()) {
+        if (newTransactionDetail.isRepaymentLikeType() || 
newTransactionDetail.isWaiver()) {
             
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan,
 newTransactionDetail, transactionForAdjustment,
                     scheduleGeneratorDTO);
         }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index c696a1fbbc..40030968b4 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -345,8 +345,8 @@ public abstract class BaseLoanIntegrationTest extends 
IntegrationTest {
                 .isInterestRecalculationEnabled(true)//
                 .interestRecalculationCompoundingMethod(0)//
                 
.rescheduleStrategyMethod(RescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD)//
-                
.recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD)//
-                .recalculationRestFrequencyInterval(0)//
+                
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)//
+                .recalculationRestFrequencyInterval(1)//
                 .isArrearsBasedOnOriginalSchedule(false)//
                 .isCompoundingToBePostedAsTransaction(false)//
                 .preClosureInterestCalculationStrategy(1)//
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
index e0cd6de3d5..e2a6b23709 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestPauseApiTest.java
@@ -301,7 +301,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
     @Test
     public void testUpdateInterestPauseByLoanId_overlapping_shouldFail3() {
-        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByLoanId("2023-01-02", "2023-01-06",
+        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByLoanId("2023-01-03", "2023-01-06",
                 "yyyy-MM-dd", "en", loanId);
         PostLoansLoanIdTransactionsResponse createResponse2 = 
LOAN_TRANSACTION_HELPER.createInterestPauseByLoanId("2023-01-07",
                 "2023-01-12", "yyyy-MM-dd", "en", loanId);
@@ -495,7 +495,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
     @Test
     public void testUpdateInterestPauseByExternalId_overlapping_shouldFail3() {
-        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-02",
+        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-03",
                 "2023-01-06", "yyyy-MM-dd", "en", externalId);
         PostLoansLoanIdTransactionsResponse createResponse2 = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-07",
                 "2023-01-12", "yyyy-MM-dd", "en", externalId);
@@ -517,7 +517,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
     @Test
     public void 
testUpdateInterestPauseByExternalId_endDateBeforeStartDate_shouldFail() {
-        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-01",
+        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-03",
                 "2023-01-12", "yyyy-MM-dd", "en", externalId);
 
         Assertions.assertNotNull(createResponse);
@@ -538,7 +538,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
     @Test
     public void 
testUpdateInterestPauseByExternalId_startDateBeforeLoanStart_shouldFail() {
-        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-01",
+        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-03",
                 "2023-01-12", "yyyy-MM-dd", "en", externalId);
 
         Assertions.assertNotNull(createResponse);
@@ -559,7 +559,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
     @Test
     public void 
testDeleteInterestPauseByExternalId_validRequest_shouldSucceed() {
-        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-01",
+        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-03",
                 "2023-01-12", "yyyy-MM-dd", "en", externalId);
 
         Assertions.assertNotNull(createResponse, "Create response should not 
be null");
@@ -591,7 +591,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
 
     @Test
     public void 
testDeleteInterestPauseByExternalId_invalidExternalId_shouldFail() {
-        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-01",
+        PostLoansLoanIdTransactionsResponse createResponse = 
LOAN_TRANSACTION_HELPER.createInterestPauseByExternalId("2023-01-03",
                 "2023-01-12", "yyyy-MM-dd", "en", externalId);
 
         Assertions.assertNotNull(createResponse);
@@ -698,7 +698,7 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
         AdvancedPaymentData defaultAllocation = 
createDefaultPaymentAllocation(futureInstallmentAllocationRule);
         String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal(loanPrincipalAmount).withNumberOfRepayments(numberOfRepayments)
                 
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod(interestRatePerPeriod)
-                
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
+                
.withInterestRateFrequencyTypeAsYear().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
                 .withAccountingRulePeriodicAccrual(new Account[] { 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
                 
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).addAdvancedPaymentAllocation(defaultAllocation)
                 
.withLoanScheduleType(LoanScheduleType.PROGRESSIVE).withLoanScheduleProcessingType(LoanScheduleProcessingType.HORIZONTAL)
@@ -719,10 +719,10 @@ public class LoanInterestPauseApiTest extends 
BaseLoanIntegrationTest {
         String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(loanPrincipalAmount)
                 
.withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsDays().withNumberOfRepayments(numberOfRepayments)
                 
.withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsDays().withInterestRatePerPeriod(interestRatePerPeriod)
-                
.withInterestTypeAsFlatBalance().withAmortizationTypeAsEqualPrincipalPayments()
-                
.withInterestCalculationPeriodTypeSameAsRepaymentPeriod().withExpectedDisbursementDate(dateString)
-                
.withSubmittedOnDate(dateString).withLoanType("individual").withExternalId(externalId)
-                
.withRepaymentStrategy("advanced-payment-allocation-strategy").build(clientId.toString(),
 loanProductId.toString(), null);
+                
.withInterestTypeAsDecliningBalance().withAmortizationTypeAsEqualInstallments().withInterestCalculationPeriodTypeAsDays()
+                
.withExpectedDisbursementDate(dateString).withSubmittedOnDate(dateString).withLoanType("individual")
+                
.withExternalId(externalId).withRepaymentStrategy("advanced-payment-allocation-strategy")
+                .build(clientId.toString(), loanProductId.toString(), null);
 
         loanId = LOAN_TRANSACTION_HELPER.getLoanId(loanApplicationJSON);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
index 343e5063b3..83473953ec 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
@@ -455,7 +455,8 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
     public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStep()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(
+                    
create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD));
 
             Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
                     4, null);
@@ -532,7 +533,8 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
     public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePaidPaidOnTimeLatePaidPayoffOnTime()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(
+                    
create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD));
 
             Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
                     4, null);
@@ -875,7 +877,8 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
     public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepOnePaid()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(
+                    
create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD));
 
             Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
                     4, null);
@@ -997,7 +1000,8 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
     public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnProgressiveLoanJob()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(
+                    
create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD));
 
             Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
                     4, null);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
index 89b8e41670..0c5a9551eb 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
@@ -265,7 +265,7 @@ public class LoanRescheduleRequestTest extends 
BaseLoanIntegrationTest {
         runAt("15 February 2023", () -> {
 
             loanResponse.set(applyForLoanApplication(client.getClientId(), 
commonLoanProductId, BigDecimal.valueOf(500.0), 45, 15, 3,
-                    BigDecimal.ZERO, "01 January 2023", "01 January 2023"));
+                    BigDecimal.TEN, "01 January 2023", "01 January 2023"));
 
             loanTransactionHelper.approveLoan(loanResponse.get().getLoanId(),
                     new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
index 3f1592b315..3978155628 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java
@@ -469,7 +469,7 @@ public final class BatchHelper {
         final String dateString = 
LocalDate.now(Utils.getZoneIdOfTenant()).format(DateTimeFormatter.ofPattern(dateFormat));
 
         final String body = String.format(
-                "{\"chargeId\": \"%d\", \"locale\": \"en\", \"amount\": 
\"11.15\", " + "\"dateFormat\": \"%s\", \"dueDate\": \"%s\"}",
+                "{\"chargeId\": \"%d\", \"locale\": \"en\", \"amount\": 
\"100.0\", " + "\"dateFormat\": \"%s\", \"dueDate\": \"%s\"}",
                 chargeId, dateFormat, dateString);
         br.setBody(body);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BusinessDateHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BusinessDateHelper.java
index d94eb3e9b7..0ae7d0764b 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BusinessDateHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BusinessDateHelper.java
@@ -18,6 +18,8 @@
  */
 package org.apache.fineract.integrationtests.common;
 
+import static 
org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
+
 import com.google.gson.Gson;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
@@ -28,13 +30,16 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.BusinessDateData;
 import org.apache.fineract.client.models.BusinessDateRequest;
 import org.apache.fineract.client.models.BusinessDateResponse;
+import org.apache.fineract.client.models.PutGlobalConfigurationsRequest;
 import org.apache.fineract.client.util.Calls;
 import org.apache.fineract.client.util.JSON;
 import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import 
org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
 
 @Slf4j
 public final class BusinessDateHelper {
 
+    private static final String DATETIME_PATTERN = "dd MMMM yyyy";
     private static final Gson GSON = new JSON().getGson();
 
     public BusinessDateHelper() {}
@@ -51,7 +56,7 @@ public final class BusinessDateHelper {
         return Utils.performServerPost(requestSpec, responseSpec, 
BUSINESS_DATE_API, buildBusinessDateRequest(type, date), "changes");
     }
 
-    public BusinessDateResponse updateBusinessDate(final BusinessDateRequest 
request) {
+    public static BusinessDateResponse updateBusinessDate(final 
BusinessDateRequest request) {
         log.info("------------------UPDATE BUSINESS 
DATE----------------------");
         log.info("------------------Type: {}, date: {}----------------------", 
request.getType(), request.getDate());
         return 
Calls.ok(FineractClientHelper.getFineractClient().businessDateManagement.updateBusinessDate(request));
@@ -91,4 +96,17 @@ public final class BusinessDateHelper {
         return new Gson().toJson(map);
     }
 
+    public static void runAt(String date, Runnable runnable) {
+        try {
+            new 
GlobalConfigurationHelper().updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(true));
+            updateBusinessDate(
+                    new 
BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
+            runnable.run();
+        } finally {
+            new 
GlobalConfigurationHelper().updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_BUSINESS_DATE,
+                    new PutGlobalConfigurationsRequest().enabled(false));
+        }
+    }
+
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index 1d4f389ea0..847bec4ba9 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -127,8 +127,8 @@ public class LoanProductTestBuilder {
     private String interestRecalculationCompoundingMethod = "0";
     private String preCloseInterestCalculationStrategy = 
INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE;
     private String rescheduleStrategyMethod = "1";
-    private String recalculationRestFrequencyType = "1";
-    private String recalculationRestFrequencyInterval = "0";
+    private String recalculationRestFrequencyType = "2";
+    private String recalculationRestFrequencyInterval = "1";
     private String recalculationCompoundingFrequencyType = null;
     private String recalculationCompoundingFrequencyInterval = null;
     private String minimumDaysBetweenDisbursalAndFirstRepayment = null;
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTestLifecycleExtension.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTestLifecycleExtension.java
index e3d9b0f168..e19019aad0 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTestLifecycleExtension.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTestLifecycleExtension.java
@@ -18,62 +18,73 @@
  */
 package org.apache.fineract.integrationtests.common.loans;
 
-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.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatterBuilder;
-import java.util.HashMap;
 import java.util.List;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
+import org.apache.fineract.client.util.Calls;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.FineractClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
 
 public class LoanTestLifecycleExtension implements AfterEachCallback {
 
-    private ResponseSpecification responseSpec;
-    private RequestSpecification requestSpec;
     private LoanTransactionHelper loanTransactionHelper;
-    private DateTimeFormatter dateFormatter = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
+    public static final String DATE_FORMAT = "dd MMMM yyyy";
+    private final DateTimeFormatter dateFormatter = new 
DateTimeFormatterBuilder().appendPattern(DATE_FORMAT).toFormatter();
 
     @Override
     public void afterEach(ExtensionContext context) {
-        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);
+        
BusinessDateHelper.runAt(DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.getLocalDateOfTenant()),
 () -> {
+            this.loanTransactionHelper = new LoanTransactionHelper(null, null);
 
-        // Fully repay ACTIVE loans, so it will not be picked up by any jobs
-        List<Integer> loanIds = 
LoanTransactionHelper.getLoanIdsByStatusId(requestSpec, responseSpec, 300);
-        loanIds.forEach(loanId -> {
-            HashMap prepayDetail = 
this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, 
loanId);
-            LocalDate transactionDate = LocalDate.of((Integer) ((List) 
prepayDetail.get("date")).get(0),
-                    (Integer) ((List) prepayDetail.get("date")).get(1), 
(Integer) ((List) prepayDetail.get("date")).get(2));
-            Double amount = 
Double.parseDouble(String.valueOf(prepayDetail.get("amount")));
-            Double netDisbursalAmount = 
Double.parseDouble(String.valueOf(prepayDetail.get("netDisbursalAmount")));
-            Double repayAmount = Double.compare(amount, 0.0) > 0 ? amount : 
netDisbursalAmount;
-            loanTransactionHelper.makeLoanRepayment((long) loanId, new 
PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy")
-                    
.transactionDate(dateFormatter.format(transactionDate)).locale("en").transactionAmount(repayAmount));
-        });
-        // Undo APPROVED loans, so the next step can REJECT them, so it will 
not be picked up by any jobs
-        loanIds = LoanTransactionHelper.getLoanIdsByStatusId(requestSpec, 
responseSpec, 200);
-        loanIds.forEach(loanId -> {
-            loanTransactionHelper.undoApproval(loanId);
-        });
-        // Mark SUBMITTED loans, as REJECTED, so it will not be picked up by 
any jobs
-        loanIds = LoanTransactionHelper.getLoanIdsByStatusId(requestSpec, 
responseSpec, 100);
-        loanIds.forEach(loanId -> {
-            GetLoansLoanIdResponse details = 
loanTransactionHelper.getLoanDetails((long) loanId);
-            loanTransactionHelper.rejectLoan((long) loanId,
-                    new 
PostLoansLoanIdRequest().rejectedOnDate(dateFormatter.format(details.getTimeline().getSubmittedOnDate()))
-                            .locale("en").dateFormat("dd MMMM yyyy"));
+            // Fully repay ACTIVE loans, so it will not be picked up by any 
jobs
+            List<Long> loanIds = 
LoanTransactionHelper.getLoanIdsByStatusId(300);
+            loanIds.forEach(loanId -> {
+                GetLoansLoanIdResponse loanResponse = Calls
+                        
.ok(FineractClientHelper.getFineractClient().loans.retrieveLoan((long) loanId, 
null, "all", null, null));
+                
loanResponse.getDisbursementDetails().forEach(disbursementDetail -> {
+                    if (disbursementDetail.getActualDisbursementDate() == 
null) {
+                        loanTransactionHelper.disburseLoan((long) loanId,
+                                new PostLoansLoanIdRequest()
+                                        
.actualDisbursementDate(dateFormatter.format(disbursementDetail.getExpectedDisbursementDate()))
+                                        .dateFormat(DATE_FORMAT).locale("en")
+                                        
.transactionAmount(BigDecimal.valueOf(disbursementDetail.getPrincipal())));
+                    }
+                });
+                loanResponse = Calls
+                        
.ok(FineractClientHelper.getFineractClient().loans.retrieveLoan((long) loanId, 
null, "all", null, null));
+                GetLoansLoanIdTransactionsTemplateResponse prepayDetail = 
this.loanTransactionHelper.getPrepaymentAmount(loanId,
+                        dateFormatter.format(Utils.getLocalDateOfTenant()), 
DATE_FORMAT);
+                LocalDate transactionDate = prepayDetail.getDate();
+                Double amount = prepayDetail.getAmount();
+                Double netDisbursalAmount = 
prepayDetail.getNetDisbursalAmount();
+                Double repayAmount = Double.compare(amount, 0.0) > 0 ? amount 
: netDisbursalAmount;
+                loanTransactionHelper.makeLoanRepayment(loanId, new 
PostLoansLoanIdTransactionsRequest().dateFormat(DATE_FORMAT)
+                        
.transactionDate(dateFormatter.format(transactionDate)).locale("en").transactionAmount(repayAmount));
+            });
+            // Undo APPROVED loans, so the next step can REJECT them, so it 
will not be picked up by any jobs
+            loanIds = LoanTransactionHelper.getLoanIdsByStatusId(200);
+            loanIds.forEach(loanId -> {
+                loanTransactionHelper.undoApprovalForLoan(loanId, new 
PostLoansLoanIdRequest());
+            });
+            // Mark SUBMITTED loans, as REJECTED, so it will not be picked up 
by any jobs
+            loanIds = LoanTransactionHelper.getLoanIdsByStatusId(100);
+            loanIds.forEach(loanId -> {
+                GetLoansLoanIdResponse details = 
loanTransactionHelper.getLoanDetails((long) loanId);
+                loanTransactionHelper.rejectLoan(loanId,
+                        new 
PostLoansLoanIdRequest().rejectedOnDate(dateFormatter.format(details.getTimeline().getSubmittedOnDate()))
+                                .locale("en").dateFormat(DATE_FORMAT));
+            });
+            // loanIds = LoanTransactionHelper.getLoanIdsByStatusId(300);
+            // assertEquals(0, loanIds.size());
         });
     }
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 87e78d3b5b..8918a5a146 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -2983,6 +2983,10 @@ public class LoanTransactionHelper {
         return new Gson().fromJson(get, new TypeToken<ArrayList<Integer>>() 
{}.getType());
     }
 
+    public static List<Long> getLoanIdsByStatusId(Integer statusId) {
+        return 
Calls.ok(FineractClientHelper.getFineractClient().legacy.getLoansByStatus(statusId));
+    }
+
     public PutLoanProductsProductIdResponse updateLoanProduct(Long id, 
PutLoanProductsProductIdRequest requestModifyLoan) {
         return 
Calls.ok(FineractClientHelper.getFineractClient().loanProducts.updateLoanProduct(id,
 requestModifyLoan));
     }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/organisation/CampaignsTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/organisation/CampaignsTest.java
index 8e704927a2..cb9d378350 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/organisation/CampaignsTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/organisation/CampaignsTest.java
@@ -30,6 +30,7 @@ import io.restassured.specification.ResponseSpecification;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
 import org.apache.fineract.integrationtests.common.CommonConstants;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.junit.jupiter.api.BeforeEach;
@@ -42,7 +43,6 @@ import org.mockserver.model.MediaType;
 
 @ExtendWith(MockServerExtension.class)
 @MockServerSettings(ports = { 9191 })
-
 public class CampaignsTest {
 
     private RequestSpecification requestSpec;
@@ -93,145 +93,155 @@ public class CampaignsTest {
 
     @Test
     public void testSupportedActionsForCampaignWithTriggerTypeAsDirect() {
-        // creating new campaign
-        Integer campaignId = 
this.campaignsHelper.createCampaign(NON_TRIGGERED_REPORT_NAME, 
DIRECT_TRIGGER_TYPE);
-        this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
-
-        // updating campaign
-        Integer updatedCampaignId = 
this.campaignsHelper.updateCampaign(this.requestSpec, this.responseSpec, 
campaignId,
-                NON_TRIGGERED_REPORT_NAME, DIRECT_TRIGGER_TYPE);
-        assertEquals(campaignId, updatedCampaignId);
-
-        // activating campaign
-        Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                ACTIVATE_COMMAND);
-        assertEquals(activatedCampaignId, campaignId);
-
-        // closing campaign
-        Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // reactivating campaign
-        Integer reactivateCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                REACTIVATE_COMMAND);
-        assertEquals(reactivateCampaignId, campaignId);
-
-        // closing campaign again for deletion
-        closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId, CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // deleting campaign
-        Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
-        assertEquals(deletedCampaignId, campaignId);
+        
BusinessDateHelper.runAt(DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.getLocalDateOfTenant()),
 () -> {
+            // creating new campaign
+            Integer campaignId = 
this.campaignsHelper.createCampaign(NON_TRIGGERED_REPORT_NAME, 
DIRECT_TRIGGER_TYPE);
+            
this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
+
+            // updating campaign
+            Integer updatedCampaignId = 
this.campaignsHelper.updateCampaign(this.requestSpec, this.responseSpec, 
campaignId,
+                    NON_TRIGGERED_REPORT_NAME, DIRECT_TRIGGER_TYPE);
+            assertEquals(campaignId, updatedCampaignId);
+
+            // activating campaign
+            Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    ACTIVATE_COMMAND);
+            assertEquals(activatedCampaignId, campaignId);
+
+            // closing campaign
+            Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // reactivating campaign
+            Integer reactivateCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    REACTIVATE_COMMAND);
+            assertEquals(reactivateCampaignId, campaignId);
+
+            // closing campaign again for deletion
+            closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // deleting campaign
+            Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
+            assertEquals(deletedCampaignId, campaignId);
+        });
     }
 
     @Test
     public void testSupportedActionsForCampaignWithTriggerTypeAsScheduled() {
-        // creating new campaign
-        Integer campaignId = 
this.campaignsHelper.createCampaign(NON_TRIGGERED_REPORT_NAME, 
SCHEDULED_TRIGGER_TYPE);
-        this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
-
-        // updating campaign
-        Integer updatedCampaignId = 
this.campaignsHelper.updateCampaign(this.requestSpec, this.responseSpec, 
campaignId,
-                NON_TRIGGERED_REPORT_NAME, SCHEDULED_TRIGGER_TYPE);
-        assertEquals(campaignId, updatedCampaignId);
-
-        // activating campaign
-        Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                ACTIVATE_COMMAND);
-        assertEquals(activatedCampaignId, campaignId);
-
-        // closing campaign
-        Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // reactivating campaign
-        Integer reactivateCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                REACTIVATE_COMMAND);
-        assertEquals(reactivateCampaignId, campaignId);
-
-        // closing campaign again for deletion
-        closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId, CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // deleting campaign
-        Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
-        assertEquals(deletedCampaignId, campaignId);
+        
BusinessDateHelper.runAt(DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.getLocalDateOfTenant()),
 () -> {
+            // creating new campaign
+            Integer campaignId = 
this.campaignsHelper.createCampaign(NON_TRIGGERED_REPORT_NAME, 
SCHEDULED_TRIGGER_TYPE);
+            
this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
+
+            // updating campaign
+            Integer updatedCampaignId = 
this.campaignsHelper.updateCampaign(this.requestSpec, this.responseSpec, 
campaignId,
+                    NON_TRIGGERED_REPORT_NAME, SCHEDULED_TRIGGER_TYPE);
+            assertEquals(campaignId, updatedCampaignId);
+
+            // activating campaign
+            Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    ACTIVATE_COMMAND);
+            assertEquals(activatedCampaignId, campaignId);
+
+            // closing campaign
+            Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // reactivating campaign
+            Integer reactivateCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    REACTIVATE_COMMAND);
+            assertEquals(reactivateCampaignId, campaignId);
+
+            // closing campaign again for deletion
+            closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // deleting campaign
+            Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
+            assertEquals(deletedCampaignId, campaignId);
+        });
     }
 
     @Test
     public void testSupportedActionsForCampaignWithTriggerTypeAsTriggered() {
-        // creating new campaign
-        Integer campaignId = 
this.campaignsHelper.createCampaign(TRIGGERED_REPORT_NAME, 
TRIGGERED_TRIGGER_TYPE);
-        this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
-
-        // updating campaign
-        Integer updatedCampaignId = 
this.campaignsHelper.updateCampaign(this.requestSpec, this.responseSpec, 
campaignId,
-                TRIGGERED_REPORT_NAME, TRIGGERED_TRIGGER_TYPE);
-        assertEquals(campaignId, updatedCampaignId);
-
-        // activating campaign
-        Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                ACTIVATE_COMMAND);
-        assertEquals(activatedCampaignId, campaignId);
-
-        // closing campaign
-        Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // reactivating campaign
-        Integer reactivateCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                REACTIVATE_COMMAND);
-        assertEquals(reactivateCampaignId, campaignId);
-
-        // closing campaign again for deletion
-        closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId, CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // deleting campaign
-        Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
-        assertEquals(deletedCampaignId, campaignId);
+        
BusinessDateHelper.runAt(DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.getLocalDateOfTenant()),
 () -> {
+            // creating new campaign
+            Integer campaignId = 
this.campaignsHelper.createCampaign(TRIGGERED_REPORT_NAME, 
TRIGGERED_TRIGGER_TYPE);
+            
this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
+
+            // updating campaign
+            Integer updatedCampaignId = 
this.campaignsHelper.updateCampaign(this.requestSpec, this.responseSpec, 
campaignId,
+                    TRIGGERED_REPORT_NAME, TRIGGERED_TRIGGER_TYPE);
+            assertEquals(campaignId, updatedCampaignId);
+
+            // activating campaign
+            Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    ACTIVATE_COMMAND);
+            assertEquals(activatedCampaignId, campaignId);
+
+            // closing campaign
+            Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // reactivating campaign
+            Integer reactivateCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    REACTIVATE_COMMAND);
+            assertEquals(reactivateCampaignId, campaignId);
+
+            // closing campaign again for deletion
+            closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // deleting campaign
+            Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
+            assertEquals(deletedCampaignId, campaignId);
+        });
     }
 
     @SuppressWarnings("unchecked")
     @Test
     public void testSupportedActionsForCampaignWithError() {
-        final ResponseSpecification responseSpecWithError = new 
ResponseSpecBuilder().expectStatusCode(400).build();
-        CampaignsHelper campaignsHelperWithError = new 
CampaignsHelper(this.requestSpec, responseSpecWithError);
-        // creating new campaign
-        Integer campaignId = 
this.campaignsHelper.createCampaign(NON_TRIGGERED_REPORT_NAME, 
DIRECT_TRIGGER_TYPE);
-        this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
-
-        // activating campaign with failure
-        ArrayList<HashMap<String, Object>> campaignDateValidationData = 
(ArrayList<HashMap<String, Object>>) campaignsHelperWithError
-                .performActionsOnCampaignWithFailure(campaignId, 
ACTIVATE_COMMAND,
-                        
Utils.getLocalDateOfTenant().plusDays(1).format(DateTimeFormatter.ofPattern(DATE_FORMAT)),
-                        CommonConstants.RESPONSE_ERROR);
-        assertEquals("error.msg.campaign.activationDate.in.the.future",
-                
campaignDateValidationData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
-
-        // activating campaign
-        Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                ACTIVATE_COMMAND);
-        assertEquals(activatedCampaignId, campaignId);
-
-        // activating campaign with failure
-        ArrayList<HashMap<String, Object>> campaignErrorData = 
(ArrayList<HashMap<String, Object>>) campaignsHelperWithError
-                .performActionsOnCampaignWithFailure(activatedCampaignId, 
ACTIVATE_COMMAND,
-                        
Utils.getLocalDateOfTenant().format(DateTimeFormatter.ofPattern(DATE_FORMAT)), 
CommonConstants.RESPONSE_ERROR);
-        assertEquals("error.msg.campaign.already.active", 
campaignErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
-
-        // closing campaign again for deletion
-        Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
-                CLOSE_COMMAND);
-        assertEquals(closedCampaignId, campaignId);
-
-        // deleting campaign
-        Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
-        assertEquals(deletedCampaignId, campaignId);
-
+        
BusinessDateHelper.runAt(DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.getLocalDateOfTenant()),
 () -> {
+            final ResponseSpecification responseSpecWithError = new 
ResponseSpecBuilder().expectStatusCode(400).build();
+            CampaignsHelper campaignsHelperWithError = new 
CampaignsHelper(this.requestSpec, responseSpecWithError);
+            // creating new campaign
+            Integer campaignId = 
this.campaignsHelper.createCampaign(NON_TRIGGERED_REPORT_NAME, 
DIRECT_TRIGGER_TYPE);
+            
this.campaignsHelper.verifyCampaignCreatedOnServer(this.requestSpec, 
this.responseSpec, campaignId);
+
+            // activating campaign with failure
+            ArrayList<HashMap<String, Object>> campaignDateValidationData = 
(ArrayList<HashMap<String, Object>>) campaignsHelperWithError
+                    .performActionsOnCampaignWithFailure(campaignId, 
ACTIVATE_COMMAND,
+                            
Utils.getLocalDateOfTenant().plusDays(1).format(DateTimeFormatter.ofPattern(DATE_FORMAT)),
+                            CommonConstants.RESPONSE_ERROR);
+            assertEquals("error.msg.campaign.activationDate.in.the.future",
+                    
campaignDateValidationData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
+            // activating campaign
+            Integer activatedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    ACTIVATE_COMMAND);
+            assertEquals(activatedCampaignId, campaignId);
+
+            // activating campaign with failure
+            ArrayList<HashMap<String, Object>> campaignErrorData = 
(ArrayList<HashMap<String, Object>>) campaignsHelperWithError
+                    .performActionsOnCampaignWithFailure(activatedCampaignId, 
ACTIVATE_COMMAND,
+                            
Utils.getLocalDateOfTenant().format(DateTimeFormatter.ofPattern(DATE_FORMAT)), 
CommonConstants.RESPONSE_ERROR);
+            assertEquals("error.msg.campaign.already.active", 
campaignErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
+
+            // closing campaign again for deletion
+            Integer closedCampaignId = 
this.campaignsHelper.performActionsOnCampaign(this.requestSpec, 
this.responseSpec, campaignId,
+                    CLOSE_COMMAND);
+            assertEquals(closedCampaignId, campaignId);
+
+            // deleting campaign
+            Integer deletedCampaignId = 
this.campaignsHelper.deleteCampaign(this.requestSpec, this.responseSpec, 
campaignId);
+            assertEquals(deletedCampaignId, campaignId);
+        });
     }
 }
diff --git a/integration-tests/src/test/resources/jsv-messages.properties 
b/integration-tests/src/test/resources/jsv-messages.properties
new file mode 100644
index 0000000000..e87defdade
--- /dev/null
+++ b/integration-tests/src/test/resources/jsv-messages.properties
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+additionalProperties=Additional properties are not allowed: {0}
+allOf=One or more conditions in allOf failed: {0}
+anyOf=Value does not match any of the required schemas: {0}
+const=Value must be exactly: {0}
+contains=Array does not contain required item: {0}
+crossEdits=Cross edits are not allowed: {0}
+dependencies=Missing dependencies: {0}
+dependentRequired=Missing dependent properties: {0}
+edits=Invalid edits: {0}
+enum=Value {0} is not allowed. Allowed values are: {1}
+exclusiveMaximum=Value {0} must be less than (exclusive): {1}
+exclusiveMinimum=Value {0} must be greater than (exclusive): {1}
+format=Value {0} does not match required format: {1}
+items=Invalid item in array: {0}
+maxItems=Array has too many items: {0}, maximum: {1}
+maxLength=String is too long: {0}, maximum length: {1}
+maxProperties=Too many properties: {0}, maximum allowed: {1}
+maximum=Value {0} exceeds maximum: {1}
+minItems=Array has too few items: {0}, minimum: {1}
+minLength=String is too short: {0}, minimum length: {1}
+minProperties=Too few properties: {0}, minimum required: {1}
+minimum=Value {0} is below minimum: {1}
+multipleOf=Value {0} is not a multiple of {1}
+not=Value matches schema when it should not: {0}
+notAllowed=The value is not allowed: {0}
+oneOf=Value matches more than one schema in oneOf: {0}
+pattern=String does not match pattern: {0}
+prefixItems=Array does not match prefixItems validation rules: {0}
+properties=Invalid property: {0}
+propertyNames=Invalid property name: {0}
+readOnly=Property is read-only: {0}
+required=Required field is missing: {0}
+then='then' schema failed validation
+type=Expected type: {0}, found: {1}
+unionType=Value does not match any of the expected types: {0}
+uniqueItems=Array has duplicate items: {0}
+writeOnly=Property is write-only: {0}
+$ref=Reference {0} could not be resolved or is invalid
+if=Condition 'if' failed
+else='else' schema failed validation
+allErrors=Multiple validation errors occurred: {0}
+default=Validation failed for value: {0}
+patternProperties=Pattern property not matched: {0}
+
+# Format-specific
+date=Value does not match date format: {0}
+dateTime=Value does not match date-time format: {0}
+time=Value does not match time format: {0}
+email=Value is not a valid email address: {0}
+hostname=Value is not a valid hostname: {0}
+ipv4=Value is not a valid IPv4 address: {0}
+ipv6=Value is not a valid IPv6 address: {0}
+uri=Value is not a valid URI: {0}
+uuid=Value is not a valid UUID: {0}
+uriReference=Value is not a valid URI reference: {0}
+iri=Value is not a valid IRI: {0}
+iriReference=Value is not a valid IRI reference: {0}
+jsonPointer=Value is not a valid JSON Pointer: {0}
+relativeJsonPointer=Value is not a valid Relative JSON Pointer: {0}
+regex=Value is not a valid regular expression: {0}
+byte=Value is not a valid base64 string: {0}
+int32=Value is not a valid 32-bit integer: {0}
+int64=Value is not a valid 64-bit integer: {0}
+float=Value is not a valid float: {0}
+double=Value is not a valid double: {0}
+binary=Value is not a valid binary: {0}
+password=Password format validation failed
+contentEncoding=Value does not match expected encoding: {0}
+contentMediaType=Value does not match expected media type: {0}
+id=Invalid ID value: {0}
+false=Schema explicitly disallows all values
+dependentSchemas=Object does not satisfy dependent schemas: {0}
+unevaluatedProperties=Object has unevaluated properties that are not allowed: 
{0}

Reply via email to