FINERACT-2354: Allow re-age today if the previous re-age was reversed
This commit is contained in:
committed by
Adam Saghy
parent
5b1d72d15b
commit
7ee1919ddb
+6
-76
@@ -314,36 +314,6 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
|
|||||||
Optional<LocalDate> findLastNonReversedTransactionDateByLoanAndTypes(@Param("loan") Loan loan,
|
Optional<LocalDate> findLastNonReversedTransactionDateByLoanAndTypes(@Param("loan") Loan loan,
|
||||||
@Param("types") Set<LoanTransactionType> types);
|
@Param("types") Set<LoanTransactionType> types);
|
||||||
|
|
||||||
@Query("""
|
|
||||||
SELECT lt FROM LoanTransaction lt
|
|
||||||
WHERE lt.loan = :loan
|
|
||||||
AND (
|
|
||||||
(lt.reversed = true AND lt.id IN :existingTransactionIds AND lt.id NOT IN :existingReversedTransactionIds)
|
|
||||||
OR (lt.id NOT IN :existingTransactionIds)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
List<LoanTransaction> findTransactionsForAccountingBridge(@Param("loan") Loan loan,
|
|
||||||
@Param("existingTransactionIds") List<Long> existingTransactionIds,
|
|
||||||
@Param("existingReversedTransactionIds") List<Long> existingReversedTransactionIds);
|
|
||||||
|
|
||||||
@Query("""
|
|
||||||
SELECT lt FROM LoanTransaction lt
|
|
||||||
WHERE lt.loan = :loan
|
|
||||||
AND (
|
|
||||||
(lt.reversed = true AND lt.id IN :existingTransactionIds)
|
|
||||||
OR (lt.id NOT IN :existingTransactionIds)
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
List<LoanTransaction> findTransactionsForAccountingBridge(@Param("loan") Loan loan,
|
|
||||||
@Param("existingTransactionIds") List<Long> existingTransactionIds);
|
|
||||||
|
|
||||||
@Query("""
|
|
||||||
SELECT lt FROM LoanTransaction lt
|
|
||||||
WHERE lt.loan = :loan
|
|
||||||
AND lt.reversed = false
|
|
||||||
""")
|
|
||||||
List<LoanTransaction> findNonReversedByLoan(@Param("loan") Loan loan);
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT lt FROM LoanTransaction lt
|
SELECT lt FROM LoanTransaction lt
|
||||||
WHERE lt.loan = :loan
|
WHERE lt.loan = :loan
|
||||||
@@ -482,54 +452,14 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
|
|||||||
BigDecimal calculateTotalRecoveryPaymentAmount(@Param("loan") Loan loan);
|
BigDecimal calculateTotalRecoveryPaymentAmount(@Param("loan") Loan loan);
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT lt FROM LoanTransaction lt
|
SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END
|
||||||
|
FROM LoanTransaction lt
|
||||||
WHERE lt.loan = :loan
|
WHERE lt.loan = :loan
|
||||||
AND (
|
|
||||||
(:dateComparison = 'BEFORE' AND lt.dateOf < :chargeOffDate) OR
|
|
||||||
(:dateComparison = 'EQUAL' AND lt.dateOf = :chargeOffDate) OR
|
|
||||||
(:dateComparison = 'AFTER' AND lt.dateOf > :chargeOffDate)
|
|
||||||
)
|
|
||||||
AND (
|
|
||||||
(lt.reversed = true AND lt.id IN :existingTransactionIds AND lt.id NOT IN :existingReversedTransactionIds)
|
|
||||||
OR (lt.id NOT IN :existingTransactionIds)
|
|
||||||
)
|
|
||||||
ORDER BY lt.dateOf, lt.createdDate, lt.id
|
|
||||||
""")
|
|
||||||
List<LoanTransaction> findTransactionsForChargeOffClassification(@Param("loan") Loan loan,
|
|
||||||
@Param("chargeOffDate") LocalDate chargeOffDate, @Param("dateComparison") String dateComparison,
|
|
||||||
@Param("existingTransactionIds") List<Long> existingTransactionIds,
|
|
||||||
@Param("existingReversedTransactionIds") List<Long> existingReversedTransactionIds);
|
|
||||||
|
|
||||||
@Query("""
|
|
||||||
SELECT lt FROM LoanTransaction lt
|
|
||||||
WHERE lt.loan = :loan
|
|
||||||
AND (
|
|
||||||
(:dateComparison = 'BEFORE' AND lt.dateOf < :chargeOffDate) OR
|
|
||||||
(:dateComparison = 'EQUAL' AND lt.dateOf = :chargeOffDate) OR
|
|
||||||
(:dateComparison = 'AFTER' AND lt.dateOf > :chargeOffDate)
|
|
||||||
)
|
|
||||||
AND (
|
|
||||||
(lt.reversed = true AND lt.id IN :existingTransactionIds)
|
|
||||||
OR (lt.id NOT IN :existingTransactionIds)
|
|
||||||
)
|
|
||||||
ORDER BY lt.dateOf, lt.createdDate, lt.id
|
|
||||||
""")
|
|
||||||
List<LoanTransaction> findTransactionsForChargeOffClassification(@Param("loan") Loan loan,
|
|
||||||
@Param("chargeOffDate") LocalDate chargeOffDate, @Param("dateComparison") String dateComparison,
|
|
||||||
@Param("existingTransactionIds") List<Long> existingTransactionIds);
|
|
||||||
|
|
||||||
@Query("""
|
|
||||||
SELECT lt FROM LoanTransaction lt
|
|
||||||
WHERE lt.loan = :loan
|
|
||||||
AND (
|
|
||||||
(:dateComparison = 'BEFORE' AND lt.dateOf < :chargeOffDate) OR
|
|
||||||
(:dateComparison = 'EQUAL' AND lt.dateOf = :chargeOffDate) OR
|
|
||||||
(:dateComparison = 'AFTER' AND lt.dateOf > :chargeOffDate)
|
|
||||||
)
|
|
||||||
AND lt.reversed = false
|
AND lt.reversed = false
|
||||||
ORDER BY lt.dateOf, lt.createdDate, lt.id
|
AND lt.typeOf = :type
|
||||||
|
AND lt.dateOf = :transactionDate
|
||||||
""")
|
""")
|
||||||
List<LoanTransaction> findTransactionsForChargeOffClassification(@Param("loan") Loan loan,
|
boolean existsNonReversedByLoanAndTypeAndDate(@Param("loan") Loan loan, @Param("type") LoanTransactionType type,
|
||||||
@Param("chargeOffDate") LocalDate chargeOffDate, @Param("dateComparison") String dateComparison);
|
@Param("transactionDate") LocalDate transactionDate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-2
@@ -25,6 +25,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||||
@@ -34,14 +35,19 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanReAgingApiConstants;
|
import org.apache.fineract.portfolio.loanaccount.api.LoanReAgingApiConstants;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ChangeOperation;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.ChangeOperation;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class LoanReAgingValidator {
|
public class LoanReAgingValidator {
|
||||||
|
|
||||||
|
private final LoanTransactionRepository loanTransactionRepository;
|
||||||
|
|
||||||
public void validateReAge(Loan loan, JsonCommand command) {
|
public void validateReAge(Loan loan, JsonCommand command) {
|
||||||
validateReAgeRequest(loan, command);
|
validateReAgeRequest(loan, command);
|
||||||
validateReAgeBusinessRules(loan);
|
validateReAgeBusinessRules(loan);
|
||||||
@@ -107,8 +113,9 @@ public class LoanReAgingValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validate if there's already a re-aging transaction for today
|
// validate if there's already a re-aging transaction for today
|
||||||
boolean isReAgingTransactionForTodayPresent = loan.getLoanTransactions().stream()
|
final boolean isReAgingTransactionForTodayPresent = loanTransactionRepository.existsNonReversedByLoanAndTypeAndDate(loan,
|
||||||
.anyMatch(tx -> tx.getTypeOf().isReAge() && tx.getTransactionDate().equals(getBusinessLocalDate()));
|
LoanTransactionType.REAGE, getBusinessLocalDate());
|
||||||
|
|
||||||
if (isReAgingTransactionForTodayPresent) {
|
if (isReAgingTransactionForTodayPresent) {
|
||||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.reage.reage.transaction.already.present.for.today",
|
throw new GeneralPlatformDomainRuleException("error.msg.loan.reage.reage.transaction.already.present.for.today",
|
||||||
"Loan reaging can only be done once a day. There has already been a reaging done for today", loan.getId());
|
"Loan reaging can only be done once a day. There has already been a reaging done for today", loan.getId());
|
||||||
|
|||||||
+19
-7
@@ -34,7 +34,6 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
|
||||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ActionContext;
|
import org.apache.fineract.infrastructure.core.domain.ActionContext;
|
||||||
@@ -46,6 +45,7 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactionProcessor;
|
||||||
@@ -54,18 +54,30 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
|
|||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.mockito.junit.jupiter.MockitoSettings;
|
||||||
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
@SuppressFBWarnings({ "VA_FORMAT_STRING_USES_NEWLINE" })
|
@SuppressFBWarnings({ "VA_FORMAT_STRING_USES_NEWLINE" })
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
class LoanReAgingValidatorTest {
|
class LoanReAgingValidatorTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoanTransactionRepository loanTransactionRepository;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private LoanReAgingValidator underTest;
|
||||||
|
|
||||||
public static final String DATE_FORMAT = "dd MMMM yyyy";
|
public static final String DATE_FORMAT = "dd MMMM yyyy";
|
||||||
private final LocalDate actualDate = LocalDate.now(Clock.systemUTC());
|
private final LocalDate actualDate = LocalDate.now(Clock.systemUTC());
|
||||||
private final LocalDate maturityDate = actualDate.plusDays(30);
|
private final LocalDate maturityDate = actualDate.plusDays(30);
|
||||||
private final LocalDate businessDate = maturityDate.plusDays(1);
|
private final LocalDate businessDate = maturityDate.plusDays(1);
|
||||||
private final LocalDate afterMaturity = maturityDate.plusDays(7);
|
private final LocalDate afterMaturity = maturityDate.plusDays(7);
|
||||||
|
|
||||||
private LoanReAgingValidator underTest = new LoanReAgingValidator();
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||||
@@ -92,7 +104,8 @@ class LoanReAgingValidatorTest {
|
|||||||
public void testValidateReAge_ShouldThrowException_WhenExternalIdIsLongerThan100() {
|
public void testValidateReAge_ShouldThrowException_WhenExternalIdIsLongerThan100() {
|
||||||
// given
|
// given
|
||||||
Loan loan = loan();
|
Loan loan = loan();
|
||||||
JsonCommand command = jsonCommand(RandomStringUtils.randomAlphabetic(120));
|
String longExternalId = "A".repeat(120);
|
||||||
|
JsonCommand command = jsonCommand(longExternalId);
|
||||||
// when
|
// when
|
||||||
PlatformApiDataValidationException result = assertThrows(PlatformApiDataValidationException.class,
|
PlatformApiDataValidationException result = assertThrows(PlatformApiDataValidationException.class,
|
||||||
() -> underTest.validateReAge(loan, command));
|
() -> underTest.validateReAge(loan, command));
|
||||||
@@ -367,10 +380,9 @@ class LoanReAgingValidatorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testValidateReAge_ShouldThrowException_WhenLoanAlreadyHasReAgeForToday() {
|
public void testValidateReAge_ShouldThrowException_WhenLoanAlreadyHasReAgeForToday() {
|
||||||
// given
|
// given
|
||||||
List<LoanTransaction> transactions = List.of(loanTransaction(LoanTransactionType.DISBURSEMENT, maturityDate.minusDays(2)),
|
|
||||||
loanTransaction(LoanTransactionType.REAGE, businessDate));
|
|
||||||
Loan loan = loan();
|
Loan loan = loan();
|
||||||
given(loan.getLoanTransactions()).willReturn(transactions);
|
given(loanTransactionRepository.existsNonReversedByLoanAndTypeAndDate(loan, LoanTransactionType.REAGE, businessDate))
|
||||||
|
.willReturn(true);
|
||||||
JsonCommand command = jsonCommand();
|
JsonCommand command = jsonCommand();
|
||||||
// when
|
// when
|
||||||
GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class,
|
GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class,
|
||||||
|
|||||||
Reference in New Issue
Block a user