FINERACT-1806: Reverse-replay logic for Charge-off transaction

This commit is contained in:
Adam Saghy
2023-06-23 08:46:52 +02:00
parent df39965483
commit 90c10a5ade
3 changed files with 113 additions and 166 deletions
@@ -59,9 +59,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.codes.domain.CodeValue; import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer; import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
@@ -3368,7 +3366,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>(); final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
List<LoanTransaction> trans = getLoanTransactions(); List<LoanTransaction> trans = getLoanTransactions();
for (final LoanTransaction transaction : trans) { for (final LoanTransaction transaction : trans) {
if (transaction.isNotReversed() && !(transaction.isDisbursement() || transaction.isNonMonetaryTransaction())) { if (transaction.isNotReversed()
&& (transaction.isChargeOff() || !(transaction.isDisbursement() || transaction.isNonMonetaryTransaction()))) {
repaymentsOrWaivers.add(transaction); repaymentsOrWaivers.add(transaction);
} }
} }
@@ -4486,20 +4485,18 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) { final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
final List<Map<String, Object>> accountingBridgeData = new ArrayList<>(); final List<Map<String, Object>> accountingBridgeData = new ArrayList<>();
// get map before charge-off
final List<Map<String, Object>> newLoanTransactionsBeforeChargeOff = new ArrayList<>(); final List<Map<String, Object>> newLoanTransactionsBeforeChargeOff = new ArrayList<>();
final Map<String, Object> accountingBridgeDataBeforeChargeOff = getAccountingMapForChargeOffDateCriteria(currencyCode,
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, newLoanTransactionsBeforeChargeOff, true);
// get map after charge-off
final List<Map<String, Object>> newLoanTransactionsAfterChargeOff = new ArrayList<>(); final List<Map<String, Object>> newLoanTransactionsAfterChargeOff = new ArrayList<>();
final Map<String, Object> accountingBridgeDataAfterChargeOff = getAccountingMapForChargeOffDateCriteria(currencyCode, // get map before charge-off
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, newLoanTransactionsAfterChargeOff, false); final Map<String, Object> accountingBridgeDataBeforeChargeOff = buildAccountingMapForChargeOffDateCriteria(currencyCode,
isAccountTransfer, true);
// get map after charge-off
final Map<String, Object> accountingBridgeDataAfterChargeOff = buildAccountingMapForChargeOffDateCriteria(currencyCode,
isAccountTransfer, false);
// get map onCharge off date // split the transactions according charge-off date
getAccountingMapDataOnChargeOffDate(currencyCode, existingTransactionIds, existingReversedTransactionIds, classifyTransactionsBasedOnChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff,
newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff); existingTransactionIds, existingReversedTransactionIds, currencyCode);
accountingBridgeDataBeforeChargeOff.put("newLoanTransactions", newLoanTransactionsBeforeChargeOff); accountingBridgeDataBeforeChargeOff.put("newLoanTransactions", newLoanTransactionsBeforeChargeOff);
accountingBridgeData.add(accountingBridgeDataBeforeChargeOff); accountingBridgeData.add(accountingBridgeDataBeforeChargeOff);
@@ -4510,6 +4507,21 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return accountingBridgeData; return accountingBridgeData;
} }
private void classifyTransactionsBasedOnChargeOffDate(List<Map<String, Object>> newLoanTransactionsBeforeChargeOff,
List<Map<String, Object>> newLoanTransactionsAfterChargeOff, List<Long> existingTransactionIds,
List<Long> existingReversedTransactionIds, String currencyCode) {
// Before
filterTransactionsByChargeOffDate(newLoanTransactionsBeforeChargeOff, currencyCode, existingTransactionIds,
existingReversedTransactionIds, transaction -> transaction.getTransactionDate().isBefore(getChargedOffOnDate()));
// On
filterTransactionsByChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, currencyCode,
existingTransactionIds, existingReversedTransactionIds,
transaction -> transaction.getTransactionDate().isEqual(getChargedOffOnDate()));
// After
filterTransactionsByChargeOffDate(newLoanTransactionsAfterChargeOff, currencyCode, existingTransactionIds,
existingReversedTransactionIds, transaction -> transaction.getTransactionDate().isAfter(getChargedOffOnDate()));
}
private Map<String, Object> getAccountingBridgeDataGenericAttributes(final String currencyCode, boolean isAccountTransfer) { private Map<String, Object> getAccountingBridgeDataGenericAttributes(final String currencyCode, boolean isAccountTransfer) {
final Map<String, Object> accountingBridgeDataGenericAttributes = new LinkedHashMap<>(); final Map<String, Object> accountingBridgeDataGenericAttributes = new LinkedHashMap<>();
accountingBridgeDataGenericAttributes.put("loanId", getId()); accountingBridgeDataGenericAttributes.put("loanId", getId());
@@ -4525,8 +4537,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return accountingBridgeDataGenericAttributes; return accountingBridgeDataGenericAttributes;
} }
private Map<String, Object> getAccountingMapForChargeOffDateCriteria(final String currencyCode, final List<Long> existingTransactionIds, private Map<String, Object> buildAccountingMapForChargeOffDateCriteria(final String currencyCode, boolean isAccountTransfer,
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer, List<Map<String, Object>> newLoanTransactions,
boolean isBeforeChargeOffDate) { boolean isBeforeChargeOffDate) {
final Map<String, Object> accountingBridgeDataChargeOff = new LinkedHashMap<>( final Map<String, Object> accountingBridgeDataChargeOff = new LinkedHashMap<>(
getAccountingBridgeDataGenericAttributes(currencyCode, isAccountTransfer)); getAccountingBridgeDataGenericAttributes(currencyCode, isAccountTransfer));
@@ -4537,124 +4548,41 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
accountingBridgeDataChargeOff.put("isChargeOff", isChargedOff()); accountingBridgeDataChargeOff.put("isChargeOff", isChargedOff());
accountingBridgeDataChargeOff.put("isFraud", isFraud()); accountingBridgeDataChargeOff.put("isFraud", isFraud());
} }
Predicate<LoanTransaction> chargeOffDateCriteria = isBeforeChargeOffDate
? transaction -> transaction.getTransactionDate().isBefore(getChargedOffOnDate())
: transaction -> transaction.getTransactionDate().isAfter(getChargedOffOnDate());
getTransactionsForAccountingBridgeData(currencyCode, existingTransactionIds, existingReversedTransactionIds, newLoanTransactions,
chargeOffDateCriteria);
return accountingBridgeDataChargeOff; return accountingBridgeDataChargeOff;
} }
private void getAccountingMapDataOnChargeOffDate(String currencyCode, List<Long> existingTransactionIds, private void filterTransactionsByChargeOffDate(List<Map<String, Object>> filteredTransactions, final String currencyCode,
List<Long> existingReversedTransactionIds, List<Map<String, Object>> newLoanTransactionsBeforeChargeOff, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
List<Map<String, Object>> newLoanTransactionsAfterChargeOff) {
Predicate<LoanTransaction> isOnChargeOff = transaction -> transaction.getTransactionDate().isEqual(getChargedOffOnDate());
List<LoanTransaction> transactionsOnChargeOffDate = this.loanTransactions.stream().filter(isOnChargeOff)
.collect(Collectors.toList());
/**
*
* TODO: Modify logic to retrieve correct charge-off transaction once reverse replay of charge-off is
* implemented
*/
LoanTransaction chargeOffTransaction = this.loanTransactions.stream().filter(LoanTransaction::isChargeOff).findFirst().get();
for (final LoanTransaction transaction : transactionsOnChargeOffDate) {
checkAndAddReversedTransactionOnChargeOffDate(currencyCode, transaction, existingTransactionIds, existingReversedTransactionIds,
newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, chargeOffTransaction);
checkAndAddNewTransactionOnChargeOffDate(currencyCode, transaction, existingTransactionIds, newLoanTransactionsBeforeChargeOff,
newLoanTransactionsAfterChargeOff, chargeOffTransaction);
checkAndAddChargeOffTransaction(currencyCode, transaction, existingTransactionIds, newLoanTransactionsAfterChargeOff);
}
}
private void checkAndAddReversedTransactionOnChargeOffDate(String currencyCode, LoanTransaction transaction,
List<Long> existingTransactionIds, List<Long> existingReversedTransactionIds,
List<Map<String, Object>> newLoanTransactionsBeforeChargeOff, List<Map<String, Object>> newLoanTransactionsAfterChargeOff,
LoanTransaction chargeOffTransaction) {
if (!transaction.isChargeOff()) {
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId())
&& !existingReversedTransactionIds.contains(transaction.getId())) {
compareWithChargeOffIdAndAddTransactionForAccountingData(currencyCode, newLoanTransactionsBeforeChargeOff,
newLoanTransactionsAfterChargeOff, transaction, chargeOffTransaction.getId());
}
}
}
private void checkAndAddReverseReplayedTransactionOnChargeOffDate(String currencyCode, LoanTransaction transaction,
List<Map<String, Object>> newLoanTransactionsBeforeChargeOff, List<Map<String, Object>> newLoanTransactionsAfterChargeOff,
LoanTransaction chargeOffTransaction) {
Predicate<LoanTransactionRelation> isReplayed = transactionRelation -> LoanTransactionRelationTypeEnum.REPLAYED
.equals(transactionRelation.getRelationType());
List<LoanTransactionRelation> replayedTransactionRelations = transaction.getLoanTransactionRelations().stream().filter(isReplayed)
.sorted(Comparator.comparing(LoanTransactionRelation::getToTransaction, Comparator.comparingLong(LoanTransaction::getId)))
.toList();
if (!replayedTransactionRelations.isEmpty()) {
// Transaction Id for first reversed transaction
Long transactionIdForReversedTransaction = replayedTransactionRelations.get(0).getToTransaction().getId();
if (transactionIdForReversedTransaction > chargeOffTransaction.getId()) {
newLoanTransactionsAfterChargeOff.add(transaction.toMapData(currencyCode));
} else {
newLoanTransactionsBeforeChargeOff.add(transaction.toMapData(currencyCode));
}
} else {
// new transaction
compareWithChargeOffIdAndAddTransactionForAccountingData(currencyCode, newLoanTransactionsBeforeChargeOff,
newLoanTransactionsAfterChargeOff, transaction, chargeOffTransaction.getId());
}
}
private void checkAndAddNewTransactionOnChargeOffDate(String currencyCode, LoanTransaction transaction,
List<Long> existingTransactionIds, List<Map<String, Object>> newLoanTransactionsBeforeChargeOff,
List<Map<String, Object>> newLoanTransactionsAfterChargeOff, LoanTransaction chargeOffTransaction) {
if (!transaction.isChargeOff()) {
if (!existingTransactionIds.contains(transaction.getId())) {
if (!transaction.getLoanTransactionRelations().isEmpty()) {
checkAndAddReverseReplayedTransactionOnChargeOffDate(currencyCode, transaction, newLoanTransactionsBeforeChargeOff,
newLoanTransactionsAfterChargeOff, chargeOffTransaction);
} else {
// new transaction
compareWithChargeOffIdAndAddTransactionForAccountingData(currencyCode, newLoanTransactionsBeforeChargeOff,
newLoanTransactionsAfterChargeOff, transaction, chargeOffTransaction.getId());
}
}
}
}
private void checkAndAddChargeOffTransaction(String currencyCode, LoanTransaction transaction, List<Long> existingTransactionIds,
List<Map<String, Object>> newLoanTransactionsAfterChargeOff) {
if (transaction.isChargeOff()) {
/**
*
* TODO: Modify logic for reverse replay of charge-off
*/
if (!existingTransactionIds.contains(transaction.getId())) {
newLoanTransactionsAfterChargeOff.add(transaction.toMapData(currencyCode));
}
}
}
private void compareWithChargeOffIdAndAddTransactionForAccountingData(final String currencyCode,
List<Map<String, Object>> newLoanTransactionsBeforeChargeOff, List<Map<String, Object>> newLoanTransactionsAfterChargeOff,
LoanTransaction transaction, Long chargeOffTransactionId) {
if (transaction.getId() > chargeOffTransactionId) {
newLoanTransactionsAfterChargeOff.add(transaction.toMapData(currencyCode));
} else {
newLoanTransactionsBeforeChargeOff.add(transaction.toMapData(currencyCode));
}
}
private void getTransactionsForAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, final List<Map<String, Object>> newLoanTransactions,
Predicate<LoanTransaction> chargeOffDateCriteria) { Predicate<LoanTransaction> chargeOffDateCriteria) {
Consumer<LoanTransaction> addTransactionForAccounting = transaction -> { filteredTransactions.addAll(this.loanTransactions.stream().filter(chargeOffDateCriteria).filter(transaction -> {
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId()) boolean isExistingTransaction = existingTransactionIds.contains(transaction.getId());
&& !existingReversedTransactionIds.contains(transaction.getId())) { boolean isExistingReversedTransaction = existingReversedTransactionIds.contains(transaction.getId());
newLoanTransactions.add(transaction.toMapData(currencyCode));
} else if (!existingTransactionIds.contains(transaction.getId())) { if (transaction.isReversed() && isExistingTransaction && !isExistingReversedTransaction) {
newLoanTransactions.add(transaction.toMapData(currencyCode)); return true;
} else {
return !isExistingTransaction;
} }
}; }).map(transaction -> transaction.toMapData(currencyCode)).toList());
this.loanTransactions.stream().filter(chargeOffDateCriteria).forEach(addTransactionForAccounting); }
private void filterTransactionsByChargeOffDate(List<Map<String, Object>> newLoanTransactionsBeforeChargeOff,
List<Map<String, Object>> newLoanTransactionsAfterChargeOff, String currencyCode, List<Long> existingTransactionIds,
List<Long> existingReversedTransactionIds, Predicate<LoanTransaction> chargeOffDateCriteria) {
LoanTransaction chargeOffTransaction = this.loanTransactions.stream().filter(LoanTransaction::isChargeOff)
.filter(LoanTransaction::isNotReversed).findFirst().get();
this.loanTransactions.stream().filter(chargeOffDateCriteria).forEach(transaction -> {
boolean isExistingTransaction = existingTransactionIds.contains(transaction.getId());
boolean isExistingReversedTransaction = existingReversedTransactionIds.contains(transaction.getId());
List<Map<String, Object>> targetList = (transaction.getId().compareTo(chargeOffTransaction.getId()) < 0)
? newLoanTransactionsBeforeChargeOff
: newLoanTransactionsAfterChargeOff;
if ((transaction.isReversed() && isExistingTransaction && !isExistingReversedTransaction) || !isExistingTransaction) {
targetList.add(transaction.toMapData(currencyCode));
}
});
} }
public Map<String, Object> deriveAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds, public Map<String, Object> deriveAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds,
@@ -189,7 +189,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings( loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(
newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings()); newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings());
} else { } else {
createNewTransactionIfNecessary(loanTransaction, newLoanTransaction, currency, changedTransactionDetail); createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
} }
} }
@@ -204,6 +204,8 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
} else if (loanTransaction.isChargeback()) { } else if (loanTransaction.isChargeback()) {
recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed); recalculateCreditTransaction(changedTransactionDetail, loanTransaction, currency, installments, transactionsToBeProcessed);
reprocessChargebackTransactionRelation(changedTransactionDetail, transactionsToBeProcessed); reprocessChargebackTransactionRelation(changedTransactionDetail, transactionsToBeProcessed);
} else if (loanTransaction.isChargeOff()) {
recalculateChargeOffTransaction(changedTransactionDetail, loanTransaction, currency, installments);
} }
} }
reprocessInstallments(installments, currency); reprocessInstallments(installments, currency);
@@ -211,6 +213,30 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
return changedTransactionDetail; return changedTransactionDetail;
} }
private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments) {
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
newLoanTransaction.resetDerivedComponents();
// determine how much is outstanding total and breakdown for principal, interest and charges
Money principalPortion = Money.zero(currency);
Money interestPortion = Money.zero(currency);
Money feeChargesPortion = Money.zero(currency);
Money penaltychargesPortion = Money.zero(currency);
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
if (currentInstallment.isNotFullyPaidOff()) {
principalPortion = principalPortion.plus(currentInstallment.getPrincipalOutstanding(currency));
interestPortion = interestPortion.plus(currentInstallment.getInterestOutstanding(currency));
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesOutstanding(currency));
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
}
}
newLoanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
}
}
private void reprocessChargebackTransactionRelation(ChangedTransactionDetail changedTransactionDetail, private void reprocessChargebackTransactionRelation(ChangedTransactionDetail changedTransactionDetail,
List<LoanTransaction> transactionsToBeProcessed) { List<LoanTransaction> transactionsToBeProcessed) {
@@ -260,7 +286,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
List<LoanTransaction> mergedList = getMergedTransactionList(transactionsToBeProcessed, changedTransactionDetail); List<LoanTransaction> mergedList = getMergedTransactionList(transactionsToBeProcessed, changedTransactionDetail);
Money overpaidAmount = calculateOverpaidAmount(loanTransaction, mergedList, installments, currency); Money overpaidAmount = calculateOverpaidAmount(loanTransaction, mergedList, installments, currency);
processCreditTransaction(newLoanTransaction, overpaidAmount, currency, installments); processCreditTransaction(newLoanTransaction, overpaidAmount, currency, installments);
createNewTransactionIfNecessary(loanTransaction, newLoanTransaction, currency, changedTransactionDetail); if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
}
} }
private List<LoanTransaction> getMergedTransactionList(List<LoanTransaction> transactionList, private List<LoanTransaction> getMergedTransactionList(List<LoanTransaction> transactionList,
@@ -270,17 +298,16 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
return mergedList; return mergedList;
} }
private void createNewTransactionIfNecessary(LoanTransaction loanTransaction, LoanTransaction newLoanTransaction, private void createNewTransaction(LoanTransaction loanTransaction, LoanTransaction newLoanTransaction,
MonetaryCurrency currency, ChangedTransactionDetail changedTransactionDetail) { ChangedTransactionDetail changedTransactionDetail) {
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) { loanTransaction.reverse();
loanTransaction.reverse(); loanTransaction.updateExternalId(null);
loanTransaction.updateExternalId(null); newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations()); // Adding Replayed relation from newly created transaction to reversed transaction
// Adding Replayed relation from newly created transaction to reversed transaction newLoanTransaction.getLoanTransactionRelations().add(
newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction, LoanTransactionRelation.linkToTransaction(newLoanTransaction, loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED)); changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction);
}
} }
private Money calculateOverpaidAmount(LoanTransaction loanTransaction, List<LoanTransaction> transactions, private Money calculateOverpaidAmount(LoanTransaction loanTransaction, List<LoanTransaction> transactions,
@@ -58,15 +58,20 @@ import org.apache.fineract.integrationtests.common.funds.FundsHelper;
import org.apache.fineract.integrationtests.common.funds.FundsResourceHandler; import org.apache.fineract.integrationtests.common.funds.FundsResourceHandler;
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanProductHelper; import org.apache.fineract.integrationtests.common.loans.LoanProductHelper;
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper; import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
import org.apache.fineract.integrationtests.common.system.CodeHelper; import org.apache.fineract.integrationtests.common.system.CodeHelper;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(LoanTestLifecycleExtension.class)
public class LoanPostChargeOffScenariosTest { public class LoanPostChargeOffScenariosTest {
private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
private ResponseSpecification responseSpec; private ResponseSpecification responseSpec;
private RequestSpecification requestSpec; private RequestSpecification requestSpec;
private ClientHelper clientHelper; private ClientHelper clientHelper;
@@ -77,33 +82,23 @@ public class LoanPostChargeOffScenariosTest {
// asset // asset
private Account loansReceivable; private Account loansReceivable;
private Account interestFeeReceivable; private Account interestFeeReceivable;
private Account otherReceivables;
private Account uncReceivable;
private Account suspenseAccount; private Account suspenseAccount;
private Account fundReceivables; private Account fundReceivables;
// liability // liability
private Account aaSuspenseBalance;
private Account suspenseClearingAccount; private Account suspenseClearingAccount;
private Account overpaymentAccount; private Account overpaymentAccount;
// income // income
private Account deferredInterestRevenue;
private Account retainedEarningsPriorYear;
private Account interestIncome; private Account interestIncome;
private Account feeIncome; private Account feeIncome;
private Account feeChargeOff; private Account feeChargeOff;
private Account recoveries; private Account recoveries;
private Account interestIncomeChargeOff; private Account interestIncomeChargeOff;
// expense // expense
private Account creditLossBadDebt; private Account creditLossBadDebt;
private Account creditLossBadDebtFraud; private Account creditLossBadDebtFraud;
private Account writtenOff; private Account writtenOff;
private Account goodwillExpenseAccount; private Account goodwillExpenseAccount;
private DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
@BeforeEach @BeforeEach
public void setup() { public void setup() {
Utils.initializeRESTAssured(); Utils.initializeRESTAssured();
@@ -117,19 +112,14 @@ public class LoanPostChargeOffScenariosTest {
// Asset // Asset
this.loansReceivable = this.accountHelper.createAssetAccount(); this.loansReceivable = this.accountHelper.createAssetAccount();
this.interestFeeReceivable = this.accountHelper.createAssetAccount(); this.interestFeeReceivable = this.accountHelper.createAssetAccount();
this.otherReceivables = this.accountHelper.createAssetAccount();
this.uncReceivable = this.accountHelper.createAssetAccount();
this.suspenseAccount = this.accountHelper.createAssetAccount(); this.suspenseAccount = this.accountHelper.createAssetAccount();
this.fundReceivables = this.accountHelper.createAssetAccount(); this.fundReceivables = this.accountHelper.createAssetAccount();
// Liability // Liability
this.aaSuspenseBalance = this.accountHelper.createLiabilityAccount();
this.suspenseClearingAccount = this.accountHelper.createLiabilityAccount(); this.suspenseClearingAccount = this.accountHelper.createLiabilityAccount();
this.overpaymentAccount = this.accountHelper.createLiabilityAccount(); this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
// income // income
this.deferredInterestRevenue = this.accountHelper.createIncomeAccount();
this.retainedEarningsPriorYear = this.accountHelper.createIncomeAccount();
this.interestIncome = this.accountHelper.createIncomeAccount(); this.interestIncome = this.accountHelper.createIncomeAccount();
this.feeIncome = this.accountHelper.createIncomeAccount(); this.feeIncome = this.accountHelper.createIncomeAccount();
this.feeChargeOff = this.accountHelper.createIncomeAccount(); this.feeChargeOff = this.accountHelper.createIncomeAccount();
@@ -158,7 +148,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));
@@ -328,7 +318,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));
@@ -452,7 +442,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));
@@ -586,7 +576,8 @@ public class LoanPostChargeOffScenariosTest {
} }
@Test @Test
public void transactionOnChargeOfDatePreChargeOffReverseReplayTest() { @Disabled("Requires: FINERACT-1946")
public void transactionOnChargeOffDatePreChargeOffReverseReplayTest() {
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccounting(); final Integer loanProductID = createLoanProductWithPeriodicAccrualAccounting();
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
@@ -597,7 +588,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));
@@ -730,7 +721,8 @@ public class LoanPostChargeOffScenariosTest {
} }
@Test @Test
public void transactionOnChargeOfDatePostChargeOffReverseReplayTest() { @Disabled("Requires: FINERACT-1946")
public void transactionOnChargeOffDatePostChargeOffReverseReplayTest() {
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
final Integer loanProductID = createLoanProductWithPeriodicAccrualAccounting(); final Integer loanProductID = createLoanProductWithPeriodicAccrualAccounting();
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
@@ -741,7 +733,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));
@@ -885,7 +877,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));
@@ -992,7 +984,7 @@ public class LoanPostChargeOffScenariosTest {
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false));
LocalDate targetDate = LocalDate.of(2022, 9, 5); LocalDate targetDate = LocalDate.of(2022, 9, 5);
final String feeCharge1AddedDate = dateFormatter.format(targetDate); final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate);
Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId,
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10"));