FINERACT-2181: Prepayment flow test fixes
This commit is contained in:
committed by
Arnold Gálovics
parent
cb82af119b
commit
a158c930bc
+2
-2
@@ -143,11 +143,11 @@ Feature: LoanAccrualTransaction
|
||||
And Admin successfully approves the loan on "01 January 2023" with "1000" amount and expected disbursement date on "01 January 2023"
|
||||
When Admin successfully disburse the loan on "01 January 2023" with "1000" EUR transaction amount
|
||||
When Admin sets the business date to "02 January 2023"
|
||||
And Customer makes "AUTOPAY" repayment on "02 January 2023" with 1010.19 EUR transaction amount
|
||||
And Customer makes "AUTOPAY" repayment on "02 January 2023" with 1000.33 EUR transaction amount
|
||||
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||
Then Loan Transactions tab has a transaction with date: "02 January 2023", and with the following data:
|
||||
| Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| Accrual | 10.19 | 0.0 | 10.19 | 0.0 | 0.0 | 0.0 |
|
||||
| Accrual | 0.33 | 0.0 | 0.33 | 0.0 | 0.0 | 0.0 |
|
||||
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "02 January 2023"
|
||||
|
||||
@TestRailId:C2683
|
||||
|
||||
+4
-10
@@ -164,6 +164,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
private final LoanBalanceService loanBalanceService;
|
||||
private final LoanTransactionService loanTransactionService;
|
||||
private final LoanAccountDomainServiceJpaHelper loanAccountDomainServiceJpaHelper;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -242,21 +243,14 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
LocalDate recalculateFrom = null;
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && loan.getLoanProduct().getProductInterestRecalculationDetails()
|
||||
.getPreCloseInterestCalculationStrategy().calculateTillPreClosureDateEnabled()) {
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
recalculateFrom = transactionDate;
|
||||
}
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTOForPrepay = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom,
|
||||
transactionDate, holidayDetailDto);
|
||||
|
||||
Money outstanding = loanTransactionProcessingService.fetchPrepaymentDetail(scheduleGeneratorDTOForPrepay, transactionDate, loan)
|
||||
.getTotalOutstanding();
|
||||
LocalDate recalculateTill = null;
|
||||
if (repaymentAmount.isGreaterThanOrEqualTo(outstanding) && loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& loan.getLoanProduct().getProductInterestRecalculationDetails().getPreCloseInterestCalculationStrategy()
|
||||
.calculateTillPreClosureDateEnabled()) {
|
||||
recalculateTill = transactionDate;
|
||||
}
|
||||
LocalDate recalculateTill = loanAccountDomainServiceJpaHelper.calculateRecalculateTillDate(loan, transactionDate,
|
||||
scheduleGeneratorDTOForPrepay, repaymentAmount);
|
||||
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom,
|
||||
recalculateTill, holidayDetailDto);
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.infrastructure.core.domain.BatchRequestContextHolder;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Slf4j
|
||||
public class LoanAccountDomainServiceJpaHelper {
|
||||
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
|
||||
public LocalDate calculateRecalculateTillDate(Loan loan, LocalDate transactionDate, ScheduleGeneratorDTO scheduleGeneratorDTOForPrepay,
|
||||
Money repaymentAmount) {
|
||||
LocalDate recalculateTill = null;
|
||||
try {
|
||||
if (FineractRequestContextHolder.isBatchRequest() && BatchRequestContextHolder.isEnclosingTransaction()) {
|
||||
// In case of Batch requests with enclosing transaction, the current way of calculating the prepayment
|
||||
// amount (since it changes
|
||||
// the state of entities which would be written back to the DB) is incorrect, so we won't allow it for
|
||||
// now.
|
||||
// With enclosing transactions where the loan is created and repaid within the same batch request, due
|
||||
// to REQUIRES_NEW, this method
|
||||
// will simply not see that a loan has been created.
|
||||
// Temporarily if you wanna use the batch API for prepayment, make sure to split the requests in a way
|
||||
// that loan creation and
|
||||
// repayment doesn't occur in the same batch request.
|
||||
// Example testcase:
|
||||
// org.apache.fineract.integrationtests.BatchApiTest.shouldReturnOkStatusOnSuccessfulGetDatatableEntryWithNoQueryParam
|
||||
// TODO: this can be removed if the prepayment amount calculation below this is fixed in a way that it
|
||||
// doesn't change any entity
|
||||
// but works with DTOs
|
||||
return null;
|
||||
}
|
||||
loan = loanAssembler.assembleFrom(loan.getId());
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled() && loan.getLoanProduct().getProductInterestRecalculationDetails()
|
||||
.getPreCloseInterestCalculationStrategy().calculateTillPreClosureDateEnabled()) {
|
||||
Money outstanding = loanTransactionProcessingService
|
||||
.fetchPrepaymentDetail(scheduleGeneratorDTOForPrepay, transactionDate, loan).getTotalOutstanding();
|
||||
if (repaymentAmount.isGreaterThanOrEqualTo(outstanding)) {
|
||||
recalculateTill = transactionDate;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO: there's a bug where the prepayment calculation fails
|
||||
// the test-case is org.apache.fineract.integrationtests.LoanTransactionAccrualActivityPostingTest.test in
|
||||
// the integration-tests
|
||||
// seems like it occurs only on CUMULATIVE loans, not PROGRESSIVE
|
||||
log.warn("Unable to calculate prepayment amount", e);
|
||||
}
|
||||
return recalculateTill;
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.FlushModeType;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -35,19 +37,22 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
@Transactional
|
||||
public class LoanPointInTimeServiceImpl implements LoanPointInTimeService {
|
||||
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanPointInTimeData.Mapper dataMapper;
|
||||
private final EntityManager entityManager;
|
||||
|
||||
@Override
|
||||
public LoanPointInTimeData retrieveAt(Long loanId, LocalDate date) {
|
||||
entityManager.setFlushMode(FlushModeType.COMMIT);
|
||||
validateSingularRetrieval(loanId, date);
|
||||
|
||||
// Note: since everything is running in a readOnly transaction
|
||||
@@ -66,6 +71,8 @@ public class LoanPointInTimeServiceImpl implements LoanPointInTimeService {
|
||||
|
||||
return dataMapper.map(loan);
|
||||
} finally {
|
||||
entityManager.clear();
|
||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
||||
ThreadLocalContextUtil.setBusinessDates(originalBDs);
|
||||
}
|
||||
}
|
||||
@@ -89,7 +96,9 @@ public class LoanPointInTimeServiceImpl implements LoanPointInTimeService {
|
||||
@Override
|
||||
public List<LoanPointInTimeData> retrieveAt(List<Long> loanIds, LocalDate date) {
|
||||
validateBulkRetrieval(loanIds, date);
|
||||
return loanIds.stream().map(loanId -> retrieveAt(loanId, date)).toList();
|
||||
List<LoanPointInTimeData> result = loanIds.stream().map(loanId -> retrieveAt(loanId, date)).toList();
|
||||
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateBulkRetrieval(List<Long> loanIds, LocalDate date) {
|
||||
|
||||
+1
-1
@@ -342,7 +342,7 @@ public class LoanPointInTimeTest extends BaseLoanIntegrationTest {
|
||||
transaction(500.0, "Repayment", "09 February 2023"), //
|
||||
transaction(500.0, "Repayment", "01 March 2023"), //
|
||||
transaction(5032.52, "Repayment", "05 March 2023"), //
|
||||
transaction(1282.52, "Accrual", "05 March 2023") //
|
||||
transaction(1110.08, "Accrual", "05 March 2023") //
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user