FINERACT-2233: Rework journal entry handling logic in Loan module

This commit is contained in:
mariiaKraievska
2025-08-19 13:19:40 +02:00
committed by Adam Saghy
parent 942ad0183c
commit ace59352fa
37 changed files with 609 additions and 971 deletions
@@ -39,11 +39,11 @@ import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository;
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent;
import org.apache.fineract.investor.service.AccountingService;
import org.apache.fineract.investor.service.DelayedSettlementAttributeService;
import org.apache.fineract.investor.service.ExternalAssetOwnerTransferOutstandingInterestCalculation;
import org.apache.fineract.investor.service.LoanTransferabilityService;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
import org.springframework.context.annotation.Conditional;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
@@ -61,7 +61,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
ExternalTransferStatus.BUYBACK);
private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository;
private final ExternalAssetOwnerTransferLoanMappingRepository externalAssetOwnerTransferLoanMappingRepository;
private final AccountingService accountingService;
private final LoanJournalEntryPoster loanJournalEntryPoster;
private final BusinessEventNotifierService businessEventNotifierService;
private final LoanTransferabilityService loanTransferabilityService;
private final DelayedSettlementAttributeService delayedSettlementAttributeService;
@@ -148,7 +148,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
externalAssetOwnerTransferRepository.save(activeExternalAssetOwnerTransfer);
buybackExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository.save(buybackExternalAssetOwnerTransfer);
externalAssetOwnerTransferLoanMappingRepository.deleteByLoanIdAndOwnerTransfer(loan.getId(), activeExternalAssetOwnerTransfer);
accountingService.createJournalEntriesForBuybackAssetTransfer(loan, buybackExternalAssetOwnerTransfer);
loanJournalEntryPoster.postJournalEntriesForExternalOwnerTransfer(loan, buybackExternalAssetOwnerTransfer, null);
return buybackExternalAssetOwnerTransfer;
}
@@ -174,7 +174,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep
ExternalTransferStatus activeStatus = determineActiveStatus(externalAssetOwnerTransfer);
ExternalAssetOwnerTransfer newTransfer = activatePendingEntry(settlementDate, externalAssetOwnerTransfer, activeStatus);
accountingService.createJournalEntriesForSaleAssetTransfer(loan, newTransfer, previousOwner);
loanJournalEntryPoster.postJournalEntriesForExternalOwnerTransfer(loan, newTransfer, previousOwner);
return newTransfer;
}
@@ -47,6 +47,7 @@ import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -60,7 +61,7 @@ public class LoanAccountOwnerTransferServiceImpl implements LoanAccountOwnerTran
public static final LocalDate FUTURE_DATE_9999_12_31 = LocalDate.of(9999, 12, 31);
private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository;
private final ExternalAssetOwnerTransferLoanMappingRepository externalAssetOwnerTransferLoanMappingRepository;
private final AccountingService accountingService;
private final LoanJournalEntryPoster loanJournalEntryPoster;
private final BusinessEventNotifierService businessEventNotifierService;
private final ExternalAssetOwnerTransferOutstandingInterestCalculation externalAssetOwnerTransferOutstandingInterestCalculation;
@@ -107,7 +108,7 @@ public class LoanAccountOwnerTransferServiceImpl implements LoanAccountOwnerTran
buybackTransfer = updatePendingBuybackTransfer(loan, buybackTransfer);
externalAssetOwnerTransferLoanMappingRepository.deleteByLoanIdAndOwnerTransfer(loan.getId(), activeTransfer);
accountingService.createJournalEntriesForBuybackAssetTransfer(loan, buybackTransfer);
loanJournalEntryPoster.postJournalEntriesForExternalOwnerTransfer(loan, buybackTransfer, null);
businessEventNotifierService.notifyPostBusinessEvent(new LoanOwnershipTransferBusinessEvent(buybackTransfer, loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanAccountSnapshotBusinessEvent(loan));
@@ -56,13 +56,13 @@ import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository;
import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository;
import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent;
import org.apache.fineract.investor.service.AccountingService;
import org.apache.fineract.investor.service.DelayedSettlementAttributeService;
import org.apache.fineract.investor.service.ExternalAssetOwnerTransferOutstandingInterestCalculation;
import org.apache.fineract.investor.service.LoanTransferabilityService;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterAll;
@@ -97,7 +97,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
private ExternalAssetOwnerTransferLoanMappingRepository externalAssetOwnerTransferLoanMappingRepository;
@Mock
private AccountingService accountingService;
private LoanJournalEntryPoster loanJournalEntryPoster;
@Mock
private BusinessEventNotifierService businessEventNotifierService;
@@ -130,7 +130,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, actualDate)));
underTest = new LoanAccountOwnerTransferBusinessStep(externalAssetOwnerTransferRepository,
externalAssetOwnerTransferLoanMappingRepository, accountingService, businessEventNotifierService,
externalAssetOwnerTransferLoanMappingRepository, loanJournalEntryPoster, businessEventNotifierService,
loanTransferabilityService, delayedSettlementAttributeService, externalAssetOwnerTransferOutstandingInterestCalculation);
}
@@ -149,7 +149,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
final Loan processedLoan = underTest.execute(loanForProcessing);
// then
verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id")));
verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, accountingService);
verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, loanJournalEntryPoster);
assertEquals(processedLoan, loanForProcessing);
}
@@ -176,7 +176,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
// then
assertEquals("Illegal transfer found. Expected PENDING and BUYBACK, found: PENDING and ACTIVE", exception.getMessage());
verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id")));
verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, accountingService);
verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, loanJournalEntryPoster);
}
@Test
@@ -202,7 +202,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
// then
assertEquals("Delayed Settlement enabled, but found 2 transfers of statuses: PENDING and BUYBACK", exception.getMessage());
verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id")));
verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, accountingService);
verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, loanJournalEntryPoster);
}
@Test
@@ -273,7 +273,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
assertEquals(processedLoan, loanForProcessing);
verifyNoInteractions(loanTransferabilityService, accountingService);
verifyNoInteractions(loanTransferabilityService, loanJournalEntryPoster);
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(2);
verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, secondSaveResult);
@@ -316,8 +316,8 @@ public class LoanAccountOwnerTransferBusinessStepTest {
assertEquals(processedLoan, loanForProcessing);
verify(accountingService).createJournalEntriesForBuybackAssetTransfer(loanForProcessing, firstResponseItem);
verifyNoMoreInteractions(accountingService);
verify(loanJournalEntryPoster).postJournalEntriesForExternalOwnerTransfer(loanForProcessing, firstResponseItem, null);
verifyNoMoreInteractions(loanJournalEntryPoster);
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(2);
verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, firstResponseItem);
@@ -387,8 +387,8 @@ public class LoanAccountOwnerTransferBusinessStepTest {
verify(externalAssetOwnerTransferLoanMappingRepository).save(externalAssetOwnerTransferLoanMappingArgumentCaptor.capture());
verify(accountingService).createJournalEntriesForSaleAssetTransfer(loanForProcessing, savedNewTransfer, null);
verifyNoMoreInteractions(accountingService);
verify(loanJournalEntryPoster).postJournalEntriesForExternalOwnerTransfer(loanForProcessing, savedNewTransfer, null);
verifyNoMoreInteractions(loanJournalEntryPoster);
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(2);
verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, savedNewTransfer);
@@ -445,7 +445,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
assertEquals(actualDate, capturedActiveTransfer.getEffectiveDateTo());
assertEquals(processedLoan, loanForProcessing);
verifyNoInteractions(accountingService);
verifyNoInteractions(loanJournalEntryPoster);
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(1);
verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, savedNewTransfer);
@@ -516,8 +516,8 @@ public class LoanAccountOwnerTransferBusinessStepTest {
assertEquals(savedNewTransfer, externalAssetOwnerTransferLoanMappingArgumentCaptor.getValue().getOwnerTransfer());
assertEquals(processedLoan, loanForProcessing);
verify(accountingService).createJournalEntriesForSaleAssetTransfer(loanForProcessing, savedNewTransfer, previousOwner);
verifyNoMoreInteractions(accountingService);
verify(loanJournalEntryPoster).postJournalEntriesForExternalOwnerTransfer(loanForProcessing, savedNewTransfer, previousOwner);
verifyNoMoreInteractions(loanJournalEntryPoster);
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(2);
verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, savedNewTransfer);
@@ -552,7 +552,7 @@ public class LoanAccountOwnerTransferBusinessStepTest {
verify(loanTransferabilityService).isTransferable(loanForProcessing, pendingTransfer);
verifyNoMoreInteractions(loanTransferabilityService);
verifyNoInteractions(accountingService);
verifyNoInteractions(loanJournalEntryPoster);
verify(externalAssetOwnerTransferRepository).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id")));
verify(externalAssetOwnerTransferRepository).findOne(any(Specification.class));
verify(externalAssetOwnerTransferRepository, never()).save(any(ExternalAssetOwnerTransfer.class));
@@ -50,6 +50,7 @@ import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -82,6 +83,8 @@ public class LoanAccountOwnerTransferServiceTest {
private BusinessEventNotifierService businessEventNotifierService;
@Mock
private ExternalAssetOwnerTransferOutstandingInterestCalculation externalAssetOwnerTransferOutstandingInterestCalculation;
@Mock
private LoanJournalEntryPoster loanJournalEntryPoster;
@InjectMocks
private LoanAccountOwnerTransferServiceImpl underTest;