FINERACT-2181: Accruals on accounts where charge adjustments and refunds are done on the same day causing duplicate journal entries
This commit is contained in:
committed by
Adam Saghy
parent
5649aa3ded
commit
496e37e3d4
+1
-1
@@ -39,7 +39,7 @@ public class CapitalizedIncomeAmortizationBusinessStep implements LoanCOBBusines
|
||||
public Loan execute(Loan loan) {
|
||||
LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationTillDate(loan, businessDate);
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationTillDate(loan, businessDate, true);
|
||||
|
||||
return loan;
|
||||
}
|
||||
|
||||
+11
-12
@@ -280,16 +280,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
setLoanDelinquencyTag(loan, transactionDate);
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
if (!repaymentTransactionType.isChargeRefund()) {
|
||||
LoanTransactionBusinessEvent transactionRepaymentEvent = getTransactionRepaymentTypeBusinessEvent(repaymentTransactionType,
|
||||
isRecoveryRepayment, newRepaymentTransaction);
|
||||
final LoanTransactionBusinessEvent transactionRepaymentEvent = getTransactionRepaymentTypeBusinessEvent(
|
||||
repaymentTransactionType, isRecoveryRepayment, newRepaymentTransaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
|
||||
// disable all active standing orders linked to this loan if status
|
||||
// changes to closed
|
||||
disableStandingInstructionsLinkedToClosedLoan(loan);
|
||||
@@ -434,11 +433,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction));
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction));
|
||||
return newPaymentTransaction;
|
||||
}
|
||||
|
||||
@@ -686,12 +685,12 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService
|
||||
.notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction));
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService
|
||||
.notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction));
|
||||
|
||||
return newCreditBalanceRefundTransaction;
|
||||
}
|
||||
@@ -739,11 +738,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction));
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction));
|
||||
|
||||
builderResult.withEntityId(newRefundTransaction.getId()).withOfficeId(loan.getOfficeId()).withClientId(loan.getClientId())
|
||||
.withGroupId(loan.getGroupId());
|
||||
|
||||
+1
-13
@@ -19,7 +19,6 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.event.business.BusinessEventListener;
|
||||
@@ -28,7 +27,6 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseBu
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -66,19 +64,9 @@ public class LoanAccrualEventService {
|
||||
LoanStatus status = loan.getStatus();
|
||||
if (status.isClosedObligationsMet() || status.isOverpaid()) {
|
||||
log.debug("Loan balance change on accrual for loan {}", loan.getId());
|
||||
final boolean hasChargeAdjustment = hasChargeAdjustment(loan);
|
||||
loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan, hasChargeAdjustment);
|
||||
loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan, true);
|
||||
loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasChargeAdjustment(final Loan loan) {
|
||||
return loan.getLoanTransactions().stream()
|
||||
.filter(transaction -> transaction.isChargeAdjustment() && transaction.isNotReversed())
|
||||
.anyMatch(chargeAdjustment -> chargeAdjustment.getLoanTransactionRelations().stream()
|
||||
.anyMatch(relation -> relation.getRelationType() == LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT
|
||||
&& relation.getFromTransaction() != null
|
||||
&& Objects.equals(relation.getFromTransaction().getId(), chargeAdjustment.getId())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -59,7 +59,7 @@ public class LoanCapitalizedIncomeAmortizationEventService {
|
||||
if (loan.getLoanProductRelatedDetail().isEnableIncomeCapitalization()
|
||||
&& (status.isClosedObligationsMet() || status.isClosedWrittenOff() || status.isOverpaid())) {
|
||||
log.debug("Loan closure on capitalized income amortization for loan {}", loan.getId());
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationOnLoanClosure(loan);
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationOnLoanClosure(loan, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class LoanCapitalizedIncomeAmortizationEventService {
|
||||
if (loan.getLoanProductRelatedDetail().isEnableIncomeCapitalization()
|
||||
&& (status.isClosedObligationsMet() || status.isClosedWrittenOff() || status.isOverpaid())) {
|
||||
log.debug("Loan balance change on capitalized income amortization for loan {}", loan.getId());
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationOnLoanClosure(loan);
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationOnLoanClosure(loan, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ public class LoanCapitalizedIncomeAmortizationEventService {
|
||||
if (loan.getLoanProductRelatedDetail().isEnableIncomeCapitalization()) {
|
||||
log.debug("Loan pre charge-off capitalized income amortization for loan {}", loan.getId());
|
||||
loanCapitalizedIncomeAmortizationProcessingService.processCapitalizedIncomeAmortizationTillDate(loan,
|
||||
DateUtils.getBusinessLocalDate());
|
||||
DateUtils.getBusinessLocalDate(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-3
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -60,7 +61,14 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processCapitalizedIncomeAmortizationOnLoanClosure(@NotNull final Loan loan) {
|
||||
public void processCapitalizedIncomeAmortizationOnLoanClosure(@NotNull final Loan loan, final boolean addJournal) {
|
||||
List<Long> existingTransactionIds = Collections.emptyList();
|
||||
List<Long> existingReversedTransactionIds = Collections.emptyList();
|
||||
if (addJournal) {
|
||||
existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan);
|
||||
existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan);
|
||||
}
|
||||
|
||||
final LocalDate transactionDate = getFinalCapitalizedIncomeAmortizationTransactionDate(loan);
|
||||
final Optional<LoanTransaction> amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate,
|
||||
false, null);
|
||||
@@ -73,6 +81,10 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
new LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent(loanTransaction));
|
||||
}
|
||||
});
|
||||
|
||||
if (addJournal) {
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,7 +187,8 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void processCapitalizedIncomeAmortizationTillDate(@NonNull Loan loan, @NonNull LocalDate tillDate) {
|
||||
public void processCapitalizedIncomeAmortizationTillDate(@NonNull final Loan loan, @NonNull final LocalDate tillDate,
|
||||
final boolean addJournal) {
|
||||
final List<Long> existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan);
|
||||
final List<Long> existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan);
|
||||
|
||||
@@ -215,7 +228,9 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L
|
||||
transaction = loanTransactionRepository.save(transaction);
|
||||
loanTransactionRepository.flush();
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
if (addJournal) {
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
|
||||
BusinessEvent<?> event = MathUtil.isGreaterThanZero(totalAmortizationAmount)
|
||||
? new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(transaction)
|
||||
|
||||
+1
-1
@@ -318,10 +318,10 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
|
||||
.withEntityId(loanCharge.getId()) //
|
||||
|
||||
+11
-13
@@ -524,6 +524,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
@@ -1073,14 +1074,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, newInterestPaymentWaiverTransaction);
|
||||
|
||||
// Finished Notification
|
||||
|
||||
LoanTransactionBusinessEvent transactionRepaymentEvent = new LoanTransactionInterestPaymentWaiverPostBusinessEvent(
|
||||
final LoanTransactionBusinessEvent transactionRepaymentEvent = new LoanTransactionInterestPaymentWaiverPostBusinessEvent(
|
||||
newInterestPaymentWaiverTransaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
|
||||
.withLoanId(loan.getId()) //
|
||||
@@ -1457,11 +1457,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
false);
|
||||
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction));
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction));
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(waiveInterestTransaction.getId()) //
|
||||
@@ -1551,11 +1550,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
|
||||
loan.isInterestBearingAndInterestRecalculationEnabled(), false);
|
||||
loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(loanTransaction));
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(loanTransaction));
|
||||
|
||||
builder.withEntityId(loanTransaction.getId()).withEntityExternalId(loanTransaction.getExternalId());
|
||||
}
|
||||
@@ -2215,13 +2214,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
|
||||
|
||||
this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withOfficeId(loan.getOfficeId()) //
|
||||
@@ -2987,11 +2985,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
// Raise business events
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
|
||||
// Create journal entries for the new transaction(s)
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
|
||||
Long entityId = refundTransaction.getId();
|
||||
ExternalId entityExternalId = refundTransaction.getExternalId();
|
||||
|
||||
+2
-2
@@ -255,11 +255,11 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
entityId = newTransactionDetail.getId();
|
||||
entityExternalId = newTransactionDetail.getExternalId();
|
||||
}
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData));
|
||||
|
||||
journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData));
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(commandId) //
|
||||
|
||||
-1
@@ -198,7 +198,6 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
.notifyPreBusinessEvent(Mockito.any(LoanTransactionDownPaymentPreBusinessEvent.class));
|
||||
verify(businessEventNotifierService, Mockito.times(1))
|
||||
.notifyPostBusinessEvent(Mockito.any(LoanTransactionDownPaymentPostBusinessEvent.class));
|
||||
verify(businessEventNotifierService, Mockito.times(1)).notifyPostBusinessEvent(Mockito.any(LoanBalanceChangedBusinessEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user