FINERACT-1724: Add final accrual when loan is overpaid

This commit is contained in:
Adam Saghy
2023-04-21 14:20:03 +02:00
committed by Arnold Gálovics
parent 0e3ad8f73b
commit cefcb53424
8 changed files with 131 additions and 44 deletions
@@ -3423,7 +3423,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
boolean isOverpaid = getTotalOverpaid() != null && getTotalOverpaid().compareTo(BigDecimal.ZERO) > 0;
if (isOverpaid) {
// FIXME - kw - update account balance to negative amount.
handleLoanOverpayment(loanLifecycleStateMachine);
handleLoanOverpayment(transactionDate, loanLifecycleStateMachine);
statusChanged = true;
} else if (this.summary.isRepaidInFull(loanCurrency())) {
handleLoanRepaymentInFull(transactionDate, loanLifecycleStateMachine);
@@ -3536,10 +3536,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
incomeDetailsMap.put(PENALTY, penalty);
}
private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
private void handleLoanOverpayment(LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
this.overpaidOnDate = transactionDate;
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, this);
this.overpaidOnDate = DateUtils.getBusinessLocalDate();
this.closedOnDate = null;
this.actualMaturityDate = null;
}
@@ -99,5 +99,5 @@ public interface LoanAccountDomainService {
LoanTransaction creditBalanceRefund(Loan loan, LocalDate transactionDate, BigDecimal transactionAmount, String noteText,
ExternalId externalId, PaymentDetail paymentDetail);
void applyIncomeAccrualTransaction(Loan loan);
void applyFinalIncomeAccrualTransaction(Loan loan);
}
@@ -877,7 +877,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
}
@Override
public void applyIncomeAccrualTransaction(Loan loan) {
public void applyFinalIncomeAccrualTransaction(Loan loan) {
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()
// to avoid collision with processIncomeAccrualTransactionOnLoanClosure()
&& !(loan.getLoanInterestRecalculationDetails() != null
@@ -904,7 +904,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
if (total.isGreaterThanZero()) {
ExternalId externalId = externalIdFactory.create();
LoanTransaction accrualTransaction = LoanTransaction.accrueTransaction(loan, loan.getOffice(), loan.getClosedOnDate(),
LocalDate accrualTransactionDate = getFinalAccrualTransactionDate(loan);
LoanTransaction accrualTransaction = LoanTransaction.accrueTransaction(loan, loan.getOffice(), accrualTransactionDate,
total.getAmount(), interestPortion.getAmount(), feePortion.getAmount(), penaltyPortion.getAmount(), externalId);
Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid();
@@ -967,4 +969,12 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
}
}
private LocalDate getFinalAccrualTransactionDate(Loan loan) {
return switch (loan.getStatus()) {
case CLOSED_OBLIGATIONS_MET -> loan.getClosedOnDate();
case OVERPAID -> loan.getOverpaidOnDate();
default -> throw new IllegalStateException("Unexpected value: " + loan.getStatus());
};
}
}
@@ -47,9 +47,9 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
final Loan loan = event.get();
log.debug("Loan Status change for loan {}", loan.getId());
if (loan.getStatus().isClosedObligationsMet()) {
if (loan.getStatus().isClosedObligationsMet() || loan.getStatus().isOverpaid()) {
log.debug("Loan Status {} for loan {}", loan.getStatus().getCode(), loan.getId());
loanAccountDomainService.applyIncomeAccrualTransaction(loan);
loanAccountDomainService.applyFinalIncomeAccrualTransaction(loan);
}
if (loan.isOpen()) {
loan.handleMaturityDateActivate();
@@ -1229,6 +1229,19 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
LoanTransaction loanTransaction = this.loanTransactionRepository.findByIdAndLoanId(command.entityId(), command.getLoanId())
.orElseThrow(() -> new LoanTransactionNotFoundException(command.entityId(), command.getLoanId()));
if (loanTransaction.isReversed()) {
throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
"Loan transaction:" + transactionId + " chargeback not allowed as loan transaction repayment is reversed",
transactionId);
}
if (!loanTransaction.isRepaymentLikeType()) {
throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
"Loan transaction:" + transactionId + " chargeback not allowed as loan transaction is not repayment type, its type is "
+ loanTransaction.getTypeOf().getCode(),
transactionId);
}
Loan loan = this.loanAssembler.assembleFrom(loanId);
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)) {
throw new PlatformServiceUnavailableException("error.msg.loan.transfer.transaction.update.not.allowed",
@@ -1243,22 +1256,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
"Loan transaction:" + transactionId + " chargeback not allowed as loan product is interest recalculation enabled",
transactionId);
}
checkClientOrGroupActive(loan);
if (loanTransaction.isReversed()) {
throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
"Loan transaction:" + transactionId + " chargeback not allowed as loan transaction repayment is reversed",
transactionId);
}
if (!loanTransaction.isRepayment()) {
throw new PlatformServiceUnavailableException(
"error.msg.loan.chargeback.operation.not.allowed", "Loan transaction:" + transactionId
+ " chargeback not allowed as loan transaction is not repayment, is " + loanTransaction.getTypeOf().getCode(),
transactionId);
}
final List<Long> existingTransactionIds = loan.findExistingTransactionIds();
final List<Long> existingReversedTransactionIds = loan.findExistingReversedTransactionIds();