FINERACT-2104: Accrual Activity - Support final Accrual Activity transaction creation at the event of the loan got fully paid

This commit is contained in:
Soma Sörös
2024-07-24 14:48:34 +02:00
committed by Adam Saghy
parent 602ad4e953
commit b1798a66df
11 changed files with 851 additions and 64 deletions
@@ -3877,7 +3877,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public LocalDate getLastUserTransactionDate() {
LocalDate currentTransactionDate = getDisbursementDate();
for (final LoanTransaction previousTransaction : this.loanTransactions) {
if (!(previousTransaction.isReversed() || previousTransaction.isAccrual() || previousTransaction.isIncomePosting())
if (!(previousTransaction.isReversed() || previousTransaction.isAccrual() || previousTransaction.isIncomePosting()
|| previousTransaction.isAccrualActivity())
&& DateUtils.isBefore(currentTransactionDate, previousTransaction.getTransactionDate())) {
currentTransactionDate = previousTransaction.getTransactionDate();
}
@@ -618,22 +618,12 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public boolean isDueForCollectionFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
final LocalDate dueDate = getDueLocalDate();
return occursOnDayFromExclusiveAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
return DateUtils.occursOnDayFromExclusiveAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
}
public boolean isDueForCollectionFromIncludingAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive) {
final LocalDate dueDate = getDueLocalDate();
return occursOnDayFromAndUpToAndIncluding(fromAndInclusive, upToAndInclusive, dueDate);
}
private boolean occursOnDayFromExclusiveAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
final LocalDate target) {
return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
}
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive,
final LocalDate target) {
return target != null && !DateUtils.isBefore(target, fromAndInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
return DateUtils.occursOnDayFromAndUpToAndIncluding(fromAndInclusive, upToAndInclusive, dueDate);
}
public boolean isFeeCharge() {
@@ -220,13 +220,20 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
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));
}
}
final int firstNormalInstallmentNumber = LoanRepaymentScheduleProcessingWrapper.fetchFirstNormalInstallmentNumber(installments);
final LoanRepaymentScheduleInstallment currentInstallment = installments.stream()
.filter(installment -> installment.getInstallmentNumber().equals(firstNormalInstallmentNumber)
? DateUtils.occursOnDayFromExclusiveAndUpToAndIncluding(installment.getFromDate(), installment.getDueDate(),
loanTransaction.getTransactionDate())
: DateUtils.occursOnDayFromAndUpToAndIncluding(installment.getFromDate(), installment.getDueDate(),
loanTransaction.getTransactionDate()))
.findFirst().orElseThrow();
interestPortion = interestPortion.plus(currentInstallment.getInterestCharged(currency));
feeChargesPortion = feeChargesPortion.plus(currentInstallment.getFeeChargesCharged(currency));
penaltychargesPortion = penaltychargesPortion.plus(currentInstallment.getPenaltyChargesCharged(currency));
loanTransaction.updateComponentsAndTotal(principalPortion, interestPortion, feeChargesPortion, penaltychargesPortion);
}
@@ -602,7 +609,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
installmentNumber = installments.get(0).getInstallmentNumber();
}
if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual()) {
if (loanTransaction.isNotWaiver() && !loanTransaction.isAccrual() && !loanTransaction.isAccrualActivity()) {
Money feeCharges = loanTransaction.getFeeChargesPortion(currency);
Money penaltyCharges = loanTransaction.getPenaltyChargesPortion(currency);
if (chargeAmountToProcess != null && feeCharges.isGreaterThan(chargeAmountToProcess)) {
@@ -29,4 +29,9 @@ public interface LoanAccrualActivityProcessingService {
Loan makeAccrualActivityTransaction(Loan loan, LocalDate currentDate);
@Transactional
void processAccrualActivityForLoanClosure(Loan loan);
@Transactional
void processAccrualActivityForLoanReopen(Loan loan);
}
@@ -53,9 +53,16 @@ public interface LoanWritePlatformService {
CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
@Transactional
Loan reverseReplayAccrualActivityTransaction(Loan loan, LoanTransaction loanTransaction, LoanRepaymentScheduleInstallment installment,
LocalDate transactionDate);
@Transactional
Loan makeAccrualActivityTransaction(Loan loan, LoanRepaymentScheduleInstallment installment, LocalDate transactionDate);
@Transactional
Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction accrualActivityTransaction);
@Transactional
CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);