FINERACT-2421: Run inline COB on newly created loans that are behind
This commit is contained in:
committed by
Arnold Gálovics
parent
967e0ea306
commit
f6d013c68d
@@ -1961,3 +1961,33 @@ Feature: LoanAccrualTransaction
|
||||
When Loan Pay-off is made on "01 July 2024"
|
||||
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
|
||||
When Admin set "LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_ACCRUAL_ACTIVITY_POSTING" loan product "MERCHANT_ISSUED_REFUND" transaction type to "REAMORTIZATION" future installment allocation rule
|
||||
|
||||
@TestRailId:C4627
|
||||
Scenario: Verify accrual date matches charge creation date when repayment happens before COB run
|
||||
When Admin sets the business date to "17 November 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_ACCRUAL_ACTIVITY | 17 November 2025 | 100 | 0 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 30 | DAYS | 30 | DAYS | 1 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "17 November 2025" with "100" amount and expected disbursement date on "17 November 2025"
|
||||
When Admin successfully disburse the loan on "17 November 2025" with "100" EUR transaction amount
|
||||
When Admin adds "LOAN_SNOOZE_FEE" due date charge with "17 November 2025" due date and 10 EUR transaction amount
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 17 November 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 |
|
||||
# --- Date changes to next day (post-midnight but before COB) ---
|
||||
When Admin sets the business date to "18 November 2025"
|
||||
# --- Full repayment made before COB runs ---
|
||||
When Admin creates new user with "NO_BYPASS_AUTOTEST" username, "NO_BYPASS_AUTOTEST_ROLE" role name and given permissions:
|
||||
| REPAYMENT_LOAN |
|
||||
And Created user makes "AUTOPAY" repayment on "18 November 2025" with 110 EUR transaction amount
|
||||
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||
# --- Expected: Accrual transaction date should be 17 November 2025 (charge creation date) ---
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
|
||||
| 17 November 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 |
|
||||
| 18 November 2025 | Repayment | 110.0 | 100.0 | 0.0 | 10.0 | 0.0 | 0.0 |
|
||||
| 17 November 2025 | Accrual | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
|
||||
| 18 November 2025 | Accrual Activity | 10.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 |
|
||||
Then LoanAccrualTransactionCreatedBusinessEvent is raised on "17 November 2025"
|
||||
Then LoanTransactionAccrualActivityPostBusinessEvent is raised on "18 November 2025"
|
||||
|
||||
+4
@@ -100,6 +100,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
|
||||
String FIND_ALL_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_NOT_NULL_AND_MIN_AND_MAX_LOAN_ID_AND_STATUSES = "select loan.id from Loan loan where loan.id BETWEEN :minLoanId and :maxLoanId and loan.loanStatus in :loanStatuses and :cobBusinessDate = loan.lastClosedBusinessDate";
|
||||
String FIND_ALL_LOANS_BEHIND_BY_LOAN_IDS_AND_STATUSES = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in :loanStatuses and loan.lastClosedBusinessDate < :cobBusinessDate";
|
||||
String FIND_ALL_LOANS_BEHIND_ON_DISBURSEMENT_DATE = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in :loanStatuses and loan.lastClosedBusinessDate IS NULL and loan.actualDisbursementDate = :cobBusinessDate";
|
||||
|
||||
String FIND_ALL_STAYED_LOCKED_BY_COB_BUSINESS_DATE = "select loan.id, loan.externalId, loan.accountNumber from LoanAccountLock lock left join Loan loan on lock.loanId = loan.id where lock.lockPlacedOnCobBusinessDate = :cobBusinessDate";
|
||||
|
||||
@@ -275,4 +276,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
@Query("select loan.loanRepaymentScheduleDetail.enableBuyDownFee from Loan loan where loan.id = :loanId")
|
||||
Boolean isEnabledBuyDownFee(@Param("loanId") Long loanId);
|
||||
|
||||
@Query(FIND_ALL_LOANS_BEHIND_ON_DISBURSEMENT_DATE)
|
||||
List<COBIdAndLastClosedBusinessDate> findAllLoansBehindOnDisbursementDate(@Param("cobBusinessDate") LocalDate cobBusinessDate,
|
||||
@Param("loanIds") List<Long> loanIds, @Param("loanStatuses") Collection<LoanStatus> loanStatuses);
|
||||
}
|
||||
|
||||
+5
@@ -79,6 +79,11 @@ public class RetrieveAllNonClosedLoanIdServiceImpl implements RetrieveLoanIdServ
|
||||
return loanRepository.findAllLoansBehindByLoanIdsAndStatuses(businessDate, loanIds, NON_CLOSED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<COBIdAndLastClosedBusinessDate> retrieveLoanBehindOnDisbursementDate(LocalDate businessDate, List<Long> loanIds) {
|
||||
return loanRepository.findAllLoansBehindOnDisbursementDate(businessDate, loanIds, NON_CLOSED_LOAN_STATUSES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<COBIdAndLastClosedBusinessDate> retrieveLoanIdsBehindDateOrNull(LocalDate businessDate, List<Long> loanIds) {
|
||||
return loanRepository.findAllLoansBehindOrNullByLoanIdsAndStatuses(businessDate, loanIds, NON_CLOSED_LOAN_STATUSES);
|
||||
|
||||
+1
@@ -40,4 +40,5 @@ public interface RetrieveLoanIdService {
|
||||
|
||||
List<COBIdAndExternalIdAndAccountNo> findAllStayedLockedByCobBusinessDate(@Param("cobBusinessDate") LocalDate cobBusinessDate);
|
||||
|
||||
List<COBIdAndLastClosedBusinessDate> retrieveLoanBehindOnDisbursementDate(LocalDate businessDateByType, List<Long> loanIds);
|
||||
}
|
||||
|
||||
+8
-5
@@ -40,6 +40,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.batch.domain.BatchRequest;
|
||||
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
|
||||
import org.apache.fineract.cob.data.COBIdAndLastClosedBusinessDate;
|
||||
import org.apache.fineract.cob.loan.LoanCOBConstant;
|
||||
import org.apache.fineract.cob.loan.RetrieveLoanIdService;
|
||||
import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
|
||||
import org.apache.fineract.cob.service.LoanAccountLockService;
|
||||
@@ -85,8 +86,6 @@ public class LoanCOBFilterHelper implements InitializingBean {
|
||||
private static final Predicate<String> URL_FUNCTION = s -> LOAN_PATH_PATTERN.matcher(s).find()
|
||||
|| LOAN_GLIMACCOUNT_PATH_PATTERN.matcher(s).find();
|
||||
|
||||
private static final String JOB_NAME = "INLINE_LOAN_COB";
|
||||
|
||||
private Long getLoanId(boolean isGlim, String pathInfo) {
|
||||
if (!isGlim) {
|
||||
String id = LOAN_PATH_PATTERN.matcher(pathInfo).replaceAll("$1");
|
||||
@@ -197,8 +196,12 @@ public class LoanCOBFilterHelper implements InitializingBean {
|
||||
public boolean isLoanBehind(List<Long> loanIds) {
|
||||
List<COBIdAndLastClosedBusinessDate> loanIdAndLastClosedBusinessDates = new ArrayList<>();
|
||||
List<List<Long>> partitions = Lists.partition(loanIds, fineractProperties.getQuery().getInClauseParameterSizeLimit());
|
||||
partitions.forEach(partition -> loanIdAndLastClosedBusinessDates.addAll(retrieveLoanIdService
|
||||
.retrieveLoanIdsBehindDate(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE), partition)));
|
||||
partitions.forEach(partition -> {
|
||||
loanIdAndLastClosedBusinessDates.addAll(retrieveLoanIdService
|
||||
.retrieveLoanIdsBehindDate(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE), partition));
|
||||
loanIdAndLastClosedBusinessDates.addAll(retrieveLoanIdService.retrieveLoanBehindOnDisbursementDate(
|
||||
ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE), partition));
|
||||
});
|
||||
return CollectionUtils.isNotEmpty(loanIdAndLastClosedBusinessDates);
|
||||
}
|
||||
|
||||
@@ -269,7 +272,7 @@ public class LoanCOBFilterHelper implements InitializingBean {
|
||||
}
|
||||
|
||||
public void executeInlineCob(List<Long> loanIds) {
|
||||
inlineLoanCOBExecutorService.execute(loanIds, JOB_NAME);
|
||||
inlineLoanCOBExecutorService.execute(loanIds, LoanCOBConstant.INLINE_LOAN_COB_JOB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user