FINERACT-1926: Fix No Such Element exception in Asset buyback
diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java
index 65a6b4e..99b9c8e 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java
@@ -22,6 +22,7 @@
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.loan.LoanCOBBusinessStep;
@@ -103,14 +104,19 @@
private void handleBuyback(final Loan loan, final LocalDate settlementDate,
final ExternalAssetOwnerTransfer buybackExternalAssetOwnerTransfer) {
- ExternalAssetOwnerTransfer activeExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository
+ Optional<ExternalAssetOwnerTransfer> optActiveExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository
.findOne((root, query, criteriaBuilder) -> criteriaBuilder.and(criteriaBuilder.equal(root.get("loanId"), loan.getId()),
criteriaBuilder.equal(root.get("owner"), buybackExternalAssetOwnerTransfer.getOwner()),
criteriaBuilder.equal(root.get("status"), ExternalTransferStatus.ACTIVE),
- criteriaBuilder.equal(root.get("effectiveDateTo"), FUTURE_DATE_9999_12_31)))
- .orElseThrow();
- ExternalAssetOwnerTransfer newExternalAssetOwnerTransfer = buybackAsset(loan, settlementDate, buybackExternalAssetOwnerTransfer,
- activeExternalAssetOwnerTransfer);
+ criteriaBuilder.equal(root.get("effectiveDateTo"), FUTURE_DATE_9999_12_31)));
+ ExternalAssetOwnerTransfer newExternalAssetOwnerTransfer;
+ if (!optActiveExternalAssetOwnerTransfer.isPresent()) {
+ newExternalAssetOwnerTransfer = createNewEntry(settlementDate, buybackExternalAssetOwnerTransfer,
+ ExternalTransferStatus.CANCELLED, ExternalTransferSubStatus.UNSOLD, settlementDate, settlementDate);
+ } else {
+ newExternalAssetOwnerTransfer = buybackAsset(loan, settlementDate, buybackExternalAssetOwnerTransfer,
+ optActiveExternalAssetOwnerTransfer.get());
+ }
businessEventNotifierService.notifyPostBusinessEvent(new LoanOwnershipTransferBusinessEvent(newExternalAssetOwnerTransfer, loan));
businessEventNotifierService.notifyPostBusinessEvent(new LoanAccountSnapshotBusinessEvent(loan));
}
diff --git a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java
index 8286eed..0264374 100644
--- a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java
+++ b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java
@@ -383,6 +383,55 @@
assertEquals("Execute external asset owner transfer", actualEnumName);
}
+ @Test
+ public void givenLoanSaleAnsBuyBackButBalanceIsNegative() {
+ // given
+ final Loan loanForProcessing = Mockito.mock(Loan.class);
+ when(loanForProcessing.getId()).thenReturn(1L);
+ LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
+ when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary);
+ when(loanSummary.getTotalOutstanding()).thenReturn(BigDecimal.ZERO);
+ when(loanForProcessing.getTotalOverpaid()).thenReturn(BigDecimal.ONE);
+ ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
+ ExternalAssetOwnerTransfer secondResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class);
+ secondResponseItem.setSettlementDate(actualDate.plusDays(2));
+
+ ExternalAssetOwnerTransfer firstSaveResult = Mockito.mock(ExternalAssetOwnerTransfer.class);
+ ExternalAssetOwnerTransfer secondSaveResult = Mockito.mock(ExternalAssetOwnerTransfer.class);
+ secondSaveResult.setSettlementDate(actualDate.plusDays(2));
+ ExternalAssetOwnerTransfer thirdSaveResult = Mockito.mock(ExternalAssetOwnerTransfer.class);
+ ExternalAssetOwnerTransfer fourthSaveResult = Mockito.mock(ExternalAssetOwnerTransfer.class);
+
+ when(firstResponseItem.getStatus()).thenReturn(ExternalTransferStatus.PENDING);
+ when(externalAssetOwnerTransferRepository.save(any(ExternalAssetOwnerTransfer.class))).thenReturn(firstSaveResult)
+ .thenReturn(secondSaveResult).thenReturn(thirdSaveResult).thenReturn(fourthSaveResult);
+ List<ExternalAssetOwnerTransfer> response = List.of(firstResponseItem);
+ when(externalAssetOwnerTransferRepository.findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id"))))
+ .thenReturn(response);
+ ArgumentCaptor<ExternalAssetOwnerTransfer> externalAssetOwnerTransferArgumentCaptor = ArgumentCaptor
+ .forClass(ExternalAssetOwnerTransfer.class);
+ // when
+ Loan processedLoan = underTest.execute(loanForProcessing);
+ // then
+ verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id")));
+ verify(firstResponseItem).setEffectiveDateTo(actualDate);
+ verify(externalAssetOwnerTransferRepository, times(2)).save(externalAssetOwnerTransferArgumentCaptor.capture());
+
+ assertEquals(externalAssetOwnerTransferArgumentCaptor.getAllValues().get(0).getOwner(),
+ externalAssetOwnerTransferArgumentCaptor.getAllValues().get(1).getOwner());
+ assertEquals(externalAssetOwnerTransferArgumentCaptor.getAllValues().get(0).getExternalId(),
+ externalAssetOwnerTransferArgumentCaptor.getAllValues().get(1).getExternalId());
+ assertEquals(ExternalTransferStatus.DECLINED, externalAssetOwnerTransferArgumentCaptor.getAllValues().get(1).getStatus());
+ assertEquals(ExternalTransferSubStatus.BALANCE_NEGATIVE,
+ externalAssetOwnerTransferArgumentCaptor.getAllValues().get(1).getSubStatus());
+ assertEquals(actualDate, externalAssetOwnerTransferArgumentCaptor.getAllValues().get(1).getSettlementDate());
+
+ assertEquals(processedLoan, loanForProcessing);
+
+ ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(2);
+ verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, secondSaveResult);
+ }
+
@NotNull
private ArgumentCaptor<BusinessEvent<?>> verifyBusinessEvents(int expectedBusinessEvents) {
@SuppressWarnings("unchecked")