FINERACT-2153: Enable immediate charge accrual creation post maturity

This commit is contained in:
leksinomi
2024-11-27 17:49:42 +01:00
committed by Adam Saghy
parent 1ef9c0f4bb
commit 32cc83fdbf
8 changed files with 233 additions and 3 deletions
@@ -0,0 +1,169 @@
/**
* 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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.LocalDate;
import java.util.stream.Stream;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
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.loanproduct.domain.LoanProductRelatedDetail;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class LoanChargeWritePlatformServiceImplTest {
private static final Long LOAN_ID = 1L;
private static final Integer SPECIFIED_DUE_DATE = 2;
private static final LocalDate MATURITY_DATE = LocalDate.of(2024, 2, 15);
private static final LocalDate BUSINESS_DATE_AFTER = LocalDate.of(2024, 2, 26);
private static final LocalDate BUSINESS_DATE_ON = MATURITY_DATE;
private static final LocalDate BUSINESS_DATE_BEFORE = LocalDate.of(2024, 2, 14);
@InjectMocks
private LoanChargeWritePlatformServiceImpl loanChargeWritePlatformService;
@Mock
private JsonCommand jsonCommand;
@Mock
private LoanChargeApiJsonValidator loanChargeApiJsonValidator;
@Mock
private LoanAssembler loanAssembler;
@Mock
private Loan loan;
@Mock
private ChargeRepositoryWrapper chargeRepository;
@Mock
private Charge chargeDefinition;
@Mock
private LoanChargeAssembler loanChargeAssembler;
@Mock
private LoanCharge loanCharge;
@Mock
private BusinessEventNotifierService businessEventNotifierService;
@Mock
private LoanProductRelatedDetail loanRepaymentScheduleDetail;
@Mock
private LoanChargeRepository loanChargeRepository;
@Mock
private ConfigurationDomainService configurationDomainService;
@Mock
private LoanTransactionRepository loanTransactionRepository;
@Mock
private LoanTransaction loanTransaction;
@Mock
private LoanAccountDomainService loanAccountDomainService;
@Mock
private MonetaryCurrency monetaryCurrency;
@Mock
private JournalEntryWritePlatformService journalEntryWritePlatformService;
@BeforeEach
void setUp() {
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
when(chargeRepository.findOneWithNotFoundDetection(anyLong())).thenReturn(chargeDefinition);
when(chargeDefinition.getChargeTimeType()).thenReturn(SPECIFIED_DUE_DATE);
when(loanChargeAssembler.createNewFromJson(loan, chargeDefinition, jsonCommand)).thenReturn(loanCharge);
when(loan.repaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
when(loan.hasCurrencyCodeOf(any())).thenReturn(true);
when(loanCharge.getChargePaymentMode()).thenReturn(ChargePaymentMode.REGULAR);
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
when(loanChargeRepository.saveAndFlush(any(LoanCharge.class))).thenReturn(loanCharge);
when(loan.getCurrency()).thenReturn(monetaryCurrency);
when(loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(any())).thenReturn(loan);
}
@ParameterizedTest
@MethodSource("loanChargeAccrualTestCases")
void shouldHandleAccrualBasedOnConfigurationAndDates(boolean isAccrualEnabled, LocalDate businessDate, LocalDate maturityDate, boolean isAccrualExpected) {
when(configurationDomainService.isImmediateChargeAccrualPostMaturityEnabled()).thenReturn(isAccrualEnabled);
when(loan.getMaturityDate()).thenReturn(maturityDate);
when(loan.handleChargeAppliedTransaction(loanCharge, null)).thenReturn(loanTransaction);
try (MockedStatic<DateUtils> mockedDateUtils = mockStatic(DateUtils.class)) {
mockedDateUtils.when(DateUtils::getBusinessLocalDate).thenReturn(businessDate);
loanChargeWritePlatformService.addLoanCharge(LOAN_ID, jsonCommand);
}
if (isAccrualExpected) {
verify(loanTransactionRepository, times(1)).saveAndFlush(any(LoanTransaction.class));
verify(businessEventNotifierService, times(1)).notifyPostBusinessEvent(any(LoanAccrualTransactionCreatedBusinessEvent.class));
} else {
verify(loanTransactionRepository, never()).saveAndFlush(any(LoanTransaction.class));
verify(businessEventNotifierService, never()).notifyPostBusinessEvent(any(LoanAccrualTransactionCreatedBusinessEvent.class));
}
}
private static Stream<Arguments> loanChargeAccrualTestCases() {
return Stream.of(Arguments.of(true, BUSINESS_DATE_AFTER, MATURITY_DATE, true),
Arguments.of(false, BUSINESS_DATE_AFTER, MATURITY_DATE, false), Arguments.of(true, BUSINESS_DATE_ON, MATURITY_DATE, false),
Arguments.of(true, BUSINESS_DATE_BEFORE, MATURITY_DATE, false));
}
}