in fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java [80:185]
public void handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final LoanTransaction loanTransaction,
final LoanTransaction adjustedTransaction, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
if (loanTransaction.isRecoveryRepayment()) {
loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, loan);
}
if (loanTransaction.isRecoveryRepayment()
&& loanTransaction.getAmount(loan.getCurrency()).getAmount().compareTo(loan.getSummary().getTotalWrittenOff()) > 0) {
final String errorMessage = "The transaction amount cannot greater than the remaining written off amount.";
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage);
}
loanTransaction.updateLoan(loan);
final boolean isTransactionChronologicallyLatest = loan.isChronologicallyLatestRepaymentOrWaiver(loanTransaction);
if (loanTransaction.isNotZero()) {
loan.addLoanTransaction(loanTransaction);
}
if (loanTransaction.isNotRepaymentLikeType() && loanTransaction.isNotWaiver() && loanTransaction.isNotRecoveryRepayment()) {
final String errorMessage = "A transaction of type repayment or recovery repayment or waiver was expected but not received.";
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.or.waiver.or.recovery.transaction",
errorMessage);
}
final LocalDate loanTransactionDate = loanRefundService.extractTransactionDate(loan, loanTransaction);
if (DateUtils.isDateInTheFuture(loanTransactionDate)) {
final String errorMessage = "The transaction date cannot be in the future.";
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.a.future.date", errorMessage, loanTransactionDate);
}
if (loanTransaction.isInterestWaiver()) {
Money totalInterestOutstandingOnLoan = loan.getTotalInterestOutstandingOnLoan();
if (adjustedTransaction != null) {
totalInterestOutstandingOnLoan = totalInterestOutstandingOnLoan.plus(adjustedTransaction.getAmount(loan.loanCurrency()));
}
if (loanTransaction.getAmount(loan.getCurrency()).isGreaterThan(totalInterestOutstandingOnLoan)) {
final String errorMessage = "The amount of interest to waive cannot be greater than total interest outstanding on loan.";
throw new InvalidLoanStateTransitionException("waive.interest", "amount.exceeds.total.outstanding.interest", errorMessage,
loanTransaction.getAmount(loan.getCurrency()), totalInterestOutstandingOnLoan.getAmount());
}
}
loanRefundValidator.validateTransactionAmountThreshold(loan, adjustedTransaction);
final LoanRepaymentScheduleInstallment currentInstallment = loan
.fetchLoanRepaymentScheduleInstallmentByDueDate(loanTransaction.getTransactionDate());
boolean reprocess = loan.isForeclosure() || !isTransactionChronologicallyLatest || adjustedTransaction != null
|| !DateUtils.isEqualBusinessDate(loanTransaction.getTransactionDate()) || currentInstallment == null
|| !currentInstallment.getTotalOutstanding(loan.getCurrency()).isEqualTo(loanTransaction.getAmount(loan.getCurrency()));
// TODO FINERACT-2220 fix processLatestTransaction to save model.
if (loan.isProgressiveSchedule() && loan.isInterestBearing()) {
reprocess = true;
}
if (isTransactionChronologicallyLatest && adjustedTransaction == null
&& (!reprocess || !loan.isInterestBearingAndInterestRecalculationEnabled()) && !loan.isForeclosure()) {
loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction,
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
reprocess = false;
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) {
reprocess = true;
} else {
final LoanRepaymentScheduleInstallment nextInstallment = loan
.fetchRepaymentScheduleInstallment(currentInstallment.getInstallmentNumber() + 1);
if (nextInstallment != null && nextInstallment.getTotalPaidInAdvance(loan.getCurrency()).isGreaterThanZero()) {
reprocess = true;
}
}
}
}
if (reprocess) {
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
} else if (loan.isProgressiveSchedule() && loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy()) {
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
}
reprocessLoanTransactionsService.reprocessTransactions(loan);
}
/**
* FIXME: Vishwas, skipping post loan transaction checks for Loan recoveries
**/
if (loanTransaction.isNotRecoveryRepayment()) {
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
} else {
loan.updateLoanSummaryDerivedFields();
}
if (loan.getLoanProduct().isMultiDisburseLoan()) {
final BigDecimal totalDisbursed = loan.getDisbursedAmount();
final BigDecimal totalPrincipalAdjusted = loan.getSummary().getTotalPrincipalAdjustments();
final BigDecimal totalPrincipalCredited = totalDisbursed.add(totalPrincipalAdjusted);
if (totalPrincipalCredited.compareTo(loan.getSummary().getTotalPrincipalRepaid()) < 0
&& loan.repaymentScheduleDetail().getPrincipal().minus(totalDisbursed).isGreaterThanZero()) {
final String errorMessage = "The transaction amount cannot exceed threshold.";
throw new InvalidLoanStateTransitionException("transaction", "amount.exceeds.threshold", errorMessage);
}
}
}