FINERACT-2080: Move loan validation from entity to validator classes
This commit is contained in:
committed by
Adam Saghy
parent
56ee2eaaff
commit
a2c01d25e5
+4
@@ -46,6 +46,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -124,6 +125,9 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
@Mock
|
||||
private JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
|
||||
@Mock
|
||||
private LoanChargeValidator loanChargeValidator;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||
|
||||
+121
-24
@@ -21,24 +21,38 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.infrastructure.configuration.service.TemporaryConfigurationServiceContainer;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionDownPaymentPostBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionDownPaymentPreBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanRefundValidator;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -54,7 +68,11 @@ import org.mockito.quality.Strictness;
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class LoanDownPaymentHandlerServiceImplTest {
|
||||
|
||||
private final MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||
private final MockedStatic<MoneyHelper> moneyHelper = mockStatic(MoneyHelper.class);
|
||||
private final MockedStatic<MathUtil> mathUtilMock = mockStatic(MathUtil.class);
|
||||
private final MockedStatic<DateUtils> dateUtilsMock = mockStatic(DateUtils.class);
|
||||
private final MockedStatic<TemporaryConfigurationServiceContainer> tempConfigServiceMock = mockStatic(
|
||||
TemporaryConfigurationServiceContainer.class);
|
||||
|
||||
@Mock
|
||||
private BusinessEventNotifierService businessEventNotifierService;
|
||||
@@ -66,42 +84,95 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
private LoanTransaction loanTransaction;
|
||||
|
||||
@Mock
|
||||
private ScheduleGeneratorDTO scheduleGeneratorDTO;
|
||||
private JsonCommand command;
|
||||
|
||||
@Mock
|
||||
private JsonCommand command;
|
||||
private LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator;
|
||||
|
||||
@Mock
|
||||
private LoanScheduleService loanScheduleService;
|
||||
|
||||
@Mock
|
||||
private LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor;
|
||||
|
||||
@Mock
|
||||
private LoanRefundService loanRefundService;
|
||||
|
||||
@Mock
|
||||
private LoanRefundValidator loanRefundValidator;
|
||||
|
||||
private LoanDownPaymentHandlerServiceImpl underTest;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService);
|
||||
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void reset() {
|
||||
moneyHelper.close();
|
||||
tempConfigServiceMock.close();
|
||||
mathUtilMock.close();
|
||||
dateUtilsMock.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDownPaymentHandler() {
|
||||
// given
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanTransaction disbursement = Mockito.mock(LoanTransaction.class);
|
||||
MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class);
|
||||
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
final LoanTransaction disbursement = Mockito.mock(LoanTransaction.class);
|
||||
final Money mockAdjustedDownPaymentMoney = Mockito.mock(Money.class);
|
||||
final LoanProductRelatedDetail loanRepaymentRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
||||
final LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = Mockito.mock(ScheduleGeneratorDTO.class);
|
||||
final HolidayDetailDTO holidayDetailDTO = Mockito.mock(HolidayDetailDTO.class);
|
||||
final ChangedTransactionDetail changedTransactionDetail = Mockito.mock(ChangedTransactionDetail.class);
|
||||
|
||||
final MonetaryCurrency loanCurrency = new MonetaryCurrency("USD", 2, 1);
|
||||
|
||||
final Money downPaymentMoney = Mockito.mock(Money.class);
|
||||
final Money overPaymentPortionMoney = Mockito.mock(Money.class);
|
||||
final Money calculatedMoney = Money.of(loanCurrency, BigDecimal.valueOf(500));
|
||||
|
||||
when(downPaymentMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode());
|
||||
when(overPaymentPortionMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode());
|
||||
|
||||
when(loanForProcessing.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
|
||||
when(loanForProcessing.repaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
|
||||
when(loanRepaymentRelatedDetail.isInterestRecalculationEnabled()).thenReturn(true);
|
||||
when(loanRepaymentRelatedDetail.getDisbursedAmountPercentageForDownPayment()).thenReturn(BigDecimal.valueOf(10));
|
||||
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
||||
when(loanForProcessing.loanCurrency()).thenReturn(loanCurrency);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanProduct.getInstallmentAmountInMultiplesOf()).thenReturn(10);
|
||||
when(loanForProcessing.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
|
||||
when(loanForProcessing.getTransactionProcessor()).thenReturn(loanRepaymentScheduleTransactionProcessor);
|
||||
when(loanRepaymentScheduleTransactionProcessor.reprocessLoanTransactions(any(), any(), any(), any(), any()))
|
||||
.thenReturn(changedTransactionDetail);
|
||||
when(loanProductRelatedDetail.getLoanScheduleType()).thenReturn(LoanScheduleType.PROGRESSIVE);
|
||||
|
||||
when(disbursement.getOverPaymentPortion(loanCurrency)).thenReturn(overPaymentPortionMoney);
|
||||
|
||||
when(downPaymentMoney.minus(overPaymentPortionMoney)).thenReturn(calculatedMoney);
|
||||
mathUtilMock.when(() -> MathUtil.negativeToZero(any(Money.class))).thenReturn(mockAdjustedDownPaymentMoney);
|
||||
when(mockAdjustedDownPaymentMoney.isGreaterThanZero()).thenReturn(true);
|
||||
|
||||
dateUtilsMock.when(DateUtils::getBusinessLocalDate).thenReturn(LocalDate.of(2024, 11, 19));
|
||||
|
||||
when(scheduleGeneratorDTO.getHolidayDetailDTO()).thenReturn(holidayDetailDTO);
|
||||
|
||||
doNothing().when(businessEventNotifierService).notifyPreBusinessEvent(any(LoanTransactionDownPaymentPreBusinessEvent.class));
|
||||
doNothing().when(businessEventNotifierService).notifyPostBusinessEvent(any(LoanTransactionDownPaymentPostBusinessEvent.class));
|
||||
doNothing().when(businessEventNotifierService).notifyPostBusinessEvent(any(LoanBalanceChangedBusinessEvent.class));
|
||||
when(loanTransactionRepository.saveAndFlush(any(LoanTransaction.class))).thenReturn(loanTransaction);
|
||||
when(loanForProcessing.handleDownPayment(eq(disbursement), eq(command), eq(scheduleGeneratorDTO))).thenReturn(loanTransaction);
|
||||
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
||||
when(loanCurrency.getCode()).thenReturn("CODE");
|
||||
when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1);
|
||||
when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1);
|
||||
|
||||
// when
|
||||
LoanTransaction actual = underTest.handleDownPayment(scheduleGeneratorDTO, command, disbursement, loanForProcessing);
|
||||
final LoanTransaction actual = underTest.handleDownPayment(scheduleGeneratorDTO, command, disbursement, loanForProcessing);
|
||||
|
||||
// then
|
||||
assertNotNull(actual);
|
||||
@@ -110,21 +181,48 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
verify(businessEventNotifierService, Mockito.times(1))
|
||||
.notifyPostBusinessEvent(Mockito.any(LoanTransactionDownPaymentPostBusinessEvent.class));
|
||||
verify(businessEventNotifierService, Mockito.times(1)).notifyPostBusinessEvent(Mockito.any(LoanBalanceChangedBusinessEvent.class));
|
||||
verify(loanForProcessing, Mockito.times(1)).handleDownPayment(eq(disbursement), eq(command), eq(scheduleGeneratorDTO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDownPaymentHandlerNoNewTransaction() {
|
||||
// given
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
LoanTransaction disbursement = Mockito.mock(LoanTransaction.class);
|
||||
MonetaryCurrency loanCurrency = Mockito.mock(MonetaryCurrency.class);
|
||||
doNothing().when(businessEventNotifierService).notifyPreBusinessEvent(any(LoanTransactionDownPaymentPreBusinessEvent.class));
|
||||
when(loanForProcessing.handleDownPayment(eq(disbursement), eq(command), eq(scheduleGeneratorDTO))).thenReturn(null);
|
||||
final Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
final LoanTransaction disbursement = Mockito.mock(LoanTransaction.class);
|
||||
final Money mockAdjustedDownPaymentMoney = Mockito.mock(Money.class);
|
||||
final LoanProductRelatedDetail loanRepaymentRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
||||
final LoanProduct loanProduct = Mockito.mock(LoanProduct.class);
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = Mockito.mock(ScheduleGeneratorDTO.class);
|
||||
final HolidayDetailDTO holidayDetailDTO = Mockito.mock(HolidayDetailDTO.class);
|
||||
|
||||
final MonetaryCurrency loanCurrency = new MonetaryCurrency("USD", 2, 1);
|
||||
|
||||
final Money downPaymentMoney = Mockito.mock(Money.class);
|
||||
final Money overPaymentPortionMoney = Mockito.mock(Money.class);
|
||||
final Money calculatedMoney = Money.of(loanCurrency, BigDecimal.valueOf(500));
|
||||
|
||||
when(downPaymentMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode());
|
||||
when(overPaymentPortionMoney.getCurrencyCode()).thenReturn(loanCurrency.getCode());
|
||||
|
||||
when(loanForProcessing.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentRelatedDetail);
|
||||
when(loanRepaymentRelatedDetail.getDisbursedAmountPercentageForDownPayment()).thenReturn(BigDecimal.valueOf(10));
|
||||
when(loanForProcessing.getCurrency()).thenReturn(loanCurrency);
|
||||
when(loanCurrency.getCode()).thenReturn("CODE");
|
||||
when(loanCurrency.getCurrencyInMultiplesOf()).thenReturn(1);
|
||||
when(loanCurrency.getDigitsAfterDecimal()).thenReturn(1);
|
||||
when(loanForProcessing.getLoanProduct()).thenReturn(loanProduct);
|
||||
when(loanForProcessing.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
|
||||
when(loanProductRelatedDetail.getLoanScheduleType()).thenReturn(LoanScheduleType.PROGRESSIVE);
|
||||
|
||||
when(disbursement.getOverPaymentPortion(loanCurrency)).thenReturn(overPaymentPortionMoney);
|
||||
|
||||
when(downPaymentMoney.minus(overPaymentPortionMoney)).thenReturn(calculatedMoney);
|
||||
mathUtilMock.when(() -> MathUtil.negativeToZero(any(Money.class))).thenReturn(mockAdjustedDownPaymentMoney);
|
||||
when(mockAdjustedDownPaymentMoney.isGreaterThanZero()).thenReturn(false);
|
||||
|
||||
dateUtilsMock.when(DateUtils::getBusinessLocalDate).thenReturn(LocalDate.of(2024, 11, 19));
|
||||
|
||||
when(scheduleGeneratorDTO.getHolidayDetailDTO()).thenReturn(holidayDetailDTO);
|
||||
|
||||
doNothing().when(businessEventNotifierService).notifyPreBusinessEvent(any(LoanTransactionDownPaymentPreBusinessEvent.class));
|
||||
|
||||
// when
|
||||
LoanTransaction actual = underTest.handleDownPayment(scheduleGeneratorDTO, command, disbursement, loanForProcessing);
|
||||
|
||||
@@ -135,7 +233,6 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
verify(businessEventNotifierService, Mockito.never())
|
||||
.notifyPostBusinessEvent(Mockito.any(LoanTransactionDownPaymentPostBusinessEvent.class));
|
||||
verify(businessEventNotifierService, Mockito.never()).notifyPostBusinessEvent(Mockito.any(LoanBalanceChangedBusinessEvent.class));
|
||||
verify(loanForProcessing, Mockito.times(1)).handleDownPayment(eq(disbursement), eq(command), eq(scheduleGeneratorDTO));
|
||||
verify(loanTransactionRepository, Mockito.never()).saveAndFlush(any(LoanTransaction.class));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -76,6 +76,7 @@ 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.LoanChargeValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||
@@ -193,6 +194,8 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
|
||||
private LoanTransactionAssembler loanTransactionAssembler;
|
||||
@Mock
|
||||
private LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
@Mock
|
||||
private LoanChargeValidator loanChargeValidator;
|
||||
|
||||
@Test
|
||||
void givenMerchantIssuedRefundTransactionWithRelatedTransactions_whenAdjustExistingTransaction_thenRelatedTransactionsAreReversedAndEventsTriggered() {
|
||||
@@ -235,7 +238,7 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
|
||||
// Mock methods called inside adjustExistingTransaction
|
||||
when(loan.findExistingTransactionIds()).thenReturn(Collections.emptyList());
|
||||
when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
|
||||
doNothing().when(loan).validateActivityNotBeforeClientOrGroupTransferDate(any(), any());
|
||||
doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
|
||||
when(loan.isClosedWrittenOff()).thenReturn(false);
|
||||
when(loan.isClosedObligationsMet()).thenReturn(false);
|
||||
when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
|
||||
@@ -282,7 +285,7 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
|
||||
// Mock methods called inside adjustExistingTransaction
|
||||
when(loan.findExistingTransactionIds()).thenReturn(Collections.emptyList());
|
||||
when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
|
||||
doNothing().when(loan).validateActivityNotBeforeClientOrGroupTransferDate(any(), any());
|
||||
doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
|
||||
when(loan.isClosedWrittenOff()).thenReturn(false);
|
||||
when(loan.isClosedObligationsMet()).thenReturn(false);
|
||||
when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
|
||||
|
||||
Reference in New Issue
Block a user