FINERACT-2104: Accrual Activity Reverse Replay Transaction

This commit is contained in:
Soma Sörös
2024-07-15 10:31:28 +02:00
committed by Adam Saghy
parent a4f077d675
commit 9fceaf98b3
8 changed files with 398 additions and 24 deletions
@@ -2481,24 +2481,19 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
public List<LoanTransaction> retrieveListOfTransactionsForReprocessing() {
return getLoanTransactions().stream()
.filter(transaction -> transaction.isNotReversed() && !transaction.isAccrual()
&& (transaction.isChargeOff() || transaction.isReAge() || transaction.isReAmortize()
|| !transaction.isNonMonetaryTransaction()))
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
return getLoanTransactions().stream().filter(loanTransactionForReprocessingPredicate()).sorted(LoanTransactionComparator.INSTANCE)
.collect(Collectors.toList());
}
public List<LoanTransaction> retrieveListOfTransactionsPostDisbursementExcludeAccruals() {
return this.loanTransactions.stream()
.filter(transaction -> transaction.isNotReversed() && !(transaction.isAccrual() || transaction.isRepaymentAtDisbursement()
|| transaction.isNonMonetaryTransaction() || transaction.isIncomePosting()))
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
private static Predicate<LoanTransaction> loanTransactionForReprocessingPredicate() {
return transaction -> transaction.isNotReversed() && (transaction.isChargeOff() || transaction.isReAge()
|| transaction.isAccrualActivity() || transaction.isReAmortize() || !transaction.isNonMonetaryTransaction());
}
private List<LoanTransaction> retrieveListOfTransactionsExcludeAccruals() {
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
for (final LoanTransaction transaction : this.loanTransactions) {
if (transaction.isNotReversed() && !(transaction.isAccrual() || transaction.isNonMonetaryTransaction())) {
if (transaction.isNotReversed() && !transaction.isNonMonetaryTransaction()) {
repaymentsOrWaivers.add(transaction);
}
}
@@ -3901,6 +3896,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return currentTransactionDate;
}
public LoanTransaction getLastTransactionForReprocessing() {
return loanTransactions.stream() //
.filter(Loan.loanTransactionForReprocessingPredicate()) //
.reduce((first, second) -> second) //
.orElse(null);
}
public LoanTransaction getLastPaymentTransaction() {
return loanTransactions.stream() //
.filter(loanTransaction -> !loanTransaction.isReversed()) //
@@ -4526,7 +4528,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
outstanding = outstanding.plus(transactionOutstanding);
loanTransaction.updateOutstandingLoanBalance(MathUtil.negativeToZero(outstanding.getAmount()));
} else {
} else if (!loanTransaction.isAccrualActivity()) {
if (this.loanInterestRecalculationDetails != null
&& this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
&& !loanTransaction.isRepaymentAtDisbursement()) {
@@ -879,11 +879,11 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public boolean isNonMonetaryTransaction() {
return isNotReversed() && (LoanTransactionType.CONTRA.equals(getTypeOf())
|| LoanTransactionType.MARKED_FOR_RESCHEDULING.equals(getTypeOf())
|| LoanTransactionType.APPROVE_TRANSFER.equals(getTypeOf()) || LoanTransactionType.INITIATE_TRANSFER.equals(getTypeOf())
|| LoanTransactionType.REJECT_TRANSFER.equals(getTypeOf()) || LoanTransactionType.WITHDRAW_TRANSFER.equals(getTypeOf())
|| LoanTransactionType.CHARGE_OFF.equals(getTypeOf()) || LoanTransactionType.REAMORTIZE.equals(getTypeOf())
|| LoanTransactionType.REAGE.equals(getTypeOf()));
|| LoanTransactionType.MARKED_FOR_RESCHEDULING.equals(getTypeOf()) || LoanTransactionType.ACCRUAL.equals(getTypeOf())
|| LoanTransactionType.ACCRUAL_ACTIVITY.equals(getTypeOf()) || LoanTransactionType.APPROVE_TRANSFER.equals(getTypeOf())
|| LoanTransactionType.INITIATE_TRANSFER.equals(getTypeOf()) || LoanTransactionType.REJECT_TRANSFER.equals(getTypeOf())
|| LoanTransactionType.WITHDRAW_TRANSFER.equals(getTypeOf()) || LoanTransactionType.CHARGE_OFF.equals(getTypeOf())
|| LoanTransactionType.REAMORTIZE.equals(getTypeOf()) || LoanTransactionType.REAGE.equals(getTypeOf()));
}
public void updateOutstandingLoanBalance(BigDecimal outstandingLoanBalance) {
@@ -949,7 +949,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
}
public Boolean isAllowTypeTransactionAtTheTimeOfLastUndo() {
return isDisbursement() || isAccrual() || isRepaymentAtDisbursement() || isRepayment();
return isDisbursement() || isAccrual() || isRepaymentAtDisbursement() || isRepayment() || isAccrualActivity();
}
public boolean isAccrualTransaction() {
@@ -969,8 +969,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
}
public boolean isPaymentTransaction() {
return this.isNotReversed() && !(this.isDisbursement() || this.isAccrual() || this.isRepaymentAtDisbursement()
|| this.isNonMonetaryTransaction() || this.isIncomePosting());
return this.isNotReversed() && !(this.isDisbursement() || this.isRepaymentAtDisbursement() || this.isNonMonetaryTransaction()
|| this.isIncomePosting());
}
public Set<LoanCollateralManagement> getLoanCollateralManagementSet() {
@@ -204,12 +204,43 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
reprocessChargebackTransactionRelation(changedTransactionDetail, transactionsToBeProcessed);
} else if (loanTransaction.isChargeOff()) {
recalculateChargeOffTransaction(changedTransactionDetail, loanTransaction, currency, installments);
} else if (loanTransaction.isAccrualActivity()) {
recalculateAccrualActivityTransaction(changedTransactionDetail, loanTransaction, currency, installments);
}
}
reprocessInstallments(disbursementDate, transactionsToBeProcessed, installments, currency);
return changedTransactionDetail;
}
protected void calculateAccrualActivity(LoanTransaction loanTransaction, MonetaryCurrency currency,
List<LoanRepaymentScheduleInstallment> installments) {
loanTransaction.resetDerivedComponents();
// determine how much is outstanding total and breakdown for principal, interest and charges
final 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 (loanTransaction.getDateOf().isEqual(currentInstallment.getDueDate())) {
interestPortion = interestPortion.plus(currentInstallment.getInterestCharged(currency));
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesCharged(currency));
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
}
}
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
}
private void recalculateAccrualActivityTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments) {
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
calculateAccrualActivity(newLoanTransaction, currency, installments);
if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
createNewTransaction(loanTransaction, newLoanTransaction, changedTransactionDetail);
}
}
@Override
public void processLatestTransaction(final LoanTransaction loanTransaction, final TransactionCtx ctx) {
switch (loanTransaction.getTypeOf()) {
@@ -2785,7 +2785,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, loan, holidayDetailDTO,
loanRepaymentScheduleTransactionProcessor, onDate, calculateTill);
List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsPostDisbursementExcludeAccruals();
List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsForReprocessing();
loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(loanApplicationTerms.getExpectedDisbursementDate(),
loanTransactions, currency, loanScheduleDTO.getInstallments(), loan.getActiveCharges());