This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 7408f10cb FINERACT-2107: Adjust event trigger on refund reversion
7408f10cb is described below

commit 7408f10cb67b9d0b63a2508a128b08b54ba6e5b0
Author: leksinomi <[email protected]>
AuthorDate: Wed Nov 13 02:43:22 2024 +0200

    FINERACT-2107: Adjust event trigger on refund reversion
---
 .../portfolio/loanaccount/domain/Loan.java         |  60 +---
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |  58 +++-
 ...nWritePlatformServiceJpaRepositoryImplTest.java | 302 +++++++++++++++++++++
 3 files changed, 362 insertions(+), 58 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index f57235390..e039e505d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -2360,7 +2360,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), 
loanLifecycleStateMachine);
     }
 
-    private ChangedTransactionDetail 
handleRepaymentOrRecoveryOrWaiverTransaction(final LoanTransaction 
loanTransaction,
+    public ChangedTransactionDetail 
handleRepaymentOrRecoveryOrWaiverTransaction(final LoanTransaction 
loanTransaction,
             final LoanLifecycleStateMachine loanLifecycleStateMachine, final 
LoanTransaction adjustedTransaction,
             final ScheduleGeneratorDTO scheduleGeneratorDTO) {
         ChangedTransactionDetail changedTransactionDetail = null;
@@ -2681,60 +2681,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
                 possibleInterestToWaive.zero(), ExternalId.empty());
     }
 
-    public ChangedTransactionDetail adjustExistingTransaction(final 
LoanTransaction newTransactionDetail,
-            final LoanLifecycleStateMachine loanLifecycleStateMachine, final 
LoanTransaction transactionForAdjustment,
-            final List<Long> existingTransactionIds, final List<Long> 
existingReversedTransactionIds,
-            final ScheduleGeneratorDTO scheduleGeneratorDTO, final ExternalId 
reversalExternalId) {
-
-        ChangedTransactionDetail changedTransactionDetail = null;
-
-        existingTransactionIds.addAll(findExistingTransactionIds());
-        
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
-
-        
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
-                transactionForAdjustment.getTransactionDate());
-
-        if (transactionForAdjustment.isNotRepaymentLikeType() && 
transactionForAdjustment.isNotWaiver()
-                && transactionForAdjustment.isNotCreditBalanceRefund()) {
-            final String errorMessage = "Only (non-reversed) transactions of 
type repayment, waiver or credit balance refund can be adjusted.";
-            throw new InvalidLoanTransactionTypeException("transaction",
-                    
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions",
 errorMessage);
-        }
-
-        transactionForAdjustment.reverse(reversalExternalId);
-        transactionForAdjustment.manuallyAdjustedOrReversed();
-
-        if 
(transactionForAdjustment.getTypeOf().equals(LoanTransactionType.MERCHANT_ISSUED_REFUND)
-                || 
transactionForAdjustment.getTypeOf().equals(LoanTransactionType.PAYOUT_REFUND)) 
{
-            getLoanTransactions().stream() //
-                    .filter(LoanTransaction::isNotReversed)
-                    .filter(loanTransaction -> 
loanTransaction.getLoanTransactionRelations().stream()
-                            .anyMatch(relation -> 
relation.getRelationType().equals(LoanTransactionRelationTypeEnum.RELATED)
-                                    && 
relation.getToTransaction().getId().equals(transactionForAdjustment.getId())))
-                    .forEach(loanTransaction -> {
-                        loanTransaction.reverse();
-                        loanTransaction.manuallyAdjustedOrReversed();
-                    });
-        }
-
-        if (isClosedWrittenOff()) {
-            // find write off transaction and reverse it
-            final LoanTransaction writeOffTransaction = 
findWriteOffTransaction();
-            writeOffTransaction.reverse();
-        }
-
-        if (isClosedObligationsMet() || isClosedWrittenOff() || 
isClosedWithOutstandingAmountMarkedForReschedule()) {
-            
loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION, this);
-        }
-
-        if (newTransactionDetail.isRepaymentLikeType() || 
newTransactionDetail.isInterestWaiver()) {
-            changedTransactionDetail = 
handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, 
loanLifecycleStateMachine,
-                    transactionForAdjustment, scheduleGeneratorDTO);
-        }
-
-        return changedTransactionDetail;
-    }
-
     public ChangedTransactionDetail undoWrittenOff(LoanLifecycleStateMachine 
loanLifecycleStateMachine,
             final List<Long> existingTransactionIds, final List<Long> 
existingReversedTransactionIds,
             final ScheduleGeneratorDTO scheduleGeneratorDTO) {
@@ -3087,7 +3033,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         return getStatus().isClosedWrittenOff();
     }
 
