| 
34 | 34 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor;  | 
35 | 35 | import org.springframework.data.jpa.repository.Query;  | 
36 | 36 | import org.springframework.data.repository.query.Param;  | 
 | 37 | +import org.springframework.lang.NonNull;  | 
37 | 38 | 
 
  | 
38 | 39 | public interface LoanTransactionRepository extends JpaRepository<LoanTransaction, Long>, JpaSpecificationExecutor<LoanTransaction> {  | 
39 | 40 | 
 
  | 
 | 41 | +    // Predefined transaction type sets for optimized queries  | 
 | 42 | +    Set<LoanTransactionType> REPAYMENT_LIKE_TYPES = Set.of(LoanTransactionType.REPAYMENT, LoanTransactionType.RECOVERY_REPAYMENT,  | 
 | 43 | +            LoanTransactionType.MERCHANT_ISSUED_REFUND, LoanTransactionType.PAYOUT_REFUND, LoanTransactionType.GOODWILL_CREDIT,  | 
 | 44 | +            LoanTransactionType.CHARGE_REFUND, LoanTransactionType.DOWN_PAYMENT, LoanTransactionType.REFUND,  | 
 | 45 | +            LoanTransactionType.REFUND_FOR_ACTIVE_LOAN, LoanTransactionType.CREDIT_BALANCE_REFUND, LoanTransactionType.CHARGEBACK,  | 
 | 46 | +            LoanTransactionType.INTEREST_PAYMENT_WAIVER, LoanTransactionType.INTEREST_REFUND,  | 
 | 47 | +            LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT, LoanTransactionType.CHARGE_ADJUSTMENT,  | 
 | 48 | +            LoanTransactionType.REPAYMENT_AT_DISBURSEMENT);  | 
 | 49 | + | 
 | 50 | +    Set<LoanTransactionType> EXCLUDED_FROM_COB_TYPES = Set.of(LoanTransactionType.CONTRA, LoanTransactionType.MARKED_FOR_RESCHEDULING,  | 
 | 51 | +            LoanTransactionType.APPROVE_TRANSFER, LoanTransactionType.INITIATE_TRANSFER, LoanTransactionType.REJECT_TRANSFER,  | 
 | 52 | +            LoanTransactionType.WITHDRAW_TRANSFER);  | 
 | 53 | + | 
 | 54 | +    Set<LoanTransactionType> EXCLUDED_FROM_RECEIVABLE_INTEREST = Set.of(LoanTransactionType.REPAYMENT_AT_DISBURSEMENT,  | 
 | 55 | +            LoanTransactionType.DISBURSEMENT);  | 
 | 56 | + | 
40 | 57 |     Optional<LoanTransaction> findByIdAndLoanId(Long transactionId, Long loanId);  | 
41 | 58 | 
 
  | 
42 | 59 |     @Query("""  | 
@@ -402,20 +419,14 @@ SELECT MAX(lt.dateOf) FROM LoanTransaction lt  | 
402 | 419 |             WHERE lt.loan = :loan  | 
403 | 420 |                 AND lt.reversed = false  | 
404 | 421 |                 AND lt.amount > 0  | 
405 |  | -                AND lt.typeOf IN (  | 
406 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT,  | 
407 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MERCHANT_ISSUED_REFUND,  | 
408 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.PAYOUT_REFUND,  | 
409 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.GOODWILL_CREDIT,  | 
410 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_REFUND,  | 
411 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CHARGE_ADJUSTMENT,  | 
412 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.DOWN_PAYMENT,  | 
413 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INTEREST_PAYMENT_WAIVER,  | 
414 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INTEREST_REFUND,  | 
415 |  | -                    org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT  | 
416 |  | -                )  | 
 | 422 | +                AND lt.typeOf IN :repaymentLikeTypes  | 
417 | 423 |             """)  | 
418 |  | -    Optional<LocalDate> findLastRepaymentLikeTransactionDate(@Param("loan") Loan loan);  | 
 | 424 | +    Optional<LocalDate> findLastRepaymentLikeTransactionDate(@Param("loan") Loan loan,  | 
 | 425 | +            @Param("repaymentLikeTypes") Set<LoanTransactionType> repaymentLikeTypes);  | 
 | 426 | + | 
 | 427 | +    default Optional<LocalDate> findLastRepaymentLikeTransactionDate(Loan loan) {  | 
 | 428 | +        return findLastRepaymentLikeTransactionDate(loan, REPAYMENT_LIKE_TYPES);  | 
 | 429 | +    }  | 
419 | 430 | 
 
  | 
420 | 431 |     @Query("""  | 
421 | 432 |             SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END  | 
@@ -483,4 +494,132 @@ SELECT COALESCE(SUM(lt.amount), 0)  | 
483 | 494 |     BigDecimal sumTotalAmountByLoanAndTransactionTypes(@Param("loan") Loan loan,  | 
484 | 495 |             @Param("loanTransactionTypes") List<LoanTransactionType> loanTransactionTypes);  | 
485 | 496 | 
 
  | 
 | 497 | +    // COB Transaction Query for optimization  | 
 | 498 | +    @Query("""  | 
 | 499 | +            SELECT lt FROM LoanTransaction lt  | 
 | 500 | +            WHERE lt.loan = :loan  | 
 | 501 | +              AND lt.loan IS NOT NULL  | 
 | 502 | +              AND lt.reversed = false  | 
 | 503 | +              AND lt.dateOf <= :cobDate  | 
 | 504 | +              AND lt.typeOf NOT IN :excludedTypes  | 
 | 505 | +            ORDER BY lt.dateOf, lt.createdDate, lt.id  | 
 | 506 | +            """)  | 
 | 507 | +    @NonNull  | 
 | 508 | +    List<LoanTransaction> findTransactionsForCOB(@NonNull @Param("loan") Loan loan, @NonNull @Param("cobDate") LocalDate cobDate,  | 
 | 509 | +            @Param("excludedTypes") Set<LoanTransactionType> excludedTypes);  | 
 | 510 | + | 
 | 511 | +    @NonNull  | 
 | 512 | +    default List<LoanTransaction> findTransactionsForCOB(@NonNull Loan loan, @NonNull LocalDate cobDate) {  | 
 | 513 | +        return findTransactionsForCOB(loan, cobDate, EXCLUDED_FROM_COB_TYPES);  | 
 | 514 | +    }  | 
 | 515 | + | 
 | 516 | +    // Payment Transactions Query for optimization  | 
 | 517 | +    @Query("""  | 
 | 518 | +            SELECT lt  | 
 | 519 | +            FROM LoanTransaction lt  | 
 | 520 | +            WHERE lt.loan = :loan  | 
 | 521 | +                AND lt.reversed = false  | 
 | 522 | +                AND lt.typeOf IN :repaymentLikeTypes  | 
 | 523 | +            ORDER BY lt.dateOf ASC, lt.createdDate ASC, lt.id ASC  | 
 | 524 | +            """)  | 
 | 525 | +    List<LoanTransaction> findPaymentTransactionsByLoan(@Param("loan") Loan loan,  | 
 | 526 | +            @Param("repaymentLikeTypes") Set<LoanTransactionType> repaymentLikeTypes);  | 
 | 527 | + | 
 | 528 | +    default List<LoanTransaction> findPaymentTransactionsByLoan(Loan loan) {  | 
 | 529 | +        return findPaymentTransactionsByLoan(loan, REPAYMENT_LIKE_TYPES);  | 
 | 530 | +    }  | 
 | 531 | + | 
 | 532 | +    // Disbursement Transactions Query for optimization  | 
 | 533 | +    @Query("""  | 
 | 534 | +            SELECT lt  | 
 | 535 | +            FROM LoanTransaction lt  | 
 | 536 | +            WHERE lt.loan = :loan  | 
 | 537 | +                AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.DISBURSEMENT  | 
 | 538 | +            ORDER BY lt.dateOf DESC, lt.createdDate DESC, lt.id DESC  | 
 | 539 | +            """)  | 
 | 540 | +    List<LoanTransaction> findDisbursementTransactionsByLoanOrderByDateOfDesc(@Param("loan") Loan loan, Pageable pageable);  | 
 | 541 | + | 
 | 542 | +    default Optional<LoanTransaction> findLastDisbursementTransactionByLoan(Loan loan) {  | 
 | 543 | +        List<LoanTransaction> results = findDisbursementTransactionsByLoanOrderByDateOfDesc(loan, Pageable.ofSize(1));  | 
 | 544 | +        return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));  | 
 | 545 | +    }  | 
 | 546 | + | 
 | 547 | +    // Overpayment Calculation Query for optimization  | 
 | 548 | +    @Query("""  | 
 | 549 | +            SELECT lt FROM LoanTransaction lt  | 
 | 550 | +            WHERE lt.loan = :loan  | 
 | 551 | +              AND lt.loan IS NOT NULL  | 
 | 552 | +              AND lt.reversed = false  | 
 | 553 | +              AND lt.typeOf IN :repaymentLikeTypes  | 
 | 554 | +            ORDER BY lt.dateOf, lt.createdDate, lt.id  | 
 | 555 | +            """)  | 
 | 556 | +    @NonNull  | 
 | 557 | +    List<LoanTransaction> findTransactionsForOverpaymentCalculation(@NonNull @Param("loan") Loan loan,  | 
 | 558 | +            @Param("repaymentLikeTypes") Set<LoanTransactionType> repaymentLikeTypes);  | 
 | 559 | + | 
 | 560 | +    @NonNull  | 
 | 561 | +    default List<LoanTransaction> findTransactionsForOverpaymentCalculation(@NonNull Loan loan) {  | 
 | 562 | +        return findTransactionsForOverpaymentCalculation(loan, REPAYMENT_LIKE_TYPES);  | 
 | 563 | +    }  | 
 | 564 | + | 
 | 565 | +    // Has Disbursement Transaction Query for optimization  | 
 | 566 | +    @Query("""  | 
 | 567 | +            SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END  | 
 | 568 | +            FROM LoanTransaction lt  | 
 | 569 | +            WHERE lt.loan = :loan  | 
 | 570 | +              AND lt.loan IS NOT NULL  | 
 | 571 | +              AND lt.reversed = false  | 
 | 572 | +              AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.DISBURSEMENT  | 
 | 573 | +            """)  | 
 | 574 | +    boolean hasDisbursementTransaction(@NonNull @Param("loan") Loan loan);  | 
 | 575 | + | 
 | 576 | +    // Receivable Interest Query for optimization  | 
 | 577 | +    @Query("""  | 
 | 578 | +            SELECT lt FROM LoanTransaction lt  | 
 | 579 | +            WHERE lt.loan = :loan  | 
 | 580 | +              AND lt.loan IS NOT NULL  | 
 | 581 | +              AND lt.reversed = false  | 
 | 582 | +              AND lt.typeOf NOT IN :excludedTypes  | 
 | 583 | +              AND lt.dateOf <= :tillDate  | 
 | 584 | +            ORDER BY lt.dateOf, lt.createdDate, lt.id  | 
 | 585 | +            """)  | 
 | 586 | +    @NonNull  | 
 | 587 | +    List<LoanTransaction> findTransactionsForReceivableInterest(@NonNull @Param("loan") Loan loan,  | 
 | 588 | +            @NonNull @Param("tillDate") LocalDate tillDate, @Param("excludedTypes") Set<LoanTransactionType> excludedTypes);  | 
 | 589 | + | 
 | 590 | +    @NonNull  | 
 | 591 | +    default List<LoanTransaction> findTransactionsForReceivableInterest(@NonNull Loan loan, @NonNull LocalDate tillDate) {  | 
 | 592 | +        return findTransactionsForReceivableInterest(loan, tillDate, EXCLUDED_FROM_RECEIVABLE_INTEREST);  | 
 | 593 | +    }  | 
 | 594 | + | 
 | 595 | +    // Outstanding Balance Calculation Query for optimization  | 
 | 596 | +    @Query("""  | 
 | 597 | +            SELECT lt FROM LoanTransaction lt  | 
 | 598 | +            WHERE lt.loan = :loan  | 
 | 599 | +              AND lt.loan IS NOT NULL  | 
 | 600 | +              AND lt.reversed = false  | 
 | 601 | +              AND lt.typeOf NOT IN (  | 
 | 602 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.CONTRA,  | 
 | 603 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.MARKED_FOR_RESCHEDULING,  | 
 | 604 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.APPROVE_TRANSFER,  | 
 | 605 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INITIATE_TRANSFER,  | 
 | 606 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REJECT_TRANSFER,  | 
 | 607 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WITHDRAW_TRANSFER,  | 
 | 608 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL,  | 
 | 609 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT,  | 
 | 610 | +                  org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ACTIVITY  | 
 | 611 | +              )  | 
 | 612 | +            ORDER BY lt.dateOf, lt.createdDate, lt.id  | 
 | 613 | +            """)  | 
 | 614 | +    @NonNull  | 
 | 615 | +    List<LoanTransaction> findNonMonetaryTransactionsForOutstandingBalance(@NonNull @Param("loan") Loan loan);  | 
 | 616 | + | 
 | 617 | +    @Query("""  | 
 | 618 | +            SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END  | 
 | 619 | +            FROM LoanTransaction lt  | 
 | 620 | +            WHERE lt.loan = :loan  | 
 | 621 | +                AND lt.externalId = :externalId  | 
 | 622 | +            """)  | 
 | 623 | +    boolean existsByLoanAndExternalId(@Param("loan") Loan loan, @Param("externalId") ExternalId externalId);  | 
 | 624 | + | 
486 | 625 | }  | 
0 commit comments