From 15c968bc1bf35501d1f3ce13721c3d689c35f5c7 Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Wed, 21 May 2025 10:43:18 +0200 Subject: [PATCH] FINERACT-2181: Fix fully paid check on EmiModel --- .../LoanTransactionsApiResourceSwagger.java | 2 + .../LoanRepaymentScheduleInstallment.java | 7 +- .../calc/data/RepaymentPeriod.java | 2 +- .../InternalLoanInformationApiResource.java | 43 +--- .../adjustment/LoanAdjustmentServiceImpl.java | 2 +- .../BaseLoanIntegrationTest.java | 4 +- .../LoanInterestPauseApiTest.java | 22 +- .../LoanInterestRecalculationCOBTest.java | 12 +- .../LoanRescheduleRequestTest.java | 2 +- .../integrationtests/common/BatchHelper.java | 2 +- .../common/BusinessDateHelper.java | 20 +- .../common/loans/LoanProductTestBuilder.java | 4 +- .../loans/LoanTestLifecycleExtension.java | 87 ++++--- .../common/loans/LoanTransactionHelper.java | 4 + .../common/organisation/CampaignsTest.java | 220 +++++++++--------- .../test/resources/jsv-messages.properties | 92 ++++++++ 16 files changed, 326 insertions(+), 199 deletions(-) create mode 100644 integration-tests/src/test/resources/jsv-messages.properties 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 1ee205ae8..9014fd8c9 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 chargeOffReasonOptions; public List 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 86b828bc1..5789c37da 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 a92113938..e3faf4791 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 ee37e4972..67f399160 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 toApiJsonSerializerForMap; - private final ToApiJsonSerializer 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 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 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 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 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 339d4f358..1992eef92 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 c696a1fbb..40030968b 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 e0cd6de3d..e2a6b2370 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 343e5063b..83473953e 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 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 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 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 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 89b8e4167..0c5a9551e 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 3f1592b31..397815562 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 d94eb3e9b..0ae7d0764 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 1d4f389ea..847bec4ba 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 e3d9b0f16..e19019aad 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 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 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 87e78d3b5..8918a5a14 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>() {}.getType()); } + public static List 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 8e704927a..cb9d37835 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); + 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); + // 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); + // 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); + // 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); + // 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); + // 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); + // 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); + 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); + // 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); + // 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); + // 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); + // 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); + // 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); + // 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); + 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); + // 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); + // 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); + // 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); + // 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); + // 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); + // 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); + 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> campaignDateValidationData = (ArrayList>) 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 with failure + ArrayList> campaignDateValidationData = (ArrayList>) 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 + Integer activatedCampaignId = this.campaignsHelper.performActionsOnCampaign(this.requestSpec, this.responseSpec, campaignId, + ACTIVATE_COMMAND); + assertEquals(activatedCampaignId, campaignId); - // activating campaign with failure - ArrayList> campaignErrorData = (ArrayList>) 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)); + // activating campaign with failure + ArrayList> campaignErrorData = (ArrayList>) 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); + // 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 000000000..e87defdad --- /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}