-    private boolean isClosedWithOutstandingAmountMarkedForReschedule() {
+    public boolean isClosedWithOutstandingAmountMarkedForReschedule() {
         return getStatus().isClosedWithOutsandingAmountMarkedForReschedule();
     }
 
@@ -3777,7 +3723,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         this.loanTransactions.remove(loanTransaction);
     }
 
-    private void validateActivityNotBeforeClientOrGroupTransferDate(final 
LoanEvent event, final LocalDate activityDate) {
+    public void validateActivityNotBeforeClientOrGroupTransferDate(final 
LoanEvent event, final LocalDate activityDate) {
         if (this.client != null && this.client.getOfficeJoiningDate() != null) 
{
             final LocalDate clientOfficeJoiningDate = 
this.client.getOfficeJoiningDate();
             if (DateUtils.isBefore(activityDate, clientOfficeJoiningDate)) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index faeb302a6..fe2b4b604 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -1489,7 +1489,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
         
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(),
                 holidayDetailDTO.getWorkingDays(), 
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
 
-        final ChangedTransactionDetail changedTransactionDetail = 
loan.adjustExistingTransaction(newTransactionDetail,
+        final ChangedTransactionDetail changedTransactionDetail = 
adjustExistingTransaction(loan, newTransactionDetail,
                 loanLifecycleStateMachine, transactionToAdjust, 
existingTransactionIds, existingReversedTransactionIds,
                 scheduleGeneratorDTO, reversalTxnExternalId);
 
@@ -1583,6 +1583,62 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                 .with(changes).build();
     }
 
+    public ChangedTransactionDetail adjustExistingTransaction(final Loan loan, 
final LoanTransaction newTransactionDetail,
+            final LoanLifecycleStateMachine loanLifecycleStateMachine, final 
LoanTransaction transactionForAdjustment,
+            final List<Long> existingTransactionIds, final List<Long> 
existingReversedTransactionIds,
+            final ScheduleGeneratorDTO scheduleGeneratorDTO, final ExternalId 
reversalExternalId) {
+
+        ChangedTransactionDetail changedTransactionDetail = null;
+
+        existingTransactionIds.addAll(loan.findExistingTransactionIds());
+        
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
+
+        
loan.validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
+                transactionForAdjustment.getTransactionDate());
+
+        if (transactionForAdjustment.isNotRepaymentLikeType() && 
transactionForAdjustment.isNotWaiver()
+                && transactionForAdjustment.isNotCreditBalanceRefund()) {
+            final String errorMessage = "Only (non-reversed) transactions of 
type repayment, waiver or credit balance refund can be adjusted.";
+            throw new InvalidLoanTransactionTypeException("transaction",
+                    
"adjustment.is.only.allowed.to.repayment.or.waiver.or.creditbalancerefund.transactions",
 errorMessage);
+        }
+
+        transactionForAdjustment.reverse(reversalExternalId);
+        transactionForAdjustment.manuallyAdjustedOrReversed();
+
+        if 
(transactionForAdjustment.getTypeOf().equals(LoanTransactionType.MERCHANT_ISSUED_REFUND)
+                || 
transactionForAdjustment.getTypeOf().equals(LoanTransactionType.PAYOUT_REFUND)) 
{
+            loan.getLoanTransactions().stream() //
+                    .filter(LoanTransaction::isNotReversed)
+                    .filter(loanTransaction -> 
loanTransaction.getLoanTransactionRelations().stream()
+                            .anyMatch(relation -> 
relation.getRelationType().equals(LoanTransactionRelationTypeEnum.RELATED)
+                                    && 
relation.getToTransaction().getId().equals(transactionForAdjustment.getId())))
+                    .forEach(loanTransaction -> {
+                        loanTransaction.reverse();
+                        loanTransaction.manuallyAdjustedOrReversed();
+                        LoanAdjustTransactionBusinessEvent.Data eventData = 
new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
+                        
businessEventNotifierService.notifyPostBusinessEvent(new 
LoanAdjustTransactionBusinessEvent(eventData));
+                    });
+        }
+
+        if (loan.isClosedWrittenOff()) {
+            // find write off transaction and reverse it
+            final LoanTransaction writeOffTransaction = 
loan.findWriteOffTransaction();
+            writeOffTransaction.reverse();
+        }
+
+        if (loan.isClosedObligationsMet() || loan.isClosedWrittenOff() || 
loan.isClosedWithOutstandingAmountMarkedForReschedule()) {
+            
loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION, loan);
+        }
+
+        if (newTransactionDetail.isRepaymentLikeType() || 
newTransactionDetail.isInterestWaiver()) {
+            changedTransactionDetail = 
loan.handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, 
loanLifecycleStateMachine,
+                    transactionForAdjustment, scheduleGeneratorDTO);
+        }
+
+        return changedTransactionDetail;
+    }
+
     @Transactional
     @Override
     public CommandProcessingResult chargebackLoanTransaction(final Long 
loanId, final Long transactionId, final JsonCommand command) {
diff --git 
a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
new file mode 100644
index 000000000..828c608c5
--- /dev/null
+++ 
b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
@@ -0,0 +1,302 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+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;
+import 
org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
+import org.apache.fineract.cob.service.LoanAccountLockService;
+import 
org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
+import 
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
+import 
org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
+import 
org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
+import 
org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
+import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import 
org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
+import 
org.apache.fineract.organisation.teller.data.CashierTransactionDataValidator;
+import 
org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
+import 
org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository;
+import 
org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
+import org.apache.fineract.portfolio.account.domain.AccountTransferRepository;
+import 
org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
+import 
org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
+import 
org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
+import 
org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
+import org.apache.fineract.portfolio.calendar.domain.CalendarRepository;
+import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
+import 
org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import 
org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
+import 
org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
+import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
+import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
+import org.apache.fineract.portfolio.note.domain.NoteRepository;
+import 
org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
+import 
org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository;
+import 
org.apache.fineract.portfolio.repaymentwithpostdatedchecks.service.RepaymentWithPostDatedChecksAssembler;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class LoanWritePlatformServiceJpaRepositoryImplTest {
+
+    @InjectMocks
+    private LoanWritePlatformServiceJpaRepositoryImpl loanWritePlatformService;
+
+    @Mock
+    private LoanRepaymentScheduleTransactionProcessorFactory 
transactionProcessorFactory;
+    @Mock
+    private PlatformSecurityContext context;
+    @Mock
+    private LoanTransactionValidator loanTransactionValidator;
+    @Mock
+    private LoanUpdateCommandFromApiJsonDeserializer 
loanUpdateCommandFromApiJsonDeserializer;
+    @Mock
+    private LoanRepositoryWrapper loanRepositoryWrapper;
+    @Mock
+    private LoanAccountDomainService loanAccountDomainService;
+    @Mock
+    private NoteRepository noteRepository;
+    @Mock
+    private LoanTransactionRepository loanTransactionRepository;
+    @Mock
+    private LoanTransactionRelationRepository 
loanTransactionRelationRepository;
+    @Mock
+    private LoanAssembler loanAssembler;
+    @Mock
+    private JournalEntryWritePlatformService journalEntryWritePlatformService;
+    @Mock
+    private CalendarInstanceRepository calendarInstanceRepository;
+    @Mock
+    private PaymentDetailWritePlatformService 
paymentDetailWritePlatformService;
+    @Mock
+    private HolidayRepositoryWrapper holidayRepository;
+    @Mock
+    private ConfigurationDomainService configurationDomainService;
+    @Mock
+    private WorkingDaysRepositoryWrapper workingDaysRepository;
+    @Mock
+    private AccountTransfersWritePlatformService 
accountTransfersWritePlatformService;
+    @Mock
+    private AccountTransfersReadPlatformService 
accountTransfersReadPlatformService;
+    @Mock
+    private AccountAssociationsReadPlatformService 
accountAssociationsReadPlatformService;
+    @Mock
+    private LoanReadPlatformService loanReadPlatformService;
+    @Mock
+    private FromJsonHelper fromApiJsonHelper;
+    @Mock
+    private CalendarRepository calendarRepository;
+    @Mock
+    private LoanScheduleHistoryWritePlatformService 
loanScheduleHistoryWritePlatformService;
+    @Mock
+    private LoanApplicationValidator loanApplicationCommandFromApiJsonHelper;
+    @Mock
+    private AccountAssociationsRepository accountAssociationRepository;
+    @Mock
+    private AccountTransferDetailRepository accountTransferDetailRepository;
+    @Mock
+    private BusinessEventNotifierService businessEventNotifierService;
+    @Mock
+    private GuarantorDomainService guarantorDomainService;
+    @Mock
+    private LoanUtilService loanUtilService;
+    @Mock
+    private LoanSummaryWrapper loanSummaryWrapper;
+    @Mock
+    private EntityDatatableChecksWritePlatformService 
entityDatatableChecksWritePlatformService;
+    @Mock
+    private LoanRepaymentScheduleTransactionProcessorFactory 
transactionProcessingStrategy;
+    @Mock
+    private CodeValueRepositoryWrapper codeValueRepository;
+    @Mock
+    private CashierTransactionDataValidator cashierTransactionDataValidator;
+    @Mock
+    private GLIMAccountInfoRepository glimRepository;
+    @Mock
+    private LoanRepository loanRepository;
+    @Mock
+    private RepaymentWithPostDatedChecksAssembler 
repaymentWithPostDatedChecksAssembler;
+    @Mock
+    private PostDatedChecksRepository postDatedChecksRepository;
+    @Mock
+    private LoanRepaymentScheduleInstallmentRepository 
loanRepaymentScheduleInstallmentRepository;
+    @Mock
+    private LoanLifecycleStateMachine loanLifecycleStateMachine;
+    @Mock
+    private LoanAccountLockService loanAccountLockService;
+    @Mock
+    private ExternalIdFactory externalIdFactory;
+    @Mock
+    private ReplayedTransactionBusinessEventService 
replayedTransactionBusinessEventService;
+    @Mock
+    private LoanAccrualTransactionBusinessEventService 
loanAccrualTransactionBusinessEventService;
+    @Mock
+    private ErrorHandler errorHandler;
+    @Mock
+    private LoanDownPaymentHandlerService loanDownPaymentHandlerService;
+    @Mock
+    private AccountTransferRepository accountTransferRepository;
+    @Mock
+    private LoanTransactionAssembler loanTransactionAssembler;
+    @Mock
+    private LoanAccrualsProcessingService loanAccrualsProcessingService;
+
+    @Test
+    void 
givenMerchantIssuedRefundTransactionWithRelatedTransactions_whenAdjustExistingTransaction_thenRelatedTransactionsAreReversedAndEventsTriggered()
 {
+        // Arrange
+        Loan loan = mock(Loan.class);
+        LoanTransaction transactionForAdjustment = mock(LoanTransaction.class);
+        LoanTransaction newTransactionDetail = mock(LoanTransaction.class);
+        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);
+
+        // Mock transaction date
+        
when(transactionForAdjustment.getTransactionDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
+
+        // Mock transaction ID to prevent NullPointerException
+        when(transactionForAdjustment.getId()).thenReturn(1L);
+
+        // Mock loan transactions
+        LoanTransaction relatedTransaction = mock(LoanTransaction.class);
+        when(relatedTransaction.isNotReversed()).thenReturn(true);
+
+        LoanTransactionRelation transactionRelation = 
mock(LoanTransactionRelation.class);
+        
when(transactionRelation.getRelationType()).thenReturn(LoanTransactionRelationTypeEnum.RELATED);
+        
when(transactionRelation.getToTransaction()).thenReturn(transactionForAdjustment);
+
+        Set<LoanTransactionRelation> transactionRelations = new HashSet<>();
+        transactionRelations.add(transactionRelation);
+
+        
when(relatedTransaction.getLoanTransactionRelations()).thenReturn(transactionRelations);
+
+        List<LoanTransaction> loanTransactions = 
Arrays.asList(transactionForAdjustment, relatedTransaction);
+        when(loan.getLoanTransactions()).thenReturn(loanTransactions);
+
+        // Mock methods called inside adjustExistingTransaction
+        
when(loan.findExistingTransactionIds()).thenReturn(Collections.emptyList());
+        
when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
+        
doNothing().when(loan).validateActivityNotBeforeClientOrGroupTransferDate(any(),
 any());
+        when(loan.isClosedWrittenOff()).thenReturn(false);
+        when(loan.isClosedObligationsMet()).thenReturn(false);
+        
when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
+        when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
+
+        // Act
+        loanWritePlatformService.adjustExistingTransaction(loan, 
newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment,
+                existingTransactionIds, existingReversedTransactionIds, 
scheduleGeneratorDTO, reversalExternalId);
+
+        // Assert
+        // Verify that related transaction is reversed and event is triggered
+        verify(relatedTransaction).reverse();
+        verify(relatedTransaction).manuallyAdjustedOrReversed();
+
+        ArgumentCaptor<LoanAdjustTransactionBusinessEvent> eventCaptor = 
ArgumentCaptor.forClass(LoanAdjustTransactionBusinessEvent.class);
+        
verify(businessEventNotifierService).notifyPostBusinessEvent(eventCaptor.capture());
+
+        LoanAdjustTransactionBusinessEvent event = eventCaptor.getValue();
+        assertEquals(relatedTransaction, event.get().getTransactionToAdjust());
+    }
+
+    @Test
+    void 
givenNonMerchantIssuedRefundTransaction_whenAdjustExistingTransaction_thenNoRelatedTransactionsReversed()
 {
+        // Arrange
+        Loan loan = mock(Loan.class);
+        LoanTransaction transactionForAdjustment = mock(LoanTransaction.class);
+        LoanTransaction newTransactionDetail = mock(LoanTransaction.class);
+        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);
+
+        // Mock transaction date
+        
when(transactionForAdjustment.getTransactionDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
+
+        // Mock loan transactions
+        LoanTransaction unrelatedTransaction = mock(LoanTransaction.class);
+
+        // Mock methods called inside adjustExistingTransaction
+        
when(loan.findExistingTransactionIds()).thenReturn(Collections.emptyList());
+        
when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
+        
doNothing().when(loan).validateActivityNotBeforeClientOrGroupTransferDate(any(),
 any());
+        when(loan.isClosedWrittenOff()).thenReturn(false);
+        when(loan.isClosedObligationsMet()).thenReturn(false);
+        
when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
+        when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
+
+        // Act
+        loanWritePlatformService.adjustExistingTransaction(loan, 
newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment,
+                existingTransactionIds, existingReversedTransactionIds, 
scheduleGeneratorDTO, reversalExternalId);
+
+        // Assert
+        // Verify that no related transactions are reversed
+        verify(unrelatedTransaction, never()).reverse();
+        verify(unrelatedTransaction, never()).manuallyAdjustedOrReversed();
+        verify(businessEventNotifierService, 
never()).notifyPostBusinessEvent(any(LoanAdjustTransactionBusinessEvent.class));
+    }
+
+}

Reply via email to