Skip to content

Commit

Permalink
FINERACT-2060: Accrual reverse replay logic and Handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Marta Jankovics committed Nov 22, 2024
1 parent ebfc11c commit 0dc86ec
Show file tree
Hide file tree
Showing 72 changed files with 2,007 additions and 2,914 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.charge.service;

import java.util.Collection;
import java.util.List;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;

Expand Down Expand Up @@ -53,7 +54,7 @@ public interface ChargeReadPlatformService {
* Excludes Given List of Charge Types from the response
* @return
*/
Collection<ChargeData> retrieveLoanAccountApplicableCharges(Long loanId, ChargeTimeType[] excludeChargeTimes);
List<ChargeData> retrieveLoanAccountApplicableCharges(Long loanId, ChargeTimeType[] excludeChargeTimes);

/**
* Returns all charges applicable for a given loan product (filter based on Currency of Selected Loan Product)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,23 @@ public static int compare(OffsetDateTime first, OffsetDateTime second) {
}

public static int compare(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate, true);
}

public static int compareWithNullsLast(OffsetDateTime first, OffsetDateTime second) {
return compare(first, second, null, false);
}

public static int compareWithNullsLast(@NotNull Optional<OffsetDateTime> first, @NotNull Optional<OffsetDateTime> second) {
return compareWithNullsLast(first.orElse(null), second.orElse(null));
}

public static int compare(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate, boolean nullFirst) {
if (first == null) {
return second == null ? 0 : -1;
return second == null ? 0 : (nullFirst ? -1 : 1);
}
if (second == null) {
return 1;
return nullFirst ? 1 : -1;
}
first = first.withOffsetSameInstant(ZoneOffset.UTC);
second = second.withOffsetSameInstant(ZoneOffset.UTC);
Expand Down Expand Up @@ -291,7 +303,23 @@ public static boolean isDateInTheFuture(final LocalDate localDate) {
}

public static int compare(LocalDate first, LocalDate second) {
return first == null ? (second == null ? 0 : -1) : (second == null ? 1 : first.compareTo(second));
return compare(first, second, true);
}

/**
* Comparing dates. Null will be considered as last elements
*
* @param first
* @param second
* @return
*/
public static int compareWithNullsLast(LocalDate first, LocalDate second) {
return compare(first, second, false);
}

public static int compare(LocalDate first, LocalDate second, boolean nullFirst) {
return first == null ? (second == null ? 0 : (nullFirst ? -1 : 1))
: (second == null ? (nullFirst ? 1 : -1) : first.compareTo(second));
}

public static boolean isEqual(LocalDate first, LocalDate second) {
Expand Down Expand Up @@ -426,23 +454,4 @@ private static DateTimeFormatter getDateTimeFormatter(String format, Locale loca
}
return formatter;
}

/**
* Comparing dates. Null will be considered as last elements
*
* @param first
* @param second
* @return
*/
public static int compareWithNullsLast(LocalDate first, LocalDate second) {
return first == null ? (second == null ? 0 : 1) : (second == null ? -1 : first.compareTo(second));
}

public static int compareWithNullsLast(@NotNull Optional<OffsetDateTime> first, @NotNull Optional<OffsetDateTime> second) {
return DateUtils.compareWithNullsLast(first.orElse(null), second.orElse(null));
}

public static int compareWithNullsLast(OffsetDateTime first, OffsetDateTime second) {
return first == null ? (second == null ? 0 : 1) : (second == null ? -1 : first.compareTo(second));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ public static BigDecimal subtract(BigDecimal first, BigDecimal second) {
return subtract(first, second, MoneyHelper.getMathContext());
}

/** @return first minus the others considering null values, maybe negative */
public static BigDecimal subtract(BigDecimal first, BigDecimal second, BigDecimal third) {
return subtract(subtract(first, second), third);
}

/** @return first minus second considering null values, maybe negative */
public static BigDecimal subtract(BigDecimal first, BigDecimal second, MathContext mc) {
return first == null ? null : second == null ? first : first.subtract(second, mc);
Expand Down Expand Up @@ -336,6 +341,10 @@ public static String formatToSql(BigDecimal amount) {
return amount == null ? null : amount.toPlainString();
}

public static Money toMoney(BigDecimal amount, @NotNull MonetaryCurrency currency) {
return amount == null ? null : Money.of(currency, amount);
}

// ----------------- Money -----------------

public static BigDecimal toBigDecimal(Money value) {
Expand Down Expand Up @@ -454,6 +463,16 @@ public static Money min(Money first, Money second, Money third, boolean notNull)
return min(min(first, second, notNull), third, notNull);
}

/** @return Money null safe negate */
public static Money negate(Money amount) {
return negate(amount, MoneyHelper.getMathContext());
}

/** @return Money null safe negate */
public static Money negate(Money amount, MathContext mc) {
return isEmpty(amount) ? amount : amount.negated(mc);
}

/**
* Calculate percentage of a value
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,58 @@
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.GetBusinessStepConfigResponse;
import org.apache.fineract.client.models.UpdateBusinessStepConfigRequest;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.test.support.TestContext;
import org.apache.fineract.test.support.TestContextKey;
import org.springframework.stereotype.Component;
import retrofit2.Response;

@RequiredArgsConstructor
@Component
@Slf4j
public class WorkFlowJobHelper {

private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS";

private final BusinessStepConfigurationApi businessStepConfigurationApi;

public void saveOriginalCOBWorkflowJobBusinessStepList() throws IOException {
Response<GetBusinessStepConfigResponse> businessStepConfigResponse = businessStepConfigurationApi
public void setWorkflowJobs() throws IOException {
List<BusinessStep> businessSteps = List.of(//
new BusinessStep().stepName("APPLY_CHARGE_TO_OVERDUE_LOANS").order(1L), //
new BusinessStep().stepName("LOAN_DELINQUENCY_CLASSIFICATION").order(2L), //
new BusinessStep().stepName("LOAN_INTEREST_RECALCULATION").order(3L), //
new BusinessStep().stepName("ADD_PERIODIC_ACCRUAL_ENTRIES").order(4L), //
new BusinessStep().stepName("ACCRUAL_ACTIVITY_POSTING").order(5L), //
new BusinessStep().stepName("CHECK_LOAN_REPAYMENT_DUE").order(6L), //
new BusinessStep().stepName("CHECK_LOAN_REPAYMENT_OVERDUE").order(7L), //
new BusinessStep().stepName("CHECK_DUE_INSTALLMENTS").order(8L), //
new BusinessStep().stepName("UPDATE_LOAN_ARREARS_AGING").order(9L), //
new BusinessStep().stepName("EXTERNAL_ASSET_OWNER_TRANSFER").order(10L)//
);
UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);
Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
// --- log changes ---
logChanges();
}

private void logChanges() throws IOException {
// --- log changes ---
Response<GetBusinessStepConfigResponse> changesResponse = businessStepConfigurationApi
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute();
ErrorHelper.checkSuccessfulApiCall(businessStepConfigResponse);
List<BusinessStep> businessSteps = businessStepConfigResponse.body().getBusinessSteps();
businessSteps.sort(Comparator.comparingLong(BusinessStep::getOrder));
TestContext.GLOBAL.set(TestContextKey.ORIGINAL_COB_WORKFLOW_JOB_BUSINESS_STEP_LIST, businessSteps);
List<BusinessStep> businessStepsChanged = changesResponse.body().getBusinessSteps();
List<String> changes = businessStepsChanged//
.stream()//
.sorted(Comparator.comparingLong(BusinessStep::getOrder))//
.map(BusinessStep::getStepName)//
.collect(Collectors.toList());//

log.debug("Business steps has been CHANGED to the following:");
changes.forEach(e -> log.debug(e));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.List;
import org.apache.fineract.test.config.CacheConfiguration;
import org.apache.fineract.test.helper.BusinessDateHelper;
import org.apache.fineract.test.helper.WorkFlowJobHelper;
import org.apache.fineract.test.initializer.global.FineractGlobalInitializerStep;
import org.apache.fineract.test.initializer.scenario.FineractScenarioInitializerStep;
import org.apache.fineract.test.initializer.suite.FineractSuiteInitializerStep;
Expand All @@ -40,8 +39,7 @@ public class BaseFineractInitializerConfiguration {
@Bean
public FineractInitializer fineractInitializer(List<FineractGlobalInitializerStep> globalInitializerSteps,
List<FineractSuiteInitializerStep> suiteInitializerSteps, List<FineractScenarioInitializerStep> scenarioInitializerSteps,
BusinessDateHelper businessDateHelper, WorkFlowJobHelper workFlowJobHelper) {
return new FineractInitializer(globalInitializerSteps, suiteInitializerSteps, scenarioInitializerSteps, businessDateHelper,
workFlowJobHelper);
BusinessDateHelper businessDateHelper) {
return new FineractInitializer(globalInitializerSteps, suiteInitializerSteps, scenarioInitializerSteps, businessDateHelper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.test.helper.BusinessDateHelper;
import org.apache.fineract.test.helper.WorkFlowJobHelper;
import org.apache.fineract.test.initializer.global.FineractGlobalInitializerStep;
import org.apache.fineract.test.initializer.scenario.FineractScenarioInitializerStep;
import org.apache.fineract.test.initializer.suite.FineractSuiteInitializerStep;
Expand All @@ -39,7 +38,6 @@ public class FineractInitializer implements InitializingBean {
private final List<FineractSuiteInitializerStep> suiteInitializerSteps;
private final List<FineractScenarioInitializerStep> scenarioInitializerSteps;
private final BusinessDateHelper businessDateHelper;
private final WorkFlowJobHelper workFlowJobHelper;

@Override
public void afterPropertiesSet() throws Exception {
Expand All @@ -63,32 +61,27 @@ public void setupGlobalDefaults() throws Exception {
for (FineractGlobalInitializerStep initializerStep : globalInitializerSteps) {
initializerStep.initialize();
}

businessDateHelper.setBusinessDateToday();
}

public void setupDefaultsForSuite() throws Exception {
for (FineractSuiteInitializerStep initializerStep : suiteInitializerSteps) {
initializerStep.initializeForSuite();
}

businessDateHelper.setBusinessDateToday();
workFlowJobHelper.saveOriginalCOBWorkflowJobBusinessStepList();
}

public void setupDefaultsForScenario() throws Exception {
for (FineractScenarioInitializerStep scenarioInitializerStep : scenarioInitializerSteps) {
scenarioInitializerStep.initializeForScenario();
}

businessDateHelper.setBusinessDateToday();
}

public void resetDefaultsAfterSuite() throws Exception {
for (FineractSuiteInitializerStep initializerStep : suiteInitializerSteps) {
initializerStep.resetAfterSuite();
}

businessDateHelper.setBusinessDateToday();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,18 @@
*/
package org.apache.fineract.test.initializer.global;

import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.client.models.BusinessStep;
import org.apache.fineract.client.models.GetBusinessStepConfigResponse;
import org.apache.fineract.client.models.UpdateBusinessStepConfigRequest;
import org.apache.fineract.client.services.BusinessStepConfigurationApi;
import org.apache.fineract.test.helper.ErrorHelper;
import org.apache.fineract.test.helper.WorkFlowJobHelper;
import org.springframework.stereotype.Component;
import retrofit2.Response;

@RequiredArgsConstructor
@Component
public class CobBusinessStepInitializerStep implements FineractGlobalInitializerStep {

private static final String WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS = "LOAN_CLOSE_OF_BUSINESS";
private static final String BUSINESS_STEP_NAME_ACCRUAL_ACTIVITY_POSTING = "ACCRUAL_ACTIVITY_POSTING";
private final BusinessStepConfigurationApi businessStepConfigurationApi;
private final WorkFlowJobHelper workFlowJobHelper;

@Override
public void initialize() throws Exception {
// --- Adding ACCRUAL_ACTIVITY_POSTING to default COB steps ---
Response<GetBusinessStepConfigResponse> businessStepConfigResponse = businessStepConfigurationApi
.retrieveAllConfiguredBusinessStep(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS).execute();
ErrorHelper.checkSuccessfulApiCall(businessStepConfigResponse);
List<BusinessStep> businessSteps = businessStepConfigResponse.body().getBusinessSteps();
businessSteps.sort(Comparator.comparingLong(BusinessStep::getOrder));
Long lastOrder = businessSteps.get(businessSteps.size() - 1).getOrder();

BusinessStep accrualActivityPosting = new BusinessStep().stepName(BUSINESS_STEP_NAME_ACCRUAL_ACTIVITY_POSTING).order(lastOrder + 1);
businessSteps.add(accrualActivityPosting);

UpdateBusinessStepConfigRequest request = new UpdateBusinessStepConfigRequest().businessSteps(businessSteps);

Response<Void> response = businessStepConfigurationApi.updateJobBusinessStepConfig(WORKFLOW_NAME_LOAN_CLOSE_OF_BUSINESS, request)
.execute();
ErrorHelper.checkSuccessfulApiCall(response);
workFlowJobHelper.setWorkflowJobs();
}
}
Loading

0 comments on commit 0dc86ec

Please sign in to comment.