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
@@ -131,8 +131,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
private AvroDateTimeMapper mapper;
private final LoanChargeService loanChargeService = new LoanChargeService(mock(LoanChargeValidator.class),
mock(LoanTransactionProcessingService.class), mock(LoanLifecycleStateMachine.class), mock(LoanBalanceService.class),
mock(LoanTransactionRepository.class));
mock(LoanTransactionProcessingService.class), mock(LoanLifecycleStateMachine.class), mock(LoanBalanceService.class));
private MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
@@ -28,9 +28,7 @@ import static org.mockito.Mockito.when;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -194,6 +192,8 @@ class LoanAdjustmentServiceImplTest {
private LoanAccrualsProcessingService loanAccrualsProcessingService;
@Mock
private LoanChargeValidator loanChargeValidator;
@Mock
private LoanJournalEntryPoster journalEntryPoster;
@Test
void givenMerchantIssuedRefundTransactionWithRelatedTransactions_whenAdjustExistingTransaction_thenRelatedTransactionsAreReversedAndEventsTriggered() {
@@ -204,9 +204,6 @@ class LoanAdjustmentServiceImplTest {
ScheduleGeneratorDTO scheduleGeneratorDTO = mock(ScheduleGeneratorDTO.class);
ExternalId reversalExternalId = ExternalId.generate();
List<Long> existingTransactionIds = new ArrayList<>();
List<Long> existingReversedTransactionIds = new ArrayList<>();
// Mock transaction type
when(transactionForAdjustment.getTypeOf()).thenReturn(LoanTransactionType.MERCHANT_ISSUED_REFUND);
when(transactionForAdjustment.isNotRepaymentLikeType()).thenReturn(false);
@@ -233,16 +230,12 @@ class LoanAdjustmentServiceImplTest {
List<LoanTransaction> loanTransactions = Arrays.asList(transactionForAdjustment, relatedTransaction);
when(loan.getLoanTransactions()).thenReturn(loanTransactions);
// Mock methods called inside adjustExistingTransaction
when(loanTransactionRepository.findTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList());
when(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList());
doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
when(loan.isClosedWrittenOff()).thenReturn(false);
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
// Act
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds,
existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, scheduleGeneratorDTO, reversalExternalId);
// Assert
// Verify that related transaction is reversed and event is triggered
@@ -265,9 +258,6 @@ class LoanAdjustmentServiceImplTest {
ScheduleGeneratorDTO scheduleGeneratorDTO = mock(ScheduleGeneratorDTO.class);
ExternalId reversalExternalId = ExternalId.generate();
List<Long> existingTransactionIds = new ArrayList<>();
List<Long> existingReversedTransactionIds = new ArrayList<>();
// Mock transaction type
when(transactionForAdjustment.getTypeOf()).thenReturn(LoanTransactionType.REPAYMENT);
when(transactionForAdjustment.isNotRepaymentLikeType()).thenReturn(false);
@@ -279,15 +269,12 @@ class LoanAdjustmentServiceImplTest {
LoanTransaction unrelatedTransaction = mock(LoanTransaction.class);
// Mock methods called inside adjustExistingTransaction
when(loanTransactionRepository.findTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList());
when(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList());
doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
when(loan.isClosedWrittenOff()).thenReturn(false);
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
// Act
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds,
existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, scheduleGeneratorDTO, reversalExternalId);
// Assert
// Verify that no related transactions are reversed
@@ -19,10 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.service;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
@@ -35,7 +32,6 @@ import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Stream;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -61,7 +57,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
@@ -155,9 +150,6 @@ class LoanChargeWritePlatformServiceImplTest {
@Mock
private LoanAccountService loanAccountService;
@Mock
private LoanAccountingBridgeMapper loanAccountingBridgeMapper;
@Mock
private LoanChargeService loanChargeService;
@@ -170,6 +162,9 @@ class LoanChargeWritePlatformServiceImplTest {
@Mock
private LoanLifecycleStateMachine loanLifecycleStateMachine;
@Mock
private LoanJournalEntryPoster journalEntryPoster;
@BeforeEach
void setUp() {
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
@@ -190,10 +185,6 @@ class LoanChargeWritePlatformServiceImplTest {
when(loan.getCurrency()).thenReturn(monetaryCurrency);
when(monetaryCurrency.getCode()).thenReturn(CURRENCY_CODE);
when(loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(any())).thenReturn(loan);
List<Long> existingTransactionIds = new ArrayList<>();
List<Long> existingReversedTransactionIds = new ArrayList<>();
when(loanTransactionRepository.findTransactionIdsByLoan(loan)).thenReturn(existingTransactionIds);
when(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)).thenReturn(existingReversedTransactionIds);
when(loan.getLoanCharges()).thenReturn(new HashSet<>());
when(loan.getDisbursementDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
@@ -209,7 +200,6 @@ class LoanChargeWritePlatformServiceImplTest {
when(loan.isCashBasedAccountingEnabledOnLoanProduct()).thenReturn(false);
when(loan.isUpfrontAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
when(loanAccountingBridgeMapper.deriveAccountingBridgeData(anyString(), anyList(), anyList(), anyBoolean(), any(Loan.class))).thenReturn(new AccountingBridgeDataDTO());
doNothing().when(journalEntryWritePlatformService).createJournalEntriesForLoan(any(AccountingBridgeDataDTO.class));
doNothing().when(loanChargeService).addLoanCharge(any(Loan.class), any(LoanCharge.class));
}
@@ -117,6 +117,9 @@ public class LoanDownPaymentHandlerServiceImplTest {
@Mock
private LoanTransactionService loanTransactionService;
@Mock
private LoanJournalEntryPoster loanJournalEntryPoster;
private LoanDownPaymentHandlerServiceImpl underTest;
@BeforeEach
@@ -124,7 +127,7 @@ public class LoanDownPaymentHandlerServiceImplTest {
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine, loanBalanceService,
loanTransactionService);
loanTransactionService, loanJournalEntryPoster);
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);