FINERACT-2181: Move the remaining transactions reprocessing logic out of the loan entity to ReprocessLoanTransactionsService.

This commit is contained in:
mariiaKraievska
2025-03-25 12:09:28 +01:00
committed by Adam Saghy
parent f3bb66f209
commit 2d2cbb46b5
5 changed files with 57 additions and 76 deletions
@@ -713,28 +713,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_ADDED, this); loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_ADDED, this);
} }
public ChangedTransactionDetail reprocessTransactions() {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = getTransactionProcessor();
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retrieveListOfTransactionsForReprocessing();
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
getActiveCharges());
for (TransactionChangeData change : changedTransactionDetail.getTransactionChanges()) {
change.getNewTransaction().updateLoan(this);
}
final List<LoanTransaction> newTransactions = changedTransactionDetail.getTransactionChanges().stream()
.map(TransactionChangeData::getNewTransaction).toList();
this.loanTransactions.addAll(newTransactions);
updateLoanSummaryDerivedFields();
return changedTransactionDetail;
}
public ChangedTransactionDetail reprocessTransactionsWithPostTransactionChecks(LocalDate transactionDate) {
ChangedTransactionDetail changedDetail = reprocessTransactions();
doPostLoanTransactionChecks(transactionDate, loanLifecycleStateMachine);
return changedDetail;
}
/** /**
* Creates a loanTransaction for "Apply Charge Event" with transaction date set to "suppliedTransactionDate". The * Creates a loanTransaction for "Apply Charge Event" with transaction date set to "suppliedTransactionDate". The
* newly created transaction is also added to the Loan on which this method is called. * newly created transaction is also added to the Loan on which this method is called.
@@ -832,34 +810,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
return lastRepaymentDate; return lastRepaymentDate;
} }
public Optional<ChangedTransactionDetail> removeLoanCharge(final LoanCharge loanCharge) { public void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final LoanCharge loanCharge) {
final boolean removed = loanCharge.isActive();
if (removed) {
loanCharge.setActive(false);
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), getActiveCharges());
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
}
removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loanCharge);
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(getCurrency())) {
/*
* TODO Vishwas Currently we do not allow removing a loan charge after a loan is approved (hence there is no
* need to adjust any loan transactions).
*
* Consider removing this block of code or logically completing it for the future by getting the list of
* affected Transactions
*/
return Optional.ofNullable(reprocessTransactions());
}
this.charges.remove(loanCharge);
updateLoanSummaryDerivedFields();
return Optional.empty();
}
private void removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(final LoanCharge loanCharge) {
if (loanCharge.isDueAtDisbursement()) { if (loanCharge.isDueAtDisbursement()) {
LoanTransaction transactionToRemove = null; LoanTransaction transactionToRemove = null;
List<LoanTransaction> transactions = getLoanTransactions(); List<LoanTransaction> transactions = getLoanTransactions();
@@ -28,7 +28,7 @@ public interface ReprocessLoanTransactionsService {
void reprocessTransactions(Loan loan); void reprocessTransactions(Loan loan);
void reprocessTransactions(Loan loan, List<LoanTransaction> loanTransactions); void reprocessParticularTransactions(Loan loan, List<LoanTransaction> loanTransactions);
void reprocessTransactionsWithPostTransactionChecks(Loan loan, LocalDate transactionDate); void reprocessTransactionsWithPostTransactionChecks(Loan loan, LocalDate transactionDate);
@@ -27,6 +27,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService; import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
@@ -43,34 +44,24 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
@Override @Override
public void reprocessTransactions(final Loan loan) { public void reprocessTransactions(final Loan loan) {
final ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactions(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = loan.retrieveListOfTransactionsForReprocessing();
if (changedTransactionDetail != null) { final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan,
handleChangedDetail(changedTransactionDetail); allNonContraTransactionsPostDisbursement);
} handleChangedDetail(changedTransactionDetail);
} }
@Override @Override
public void reprocessTransactions(final Loan loan, final List<LoanTransaction> loanTransactions) { public void reprocessParticularTransactions(final Loan loan, final List<LoanTransaction> loanTransactions) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor(); final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan, loanTransactions);
final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
loan.getDisbursementDate(), loanTransactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
loan.getActiveCharges());
for (TransactionChangeData change : changedTransactionDetail.getTransactionChanges()) {
change.getNewTransaction().updateLoan(loan);
}
final List<LoanTransaction> newTransactions = changedTransactionDetail.getTransactionChanges().stream()
.map(TransactionChangeData::getNewTransaction).toList();
loan.getLoanTransactions().addAll(newTransactions);
loan.updateLoanSummaryDerivedFields();
handleChangedDetail(changedTransactionDetail); handleChangedDetail(changedTransactionDetail);
} }
@Override @Override
public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) { public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) {
final ChangedTransactionDetail changedTransactionDetail = loan.reprocessTransactionsWithPostTransactionChecks(transactionDate); final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan,
if (changedTransactionDetail != null) { loan.retrieveListOfTransactionsForReprocessing());
handleChangedDetail(changedTransactionDetail); loan.doPostLoanTransactionChecks(transactionDate, loan.getLoanLifecycleStateMachine());
} handleChangedDetail(changedTransactionDetail);
} }
@Override @Override
@@ -80,7 +71,30 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
@Override @Override
public void removeLoanCharge(final Loan loan, final LoanCharge loanCharge) { public void removeLoanCharge(final Loan loan, final LoanCharge loanCharge) {
loan.removeLoanCharge(loanCharge).ifPresent(this::handleChangedDetail); final boolean removed = loanCharge.isActive();
if (removed) {
loanCharge.setActive(false);
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
wrapper.reprocess(loan.getCurrency(), loan.getDisbursementDate(), loan.getRepaymentScheduleInstallments(),
loan.getActiveCharges());
loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement());
}
loan.removeOrModifyTransactionAssociatedWithLoanChargeIfDueAtDisbursement(loanCharge);
if (!loanCharge.isDueAtDisbursement() && loanCharge.isPaidOrPartiallyPaid(loan.getCurrency())) {
/*
* TODO Vishwas Currently we do not allow removing a loan charge after a loan is approved (hence there is no
* need to adjust any loan transactions).
*
* Consider removing this block of code or logically completing it for the future by getting the list of
* affected Transactions
*/
reprocessTransactions(loan);
return;
}
loan.getLoanCharges().remove(loanCharge);
loan.updateLoanSummaryDerivedFields();
} }
@Override @Override
@@ -109,4 +123,20 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
} }
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
} }
private ChangedTransactionDetail reprocessTransactionsAndFetchChangedTransactions(final Loan loan,
final List<LoanTransaction> loanTransactions) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor();
final ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(
loan.getDisbursementDate(), loanTransactions, loan.getCurrency(), loan.getRepaymentScheduleInstallments(),
loan.getActiveCharges());
for (TransactionChangeData change : changedTransactionDetail.getTransactionChanges()) {
change.getNewTransaction().updateLoan(loan);
}
final List<LoanTransaction> newTransactions = changedTransactionDetail.getTransactionChanges().stream()
.map(TransactionChangeData::getNewTransaction).toList();
loan.getLoanTransactions().addAll(newTransactions);
loan.updateLoanSummaryDerivedFields();
return changedTransactionDetail;
}
} }
@@ -2918,7 +2918,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
} }
final List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsForReprocessing(); final List<LoanTransaction> loanTransactions = loan.retrieveListOfTransactionsForReprocessing();
loanTransactions.add(chargeOffTransaction); loanTransactions.add(chargeOffTransaction);
reprocessLoanTransactionsService.reprocessTransactions(loan, loanTransactions); reprocessLoanTransactionsService.reprocessParticularTransactions(loan, loanTransactions);
loan.addLoanTransaction(chargeOffTransaction); loan.addLoanTransaction(chargeOffTransaction);
} else { } else {
reprocessLoanTransactionsService.processLatestTransaction(chargeOffTransaction, loan); reprocessLoanTransactionsService.processLatestTransaction(chargeOffTransaction, loan);
@@ -130,11 +130,11 @@ public class LoanReAmortizationServiceImpl {
private void reverseReAmortizeTransaction(LoanTransaction reAmortizeTransaction, JsonCommand command) { private void reverseReAmortizeTransaction(LoanTransaction reAmortizeTransaction, JsonCommand command) {
ExternalId reversalExternalId = externalIdFactory.createFromCommand(command, ExternalId reversalExternalId = externalIdFactory.createFromCommand(command,
LoanReAmortizationApiConstants.externalIdParameterName); LoanReAmortizationApiConstants.externalIdParameterName);
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(reAmortizeTransaction.getLoan(), reAmortizeTransaction, final Loan loan = reAmortizeTransaction.getLoan();
"reversed"); loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loan, reAmortizeTransaction, "reversed");
reAmortizeTransaction.reverse(reversalExternalId); reAmortizeTransaction.reverse(reversalExternalId);
reAmortizeTransaction.manuallyAdjustedOrReversed(); reAmortizeTransaction.manuallyAdjustedOrReversed();
reprocessLoanTransactionsService.reprocessTransactions(reAmortizeTransaction.getLoan()); reprocessLoanTransactionsService.reprocessTransactions(loan);
} }
private LoanTransaction findLatestNonReversedReAmortizeTransaction(Loan loan) { private LoanTransaction findLatestNonReversedReAmortizeTransaction(Loan loan) {