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));
+ }
+
+}