FINERACT-2274: Rework Accrual service
This commit is contained in:
committed by
Adam Saghy
parent
fa405f09fd
commit
f6f8c6f4a5
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record CumulativeIncomeFromIncomePosting(BigDecimal interestAmount, BigDecimal feeAmount, BigDecimal penaltyAmount) {
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
|
||||
public interface TransactionPortionsForForeclosure {
|
||||
|
||||
LoanTransactionType getTransactionType();
|
||||
|
||||
BigDecimal getInterestPortion();
|
||||
|
||||
BigDecimal getFeeChargesPortion();
|
||||
|
||||
BigDecimal getPenaltyChargesPortion();
|
||||
|
||||
}
|
||||
+15
-3
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
@@ -25,7 +26,18 @@ import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface LoanChargePaidByRepository extends JpaRepository<LoanChargePaidBy, Long>, JpaSpecificationExecutor<LoanCharge> {
|
||||
|
||||
@Query("select lp from LoanChargePaidBy lp where lp.loanCharge=:loanCharge and lp.installmentNumber=:installmentNumber")
|
||||
LoanChargePaidBy getLoanChargePaidByLoanCharge(@Param("loanCharge") LoanCharge loanCharge,
|
||||
@Param("installmentNumber") Integer installmentNo);
|
||||
@Query("""
|
||||
SELECT lcpb
|
||||
FROM LoanChargePaidBy lcpb
|
||||
JOIN lcpb.loanTransaction lt
|
||||
JOIN lcpb.loanCharge lc
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND (lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL
|
||||
OR lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT)
|
||||
AND (lt.feeChargesPortion > 0 OR lt.penaltyChargesPortion > 0)
|
||||
AND lcpb.installmentNumber IS NULL
|
||||
""")
|
||||
List<LoanChargePaidBy> findChargePaidByMappingsWithoutInstallmentNumber(@Param("loan") Loan loan);
|
||||
|
||||
}
|
||||
|
||||
+197
-25
@@ -18,12 +18,16 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.TransactionPortionsForForeclosure;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.UnpaidChargeData;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
@@ -32,31 +36,8 @@ import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface LoanTransactionRepository extends JpaRepository<LoanTransaction, Long>, JpaSpecificationExecutor<LoanTransaction> {
|
||||
|
||||
String FIND_ID_BY_EXTERNAL_ID = "SELECT lt.id FROM LoanTransaction lt WHERE lt.externalId = :externalId";
|
||||
String FIND_LOAN_ID_BY_ID = "SELECT lt.loan.id FROM LoanTransaction lt WHERE lt.id = :id";
|
||||
|
||||
Optional<LoanTransaction> findByIdAndLoanId(Long transactionId, Long loanId);
|
||||
|
||||
@Query("""
|
||||
SELECT lt FROM LoanTransaction lt
|
||||
WHERE lt.loan.id = :loanId
|
||||
AND lt.typeOf IN :transactionTypes
|
||||
AND lt.id NOT IN :existingTransactionIds
|
||||
AND lt.reversed = false
|
||||
""")
|
||||
List<LoanTransaction> findNewTransactionsByLoanIdAndTypeAndNotInIds(@Param("loanId") Long loanId,
|
||||
@Param("transactionTypes") List<LoanTransactionType> transactionTypes,
|
||||
@Param("existingTransactionIds") List<Long> existingTransactionIds);
|
||||
|
||||
@Query("""
|
||||
SELECT lt FROM LoanTransaction lt
|
||||
WHERE lt.loan.id = :loanId
|
||||
AND lt.typeOf IN :transactionTypes
|
||||
AND lt.reversed = false
|
||||
""")
|
||||
List<LoanTransaction> findNewTransactionsByLoanIdAndType(@Param("loanId") Long loanId,
|
||||
@Param("transactionTypes") List<LoanTransactionType> transactionTypes);
|
||||
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData(
|
||||
lt.loan.id,
|
||||
@@ -72,7 +53,7 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
|
||||
Collection<LoanScheduleDelinquencyData> fetchLoanTransactionsByTypeAndLessOrEqualDate(
|
||||
@Param("transactionType") LoanTransactionType transactionType, @Param("businessDate") LocalDate businessDate);
|
||||
|
||||
@Query(FIND_ID_BY_EXTERNAL_ID)
|
||||
@Query("SELECT lt.id FROM LoanTransaction lt WHERE lt.externalId = :externalId")
|
||||
Long findIdByExternalId(@Param("externalId") ExternalId externalId);
|
||||
|
||||
@Query("""
|
||||
@@ -88,9 +69,41 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
|
||||
""")
|
||||
List<UnpaidChargeData> fetchTotalUnpaidChargesForLoan(@Param("loan") Loan loan);
|
||||
|
||||
@Query(FIND_LOAN_ID_BY_ID)
|
||||
@Query("SELECT lt.loan.id FROM LoanTransaction lt WHERE lt.id = :id")
|
||||
Optional<Long> findLoanIdById(@Param("id") Long id);
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(lt.unrecognizedIncomePortion), 0)
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WAIVE_INTEREST
|
||||
AND lt.reversed = false
|
||||
AND lt.dateOf <= :toDate
|
||||
""")
|
||||
BigDecimal findTotalUnrecognizedIncomeFromInterestWaiverByLoanAndDate(@Param("loan") Loan loan, @Param("toDate") LocalDate toDate);
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(CASE WHEN lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL THEN lt.interestPortion
|
||||
WHEN lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT THEN -lt.interestPortion
|
||||
ELSE 0 END), 0)
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
""")
|
||||
BigDecimal findTotalInterestAccruedAmount(@Param("loan") Loan loan);
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(lt.interestPortion), 0)
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL
|
||||
AND lt.dateOf > :fromDate
|
||||
AND lt.dateOf <= :dueDate
|
||||
""")
|
||||
BigDecimal findAccrualInterestInPeriod(@Param("loan") Loan loan, @Param("fromDate") LocalDate fromDate,
|
||||
@Param("dueDate") LocalDate dueDate);
|
||||
|
||||
@Query("""
|
||||
SELECT CASE WHEN COUNT(lt) > 0 THEN false ELSE true END
|
||||
FROM LoanTransaction lt
|
||||
@@ -133,4 +146,163 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
|
||||
""")
|
||||
List<Long> findReversedTransactionIdsByLoan(@Param("loan") Loan loan);
|
||||
|
||||
@Query("""
|
||||
SELECT
|
||||
lt.typeOf AS transactionType,
|
||||
lt.interestPortion AS interestPortion,
|
||||
lt.feeChargesPortion AS feeChargesPortion,
|
||||
lt.penaltyChargesPortion AS penaltyChargesPortion
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.dateOf <= :tillDate
|
||||
AND lt.typeOf NOT IN (
|
||||
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.DISBURSEMENT,
|
||||
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT_AT_DISBURSEMENT
|
||||
)
|
||||
ORDER BY lt.dateOf
|
||||
""")
|
||||
List<TransactionPortionsForForeclosure> findTransactionDataForForeclosureIncome(@Param("loan") Loan loan,
|
||||
@Param("tillDate") LocalDate tillDate);
|
||||
|
||||
@Query("""
|
||||
SELECT lt
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf IN :types
|
||||
AND lt.dateOf = :transactionDate
|
||||
""")
|
||||
Optional<LoanTransaction> findNonReversedByLoanAndTypesAndDate(@Param("loan") Loan loan, @Param("type") Set<LoanTransactionType> types,
|
||||
@Param("transactionDate") LocalDate transactionDate);
|
||||
|
||||
@Query("""
|
||||
SELECT lt
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf IN :types
|
||||
AND lt.dateOf > :transactionDate
|
||||
ORDER BY lt.dateOf
|
||||
""")
|
||||
List<LoanTransaction> findNonReversedByLoanAndTypesAndAfterDate(@Param("loan") Loan loan,
|
||||
@Param("types") Set<LoanTransactionType> types, @Param("transactionDate") LocalDate transactionDate);
|
||||
|
||||
@Query("""
|
||||
SELECT CASE WHEN COUNT(lt) > 0 THEN true ELSE false END
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf IN :types
|
||||
AND lt.dateOf >= :accrualDate
|
||||
""")
|
||||
boolean existsNonReversedByLoanAndTypesAndOnOrAfterDate(@Param("loan") Loan loan, @Param("types") Set<LoanTransactionType> types,
|
||||
@Param("accrualDate") LocalDate accrualDate);
|
||||
|
||||
@Query("""
|
||||
SELECT lt
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf IN :types
|
||||
AND lt.id NOT IN :existingTransactionIds
|
||||
""")
|
||||
List<LoanTransaction> findNonReversedByLoanAndTypesAndNotInIds(@Param("loan") Loan loan, @Param("types") Set<LoanTransactionType> types,
|
||||
@Param("existingTransactionIds") List<Long> existingTransactionIds);
|
||||
|
||||
@Query("""
|
||||
SELECT lt
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf IN :types
|
||||
""")
|
||||
List<LoanTransaction> findNonReversedByLoanAndTypes(@Param("loan") Loan loan, @Param("types") Set<LoanTransactionType> types);
|
||||
|
||||
@Query("""
|
||||
SELECT lt
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.dateOf >= :date
|
||||
AND lt.typeOf IN :types
|
||||
ORDER BY lt.dateOf
|
||||
""")
|
||||
List<LoanTransaction> findNonReversedByLoanAndTypesAndOnOrAfterDate(@Param("loan") Loan loan,
|
||||
@Param("types") Set<LoanTransactionType> types, @Param("date") LocalDate date);
|
||||
|
||||
@Query("""
|
||||
SELECT new org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting(
|
||||
COALESCE(SUM(lt.interestPortion), 0),
|
||||
COALESCE(SUM(lt.feeChargesPortion), 0),
|
||||
COALESCE(SUM(lt.penaltyChargesPortion), 0)
|
||||
)
|
||||
FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INCOME_POSTING
|
||||
""")
|
||||
CumulativeIncomeFromIncomePosting findCumulativeIncomeByLoanAndType(@Param("loan") Loan loan);
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(CASE WHEN lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL THEN lcpb.amount
|
||||
WHEN lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT THEN -lcpb.amount
|
||||
ELSE 0 END), 0)
|
||||
FROM LoanChargePaidBy lcpb
|
||||
JOIN lcpb.loanTransaction lt
|
||||
WHERE lcpb.loanCharge = :loanCharge
|
||||
AND lt.reversed = false
|
||||
""")
|
||||
BigDecimal findChargeAccrualAmount(@Param("loanCharge") LoanCharge loanCharge);
|
||||
|
||||
@Query("""
|
||||
SELECT COALESCE(SUM(lt.unrecognizedIncomePortion), 0)
|
||||
FROM LoanChargePaidBy lcpb
|
||||
JOIN lcpb.loanTransaction lt
|
||||
WHERE lcpb.loanCharge = :loanCharge
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf = org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.WAIVE_CHARGES
|
||||
AND lt.dateOf <= :tillDate
|
||||
""")
|
||||
BigDecimal findChargeUnrecognizedWaivedAmount(@Param("loanCharge") LoanCharge loanCharge, @Param("tillDate") LocalDate tillDate);
|
||||
|
||||
@Query("""
|
||||
SELECT MAX(lt.dateOf) FROM LoanTransaction lt
|
||||
WHERE lt.loan = :loan
|
||||
AND lt.reversed = false
|
||||
AND lt.typeOf IN :types
|
||||
""")
|
||||
Optional<LocalDate> findLastNonReversedTransactionDateByLoanAndTypes(@Param("loan") Loan loan,
|
||||
@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> findNonReversedTransactionsByLoan(@Param("loan") Loan loan);
|
||||
|
||||
}
|
||||
|
||||
+15
-8
@@ -35,6 +35,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -42,6 +43,8 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAccountingBridgeMapper {
|
||||
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
|
||||
public List<AccountingBridgeDataDTO> deriveAccountingBridgeDataForChargeOff(final String currencyCode,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final boolean isAccountTransfer,
|
||||
final Loan loan) {
|
||||
@@ -72,16 +75,20 @@ public class LoanAccountingBridgeMapper {
|
||||
|
||||
public AccountingBridgeDataDTO deriveAccountingBridgeData(final String currencyCode, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final boolean isAccountTransfer, final Loan loan) {
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactions = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : loan.getLoanTransactions()) {
|
||||
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId())
|
||||
&& !existingReversedTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(mapToLoanTransactionData(transaction, currencyCode));
|
||||
} else if (!existingTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(mapToLoanTransactionData(transaction, currencyCode));
|
||||
}
|
||||
List<LoanTransaction> transactions;
|
||||
if (existingTransactionIds == null || existingTransactionIds.isEmpty()) {
|
||||
transactions = loanTransactionRepository.findNonReversedTransactionsByLoan(loan);
|
||||
} else if (existingReversedTransactionIds == null || existingReversedTransactionIds.isEmpty()) {
|
||||
transactions = loanTransactionRepository.findTransactionsForAccountingBridge(loan, existingTransactionIds);
|
||||
} else {
|
||||
transactions = loanTransactionRepository.findTransactionsForAccountingBridge(loan, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
}
|
||||
|
||||
final List<AccountingBridgeLoanTransactionDTO> newLoanTransactions = transactions.stream() //
|
||||
.map(transaction -> mapToLoanTransactionData(transaction, currencyCode)) //
|
||||
.toList();
|
||||
|
||||
return new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode,
|
||||
loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(),
|
||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(),
|
||||
|
||||
+1
@@ -24,4 +24,5 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
public interface LoanAccrualTransactionBusinessEventService {
|
||||
|
||||
void raiseBusinessEventForAccrualTransactions(Loan loan, List<Long> existingTransactionIds);
|
||||
|
||||
}
|
||||
|
||||
+11
-7
@@ -18,7 +18,11 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualAdjustmentTransactionBusinessEvent;
|
||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
|
||||
@@ -37,17 +41,17 @@ public class LoanAccrualTransactionBusinessEventServiceImpl implements LoanAccru
|
||||
|
||||
@Override
|
||||
public void raiseBusinessEventForAccrualTransactions(final Loan loan, final List<Long> existingTransactionIds) {
|
||||
final List<LoanTransactionType> transactionTypes = List.of(LoanTransactionType.ACCRUAL, LoanTransactionType.ACCRUAL_ADJUSTMENT);
|
||||
final List<LoanTransaction> transactions = existingTransactionIds.isEmpty()
|
||||
? loanTransactionRepository.findNewTransactionsByLoanIdAndType(loan.getId(), transactionTypes)
|
||||
: loanTransactionRepository.findNewTransactionsByLoanIdAndTypeAndNotInIds(loan.getId(), transactionTypes,
|
||||
existingTransactionIds);
|
||||
final Set<LoanTransactionType> accrualTypes = Set.of(ACCRUAL, ACCRUAL_ADJUSTMENT);
|
||||
final List<LoanTransaction> accrualTransactions = existingTransactionIds.isEmpty()
|
||||
? loanTransactionRepository.findNonReversedByLoanAndTypes(loan, accrualTypes)
|
||||
: loanTransactionRepository.findNonReversedByLoanAndTypesAndNotInIds(loan, accrualTypes, existingTransactionIds);
|
||||
|
||||
for (final LoanTransaction transaction : transactions) {
|
||||
accrualTransactions.forEach(transaction -> {
|
||||
final LoanTransactionBusinessEvent businessEvent = transaction.isAccrual()
|
||||
? new LoanAccrualTransactionCreatedBusinessEvent(transaction)
|
||||
: new LoanAccrualAdjustmentTransactionBusinessEvent(transaction);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -28,11 +28,11 @@ import org.springframework.lang.NonNull;
|
||||
|
||||
public interface LoanAccrualsProcessingService {
|
||||
|
||||
void addPeriodicAccruals(@NotNull LocalDate tilldate) throws MultiException;
|
||||
void addPeriodicAccruals(@NotNull LocalDate tillDate) throws MultiException;
|
||||
|
||||
void addPeriodicAccruals(@NotNull LocalDate tilldate, @NotNull Loan loan) throws MultiException;
|
||||
void addPeriodicAccruals(@NotNull LocalDate tillDate, @NotNull Loan loan) throws MultiException;
|
||||
|
||||
void addAccruals(@NotNull LocalDate tilldate) throws MultiException;
|
||||
void addAccruals(@NotNull LocalDate tillDate) throws MultiException;
|
||||
|
||||
void reprocessExistingAccruals(@NotNull Loan loan);
|
||||
|
||||
|
||||
-2
@@ -30,7 +30,6 @@ import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
@@ -39,7 +38,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Component
|
||||
@Transactional(readOnly = true)
|
||||
@RequiredArgsConstructor
|
||||
public class LoanTransactionReadService {
|
||||
|
||||
@PersistenceContext
|
||||
|
||||
+4
-5
@@ -25,11 +25,11 @@ import org.apache.fineract.infrastructure.core.exception.MultiException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@@ -37,11 +37,10 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class AddAccrualEntriesTasklet implements Tasklet {
|
||||
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
public RepeatStatus execute(@NonNull final StepContribution contribution, @NonNull final ChunkContext chunkContext) throws Exception {
|
||||
try {
|
||||
addAccruals(DateUtils.getBusinessLocalDate());
|
||||
} catch (MultiException e) {
|
||||
@@ -50,7 +49,7 @@ public class AddAccrualEntriesTasklet implements Tasklet {
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
private void addAccruals(final LocalDate tilldate) throws MultiException {
|
||||
loanAccrualsProcessingService.addAccruals(tilldate);
|
||||
private void addAccruals(final LocalDate tillDate) throws MultiException {
|
||||
loanAccrualsProcessingService.addAccruals(tillDate);
|
||||
}
|
||||
}
|
||||
|
||||
+115
-172
@@ -24,6 +24,9 @@ import static org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentSche
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper.isInPeriod;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction.accrualAdjustment;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction.accrueTransaction;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.ACCRUAL_ADJUSTMENT;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.INCOME_POSTING;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
@@ -34,6 +37,7 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Predicate;
|
||||
@@ -65,9 +69,12 @@ import org.apache.fineract.portfolio.loanaccount.data.AccrualBalances;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccrualChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.TransactionPortionsForForeclosure;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
|
||||
@@ -77,6 +84,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
@@ -95,8 +103,7 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||
@RequiredArgsConstructor
|
||||
public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessingService {
|
||||
|
||||
private static final Predicate<LoanTransaction> ACCRUAL_PREDICATE = t -> t.isNotReversed()
|
||||
&& (t.isAccrual() || t.isAccrualAdjustment());
|
||||
private static final Set<LoanTransactionType> ACCRUAL_TYPES = Set.of(ACCRUAL, ACCRUAL_ADJUSTMENT);
|
||||
|
||||
private static final String ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE = "submitted-date";
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
@@ -114,6 +121,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
private final LoanChargeService loanChargeService;
|
||||
private final LoanBalanceService loanBalanceService;
|
||||
private final LoanChargePaidByRepository loanChargePaidByRepository;
|
||||
|
||||
/**
|
||||
* method adds accrual for batch job "Add Periodic Accrual Transactions" and add accruals api for Loan
|
||||
@@ -267,21 +275,18 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
LocalDate lastCompoundingDate = loan.getDisbursementDate();
|
||||
List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = extractInterestRecalculationAdditionalDetails(loan);
|
||||
List<LoanTransaction> incomeTransactions = retrieveListOfIncomePostingTransactions(loan);
|
||||
List<LoanTransaction> accrualTransactions = retrieveListOfAccrualTransactions(loan);
|
||||
for (LoanInterestRecalcualtionAdditionalDetails compoundingDetail : compoundingDetails) {
|
||||
if (!DateUtils.isBeforeBusinessDate(compoundingDetail.getEffectiveDate())) {
|
||||
break;
|
||||
}
|
||||
LoanTransaction incomeTransaction = getTransactionForDate(incomeTransactions, compoundingDetail.getEffectiveDate());
|
||||
LoanTransaction accrualTransaction = getTransactionForDate(accrualTransactions, compoundingDetail.getEffectiveDate());
|
||||
addUpdateIncomeAndAccrualTransaction(loan, compoundingDetail, lastCompoundingDate, incomeTransaction, accrualTransaction);
|
||||
|
||||
addUpdateIncomeAndAccrualTransaction(loan, compoundingDetail, lastCompoundingDate);
|
||||
lastCompoundingDate = compoundingDetail.getEffectiveDate();
|
||||
}
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
LoanRepaymentScheduleInstallment lastInstallment = LoanRepaymentScheduleInstallment.getLastNonDownPaymentInstallment(installments);
|
||||
reverseTransactionsAfter(incomeTransactions, lastInstallment.getDueDate(), false);
|
||||
reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate(), false);
|
||||
|
||||
reverseTransactionsAfter(loan, Set.of(ACCRUAL, ACCRUAL_ADJUSTMENT, INCOME_POSTING), lastInstallment.getDueDate(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,16 +315,14 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
&& (loan.getAccruedTill() == null || !DateUtils.isEqual(foreClosureDate, loan.getAccruedTill()))) {
|
||||
final LoanRepaymentScheduleInstallment foreCloseDetail = loanBalanceService.fetchLoanForeclosureDetail(loan, foreClosureDate);
|
||||
MonetaryCurrency currency = loan.getCurrency();
|
||||
reverseTransactionsAfter(retrieveListOfAccrualTransactions(loan), foreClosureDate, false);
|
||||
reverseTransactionsAfter(loan, ACCRUAL_TYPES, foreClosureDate, false);
|
||||
|
||||
HashMap<String, Object> incomeDetails = new HashMap<>();
|
||||
final Map<String, Money> incomeDetails = determineReceivableIncomeForeClosure(loan, foreClosureDate);
|
||||
|
||||
determineReceivableIncomeForeClosure(loan, foreClosureDate, incomeDetails);
|
||||
|
||||
Money interestPortion = foreCloseDetail.getInterestCharged(currency).minus((Money) incomeDetails.get(Loan.INTEREST));
|
||||
Money feePortion = foreCloseDetail.getFeeChargesCharged(currency).minus((Money) incomeDetails.get(Loan.FEE));
|
||||
Money penaltyPortion = foreCloseDetail.getPenaltyChargesCharged(currency).minus((Money) incomeDetails.get(Loan.PENALTIES));
|
||||
Money total = interestPortion.plus(feePortion).plus(penaltyPortion);
|
||||
final Money interestPortion = foreCloseDetail.getInterestCharged(currency).minus(incomeDetails.get(Loan.INTEREST));
|
||||
final Money feePortion = foreCloseDetail.getFeeChargesCharged(currency).minus(incomeDetails.get(Loan.FEE));
|
||||
final Money penaltyPortion = foreCloseDetail.getPenaltyChargesCharged(currency).minus(incomeDetails.get(Loan.PENALTIES));
|
||||
final Money total = interestPortion.plus(feePortion).plus(penaltyPortion);
|
||||
|
||||
if (total.isGreaterThanZero()) {
|
||||
createAccrualTransactionAndUpdateChargesPaidBy(loan, foreClosureDate, newAccrualTransactions, currency, interestPortion,
|
||||
@@ -341,10 +344,10 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
if (recalculationDetails != null && recalculationDetails.isCompoundingToBePostedAsTransaction()) {
|
||||
return;
|
||||
}
|
||||
final List<LoanTransaction> existingAccruals = retrieveListOfAccrualTransactions(loan);
|
||||
|
||||
final LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate();
|
||||
reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal);
|
||||
ensureAccrualTransactionMappings(loan, existingAccruals, chargeOnDueDate);
|
||||
reverseTransactionsAfter(loan, ACCRUAL_TYPES, lastDueDate, addJournal);
|
||||
ensureAccrualTransactionMappings(loan, chargeOnDueDate);
|
||||
if (DateUtils.isAfter(tillDate, lastDueDate)) {
|
||||
tillDate = lastDueDate;
|
||||
}
|
||||
@@ -358,8 +361,9 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
: tillDate;
|
||||
if (progressiveAccrual && accruedTill != null && !DateUtils.isAfter(tillDate, accruedTill)) {
|
||||
if (isFinal) {
|
||||
reverseTransactionsAfter(existingAccruals, accrualDate, addJournal);
|
||||
} else if (isExistAccrualAfterDate(existingAccruals, accrualDate) && hasNoActiveChargeOnDate(loan, accrualDate)) {
|
||||
reverseTransactionsAfter(loan, ACCRUAL_TYPES, accrualDate, addJournal);
|
||||
} else if (loanTransactionRepository.existsNonReversedByLoanAndTypesAndOnOrAfterDate(loan, ACCRUAL_TYPES, accrualDate)
|
||||
&& hasNoActiveChargeOnDate(loan, accrualDate)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -446,10 +450,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExistAccrualAfterDate(List<LoanTransaction> existingAccruals, LocalDate accrualDate) {
|
||||
return existingAccruals.stream().anyMatch(t -> !t.isReversed() && !DateUtils.isBefore(t.getDateOf(), accrualDate));
|
||||
}
|
||||
|
||||
private boolean hasNoActiveChargeOnDate(Loan loan, LocalDate accrualDate) {
|
||||
return loan.getLoanCharges(t -> t.isActive() && DateUtils.isEqual(t.getDueDate(), accrualDate)).isEmpty();
|
||||
}
|
||||
@@ -540,11 +540,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
// unrecognized amount of the transaction is not mapped to installments
|
||||
LocalDate dueDate = installment.getDueDate();
|
||||
LocalDate toDate = DateUtils.isBefore(dueDate, tillDate) ? dueDate : tillDate;
|
||||
Predicate<LoanTransaction> transactionPredicate = t -> !t.isReversed() && t.isInterestWaiver()
|
||||
&& !DateUtils.isAfter(t.getTransactionDate(), toDate);
|
||||
Loan loan = installment.getLoan();
|
||||
BigDecimal totalUnrecognized = loan.getLoanTransactions().stream().filter(transactionPredicate)
|
||||
.map(LoanTransaction::getUnrecognizedIncomePortion).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
BigDecimal totalUnrecognized = loanTransactionRepository.findTotalUnrecognizedIncomeFromInterestWaiverByLoanAndDate(loan, toDate);
|
||||
// total unrecognized amount from previous periods
|
||||
BigDecimal prevUnrecognized = accrualPeriods.getPeriods().stream()
|
||||
.filter(p -> p.getInstallmentNumber() < installment.getInstallmentNumber())
|
||||
@@ -558,9 +555,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
@NotNull AccrualPeriodsData accrualPeriods, @NotNull LocalDate tillDate) {
|
||||
Loan loan = installment.getLoan();
|
||||
if (isProgressiveAccrual(loan)) {
|
||||
BigDecimal totalAccrued = loan.getLoanTransactions().stream().filter(ACCRUAL_PREDICATE)
|
||||
.map(t -> t.isAccrual() ? t.getInterestPortion() : MathUtil.negate(t.getInterestPortion()))
|
||||
.reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
BigDecimal totalAccrued = loanTransactionRepository.findTotalInterestAccruedAmount(loan);
|
||||
BigDecimal prevAccrued = accrualPeriods.getPeriods().stream()
|
||||
.filter(p -> p.getInstallmentNumber() < installment.getInstallmentNumber())
|
||||
.map(p -> MathUtil.toBigDecimal(p.getTransactionAccrued())).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
@@ -569,9 +564,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
return isInPeriod(tillDate, installment, false) ? accrued : MathUtil.min(installment.getInterestAccrued(), accrued, false);
|
||||
} else {
|
||||
return isFullPeriod(tillDate, installment) ? installment.getInterestAccrued()
|
||||
: loan.getLoanTransactions().stream()
|
||||
.filter(t -> !t.isReversed() && t.isAccrual() && isInPeriod(t.getTransactionDate(), installment, false))
|
||||
.map(LoanTransaction::getInterestPortion).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
: loanTransactionRepository.findAccrualInterestInPeriod(loan, installment.getFromDate(), installment.getDueDate());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,9 +617,10 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
.setChargeAmount(chargeAmount);
|
||||
chargeData.setChargeAccruable(MathUtil.minusToZero(chargeAmount, waived));
|
||||
|
||||
final Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency);
|
||||
final Money unrecognizedWaived = MathUtil
|
||||
.toMoney(loanTransactionRepository.findChargeUnrecognizedWaivedAmount(loanCharge, tillDate), currency);
|
||||
final Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived);
|
||||
final Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency);
|
||||
final Money transactionAccrued = MathUtil.toMoney(loanTransactionRepository.findChargeAccrualAmount(loanCharge), currency);
|
||||
chargeData.setTransactionAccrued(transactionAccrued);
|
||||
chargeData.setChargeAccrued(MathUtil.minusToZero(transactionAccrued, transactionWaived));
|
||||
|
||||
@@ -642,22 +636,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}).map(LoanChargePaidBy::getAmount).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private BigDecimal calcChargeUnrecognizedWaivedAmount(@NotNull final Collection<LoanChargePaidBy> loanChargePaidBy,
|
||||
@NotNull final LocalDate tillDate) {
|
||||
return loanChargePaidBy.stream().filter(pb -> {
|
||||
final LoanTransaction t = pb.getLoanTransaction();
|
||||
return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate);
|
||||
}).map(pb -> pb.getLoanTransaction().getUnrecognizedIncomePortion()).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private BigDecimal calcChargeAccruedAmount(@NotNull final Collection<LoanChargePaidBy> loanChargePaidBy) {
|
||||
return loanChargePaidBy.stream().filter(pb -> ACCRUAL_PREDICATE.test(pb.getLoanTransaction()))
|
||||
.map(pb -> pb.getLoanTransaction().isAccrual() ? pb.getAmount() : MathUtil.negate(pb.getAmount()))
|
||||
.reduce(BigDecimal.ZERO, MathUtil::add);
|
||||
}
|
||||
|
||||
private boolean isChargeDue(@NotNull final LoanCharge loanCharge, @NotNull final LocalDate tillDate, boolean chargeOnDueDate,
|
||||
final LoanRepaymentScheduleInstallment installment, final boolean isFirstPeriod) {
|
||||
final LocalDate fromDate = installment.getFromDate();
|
||||
@@ -697,10 +675,11 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
LoanTransaction transaction = adjustment
|
||||
? accrualAdjustment(loan, loan.getOffice(), transactionDate, amount, interest, fee, penalty, externalIdFactory.create())
|
||||
: accrueTransaction(loan, loan.getOffice(), transactionDate, amount, interest, fee, penalty, externalIdFactory.create());
|
||||
loan.addLoanTransaction(transaction);
|
||||
|
||||
// update repayment schedule portions
|
||||
addTransactionMappings(transaction, accrualPeriod, adjustment);
|
||||
loanTransactionRepository.save(transaction);
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@@ -767,7 +746,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
return;
|
||||
}
|
||||
final boolean isChargeOnDueDate = isChargeOnDueDate();
|
||||
ensureAccrualTransactionMappings(loan, accrualTransactions, isChargeOnDueDate);
|
||||
ensureAccrualTransactionMappings(loan, isChargeOnDueDate);
|
||||
LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
|
||||
LocalDate lastDueDate = lastInstallment.getDueDate();
|
||||
if (isProgressiveAccrual(loan)) {
|
||||
@@ -826,7 +805,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
}
|
||||
// reverse accruals after last installment
|
||||
reverseTransactionsAfter(accrualTransactions, lastDueDate, false);
|
||||
reverseTransactionsAfter(loan, ACCRUAL_TYPES, lastDueDate, false);
|
||||
}
|
||||
|
||||
private void reprocessNonPeriodicAccruals(Loan loan, final List<LoanTransaction> accrualTransactions) {
|
||||
@@ -846,7 +825,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
final LoanTransaction interestAccrualTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan,
|
||||
interestApplied, loan.getDisbursementDate(), externalId);
|
||||
loan.addLoanTransaction(interestAccrualTransaction);
|
||||
loanTransactionRepository.save(interestAccrualTransaction);
|
||||
}
|
||||
} else {
|
||||
Set<LoanChargePaidBy> chargePaidBies = accrualTransaction.getLoanChargesPaid();
|
||||
@@ -922,7 +901,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
|
||||
private void addUpdateIncomeAndAccrualTransaction(Loan loan, LoanInterestRecalcualtionAdditionalDetails compoundingDetail,
|
||||
LocalDate lastCompoundingDate, LoanTransaction existingIncomeTransaction, LoanTransaction existingAccrualTransaction) {
|
||||
LocalDate lastCompoundingDate) {
|
||||
BigDecimal interest = BigDecimal.ZERO;
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
BigDecimal penalties = BigDecimal.ZERO;
|
||||
@@ -949,43 +928,43 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
|
||||
createUpdateIncomePostingTransaction(loan, compoundingDetail, existingIncomeTransaction, interest, fee, penalties, externalId);
|
||||
createUpdateAccrualTransaction(loan, compoundingDetail, existingAccrualTransaction, interest, fee, penalties, feeDetails,
|
||||
externalId);
|
||||
createUpdateIncomePostingTransaction(loan, compoundingDetail, interest, fee, penalties, externalId);
|
||||
createUpdateAccrualTransaction(loan, compoundingDetail, interest, fee, penalties, feeDetails, externalId);
|
||||
loan.updateLoanOutstandingBalances();
|
||||
}
|
||||
|
||||
private void createUpdateIncomePostingTransaction(Loan loan, LoanInterestRecalcualtionAdditionalDetails compoundingDetail,
|
||||
LoanTransaction existingIncomeTransaction, BigDecimal interest, BigDecimal fee, BigDecimal penalties, ExternalId externalId) {
|
||||
if (existingIncomeTransaction == null) {
|
||||
BigDecimal interest, BigDecimal fee, BigDecimal penalties, ExternalId externalId) {
|
||||
final Optional<LoanTransaction> incomeTransaction = loanTransactionRepository.findNonReversedByLoanAndTypesAndDate(loan,
|
||||
Set.of(INCOME_POSTING), compoundingDetail.getEffectiveDate());
|
||||
if (incomeTransaction.isEmpty()) {
|
||||
LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
|
||||
compoundingDetail.getAmount(), interest, fee, penalties, externalId);
|
||||
loan.addLoanTransaction(transaction);
|
||||
} else if (existingIncomeTransaction.getAmount(loan.getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
|
||||
existingIncomeTransaction.reverse();
|
||||
loanTransactionRepository.save(transaction);
|
||||
} else if (incomeTransaction.get().getAmount(loan.getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
|
||||
incomeTransaction.get().reverse();
|
||||
LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
|
||||
compoundingDetail.getAmount(), interest, fee, penalties, externalId);
|
||||
loan.addLoanTransaction(transaction);
|
||||
loanTransactionRepository.save(transaction);
|
||||
}
|
||||
}
|
||||
|
||||
private void createUpdateAccrualTransaction(Loan loan, LoanInterestRecalcualtionAdditionalDetails compoundingDetail,
|
||||
LoanTransaction existingAccrualTransaction, BigDecimal interest, BigDecimal fee, BigDecimal penalties,
|
||||
HashMap<String, Object> feeDetails, ExternalId externalId) {
|
||||
BigDecimal interest, BigDecimal fee, BigDecimal penalties, HashMap<String, Object> feeDetails, ExternalId externalId) {
|
||||
if (configurationDomainService.isExternalIdAutoGenerationEnabled()) {
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
if (existingAccrualTransaction == null
|
||||
|| !MathUtil.isEqualTo(existingAccrualTransaction.getAmount(), compoundingDetail.getAmount())) {
|
||||
if (existingAccrualTransaction != null) {
|
||||
existingAccrualTransaction.reverse();
|
||||
}
|
||||
final Optional<LoanTransaction> accrualTransaction = loanTransactionRepository.findNonReversedByLoanAndTypesAndDate(loan,
|
||||
Set.of(LoanTransactionType.ACCRUAL, LoanTransactionType.ACCRUAL_ADJUSTMENT), compoundingDetail.getEffectiveDate());
|
||||
|
||||
if (accrualTransaction.isEmpty() || !MathUtil.isEqualTo(accrualTransaction.get().getAmount(), compoundingDetail.getAmount())) {
|
||||
accrualTransaction.ifPresent(LoanTransaction::reverse);
|
||||
LoanTransaction accrual = LoanTransaction.accrueTransaction(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
|
||||
compoundingDetail.getAmount(), interest, fee, penalties, externalId);
|
||||
updateLoanChargesPaidBy(loan, accrual, feeDetails, null);
|
||||
loan.addLoanTransaction(accrual);
|
||||
loanTransactionRepository.save(accrual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -999,27 +978,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
&& loan.getStatus().isClosedObligationsMet() && !loan.isNpa() && !loan.isChargedOff()) {
|
||||
|
||||
LocalDate closedDate = loan.getClosedOnDate();
|
||||
reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate);
|
||||
reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate);
|
||||
reverseTransactionsOnOrAfter(loan, Set.of(INCOME_POSTING, ACCRUAL, ACCRUAL_ADJUSTMENT), closedDate);
|
||||
|
||||
HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
|
||||
final Map<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
|
||||
determineCumulativeIncomeFromInstallments(loan, cumulativeIncomeFromInstallments);
|
||||
HashMap<String, BigDecimal> cumulativeIncomeFromIncomePosting = new HashMap<>();
|
||||
determineCumulativeIncomeDetails(loan, retrieveListOfIncomePostingTransactions(loan), cumulativeIncomeFromIncomePosting);
|
||||
|
||||
BigDecimal interestToPost = cumulativeIncomeFromInstallments.get(Loan.INTEREST)
|
||||
.subtract(cumulativeIncomeFromIncomePosting.get(Loan.INTEREST));
|
||||
BigDecimal feeToPost = cumulativeIncomeFromInstallments.get(Loan.FEE).subtract(cumulativeIncomeFromIncomePosting.get(Loan.FEE));
|
||||
BigDecimal penaltyToPost = cumulativeIncomeFromInstallments.get(Loan.PENALTY)
|
||||
.subtract(cumulativeIncomeFromIncomePosting.get(Loan.PENALTY));
|
||||
BigDecimal amountToPost = interestToPost.add(feeToPost).add(penaltyToPost);
|
||||
final CumulativeIncomeFromIncomePosting cumulativeIncomeFromIncomePosting = loanTransactionRepository
|
||||
.findCumulativeIncomeByLoanAndType(loan);
|
||||
|
||||
final BigDecimal interestToPost = cumulativeIncomeFromInstallments.get(Loan.INTEREST)
|
||||
.subtract(cumulativeIncomeFromIncomePosting.interestAmount());
|
||||
final BigDecimal feeToPost = cumulativeIncomeFromInstallments.get(Loan.FEE)
|
||||
.subtract(cumulativeIncomeFromIncomePosting.feeAmount());
|
||||
final BigDecimal penaltyToPost = cumulativeIncomeFromInstallments.get(Loan.PENALTY)
|
||||
.subtract(cumulativeIncomeFromIncomePosting.penaltyAmount());
|
||||
final BigDecimal amountToPost = interestToPost.add(feeToPost).add(penaltyToPost);
|
||||
|
||||
createIncomePostingAndAccrualTransactionOnLoanClosure(loan, closedDate, interestToPost, feeToPost, penaltyToPost, amountToPost);
|
||||
}
|
||||
loan.updateLoanOutstandingBalances();
|
||||
}
|
||||
|
||||
private void determineCumulativeIncomeFromInstallments(Loan loan, HashMap<String, BigDecimal> cumulativeIncomeFromInstallments) {
|
||||
private void determineCumulativeIncomeFromInstallments(final Loan loan,
|
||||
final Map<String, BigDecimal> cumulativeIncomeFromInstallments) {
|
||||
BigDecimal interest = BigDecimal.ZERO;
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
BigDecimal penalty = BigDecimal.ZERO;
|
||||
@@ -1034,21 +1015,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
cumulativeIncomeFromInstallments.put(Loan.PENALTY, penalty);
|
||||
}
|
||||
|
||||
private void determineCumulativeIncomeDetails(Loan loan, List<LoanTransaction> transactions,
|
||||
HashMap<String, BigDecimal> incomeDetailsMap) {
|
||||
BigDecimal interest = BigDecimal.ZERO;
|
||||
BigDecimal fee = BigDecimal.ZERO;
|
||||
BigDecimal penalty = BigDecimal.ZERO;
|
||||
for (LoanTransaction transaction : transactions) {
|
||||
interest = interest.add(transaction.getInterestPortion(loan.getCurrency()).getAmount());
|
||||
fee = fee.add(transaction.getFeeChargesPortion(loan.getCurrency()).getAmount());
|
||||
penalty = penalty.add(transaction.getPenaltyChargesPortion(loan.getCurrency()).getAmount());
|
||||
}
|
||||
incomeDetailsMap.put(Loan.INTEREST, interest);
|
||||
incomeDetailsMap.put(Loan.FEE, fee);
|
||||
incomeDetailsMap.put(Loan.PENALTY, penalty);
|
||||
}
|
||||
|
||||
private void createIncomePostingAndAccrualTransactionOnLoanClosure(Loan loan, LocalDate closedDate, BigDecimal interestToPost,
|
||||
BigDecimal feeToPost, BigDecimal penaltyToPost, BigDecimal amountToPost) {
|
||||
ExternalId externalId = ExternalId.empty();
|
||||
@@ -1059,14 +1025,13 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
LoanTransaction finalIncomeTransaction = LoanTransaction.incomePosting(loan, loan.getOffice(), closedDate, amountToPost,
|
||||
interestToPost, feeToPost, penaltyToPost, externalId);
|
||||
loan.addLoanTransaction(finalIncomeTransaction);
|
||||
loanTransactionRepository.save(finalIncomeTransaction);
|
||||
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
List<LoanTransaction> updatedAccrualTransactions = retrieveListOfAccrualTransactions(loan);
|
||||
LocalDate lastAccruedDate = loan.getDisbursementDate();
|
||||
if (!updatedAccrualTransactions.isEmpty()) {
|
||||
lastAccruedDate = updatedAccrualTransactions.get(updatedAccrualTransactions.size() - 1).getTransactionDate();
|
||||
}
|
||||
final LocalDate lastAccruedDate = loanTransactionRepository
|
||||
.findLastNonReversedTransactionDateByLoanAndTypes(loan, ACCRUAL_TYPES) //
|
||||
.orElse(loan.getDisbursementDate());
|
||||
|
||||
HashMap<String, Object> feeDetails = new HashMap<>();
|
||||
determineFeeDetails(loan, lastAccruedDate, closedDate, feeDetails);
|
||||
if (isExternalIdAutoGenerationEnabled) {
|
||||
@@ -1075,30 +1040,35 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
LoanTransaction finalAccrual = LoanTransaction.accrueTransaction(loan, loan.getOffice(), closedDate, amountToPost,
|
||||
interestToPost, feeToPost, penaltyToPost, externalId);
|
||||
updateLoanChargesPaidBy(loan, finalAccrual, feeDetails, null);
|
||||
loan.addLoanTransaction(finalAccrual);
|
||||
loanTransactionRepository.save(finalAccrual);
|
||||
}
|
||||
}
|
||||
|
||||
// LoanForClosure
|
||||
|
||||
private void determineReceivableIncomeForeClosure(Loan loan, final LocalDate tillDate, Map<String, Object> incomeDetails) {
|
||||
private Map<String, Money> determineReceivableIncomeForeClosure(final Loan loan, final LocalDate tillDate) {
|
||||
MonetaryCurrency currency = loan.getCurrency();
|
||||
Money receivableInterest = Money.zero(currency);
|
||||
Money receivableFee = Money.zero(currency);
|
||||
Money receivablePenalty = Money.zero(currency);
|
||||
for (final LoanTransaction transaction : loan.getLoanTransactions()) {
|
||||
if (transaction.isNotReversed() && !transaction.isRepaymentAtDisbursement() && !transaction.isDisbursement()
|
||||
&& !DateUtils.isAfter(transaction.getTransactionDate(), tillDate)) {
|
||||
if (transaction.isAccrual()) {
|
||||
receivableInterest = receivableInterest.plus(transaction.getInterestPortion(currency));
|
||||
receivableFee = receivableFee.plus(transaction.getFeeChargesPortion(currency));
|
||||
receivablePenalty = receivablePenalty.plus(transaction.getPenaltyChargesPortion(currency));
|
||||
} else if (transaction.isRepaymentLikeType() || transaction.isChargePayment() || transaction.isAccrualAdjustment()) {
|
||||
receivableInterest = receivableInterest.minus(transaction.getInterestPortion(currency));
|
||||
receivableFee = receivableFee.minus(transaction.getFeeChargesPortion(currency));
|
||||
receivablePenalty = receivablePenalty.minus(transaction.getPenaltyChargesPortion(currency));
|
||||
}
|
||||
|
||||
final List<TransactionPortionsForForeclosure> transactionPortions = loanTransactionRepository
|
||||
.findTransactionDataForForeclosureIncome(loan, tillDate);
|
||||
|
||||
for (TransactionPortionsForForeclosure transactionPortion : transactionPortions) {
|
||||
LoanTransactionType transactionType = transactionPortion.getTransactionType();
|
||||
BigDecimal interestPortion = transactionPortion.getInterestPortion();
|
||||
BigDecimal feePortion = transactionPortion.getFeeChargesPortion();
|
||||
BigDecimal penaltyPortion = transactionPortion.getPenaltyChargesPortion();
|
||||
|
||||
if (transactionType.isAccrual()) {
|
||||
receivableInterest = receivableInterest.plus(Money.of(currency, interestPortion));
|
||||
receivableFee = receivableFee.plus(Money.of(currency, feePortion));
|
||||
receivablePenalty = receivablePenalty.plus(Money.of(currency, penaltyPortion));
|
||||
} else if (transactionType.isRepayment() || transactionType.isChargePayment() || transactionType.isAccrualAdjustment()) {
|
||||
receivableInterest = receivableInterest.minus(Money.of(currency, interestPortion));
|
||||
receivableFee = receivableFee.minus(Money.of(currency, feePortion));
|
||||
receivablePenalty = receivablePenalty.minus(Money.of(currency, penaltyPortion));
|
||||
}
|
||||
|
||||
if (receivableInterest.isLessThanZero()) {
|
||||
receivableInterest = receivableInterest.zero();
|
||||
}
|
||||
@@ -1110,9 +1080,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
}
|
||||
|
||||
incomeDetails.put(Loan.INTEREST, receivableInterest);
|
||||
incomeDetails.put(Loan.FEE, receivableFee);
|
||||
incomeDetails.put(Loan.PENALTIES, receivablePenalty);
|
||||
return Map.of(Loan.INTEREST, receivableInterest, Loan.FEE, receivableFee, Loan.PENALTIES, receivablePenalty);
|
||||
}
|
||||
|
||||
private void createAccrualTransactionAndUpdateChargesPaidBy(Loan loan, LocalDate foreClosureDate,
|
||||
@@ -1126,7 +1094,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
fromDate = loan.getAccruedTill();
|
||||
}
|
||||
newAccrualTransactions.add(accrualTransaction);
|
||||
loan.addLoanTransaction(accrualTransaction);
|
||||
Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid();
|
||||
for (LoanCharge loanCharge : loan.getActiveCharges()) {
|
||||
boolean isDue = loanCharge.isDueInPeriod(fromDate, foreClosureDate, DateUtils.isEqual(fromDate, loan.getDisbursementDate()));
|
||||
@@ -1148,50 +1115,32 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
private void ensureAccrualTransactionMappings(final Loan loan, final List<LoanTransaction> existingAccrualTransactions,
|
||||
final boolean chargeOnDueDate) {
|
||||
final List<LoanTransaction> transactions = existingAccrualTransactions.stream()
|
||||
.filter(t -> !MathUtil.isEmpty(t.getFeeChargesPortion()) || !MathUtil.isEmpty(t.getPenaltyChargesPortion())).toList();
|
||||
private void ensureAccrualTransactionMappings(final Loan loan, final boolean chargeOnDueDate) {
|
||||
final List<LoanChargePaidBy> entriesToProcess = loanChargePaidByRepository.findChargePaidByMappingsWithoutInstallmentNumber(loan);
|
||||
|
||||
if (transactions.isEmpty()) {
|
||||
if (entriesToProcess.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
||||
for (LoanTransaction transaction : transactions) {
|
||||
for (LoanChargePaidBy paidBy : transaction.getLoanChargesPaid()) {
|
||||
if (paidBy.getInstallmentNumber() == null) {
|
||||
final LoanCharge loanCharge = paidBy.getLoanCharge();
|
||||
final LocalDate chargeDate = (chargeOnDueDate || loanCharge.isInstalmentFee()) ? transaction.getTransactionDate()
|
||||
: loanCharge.getDueDate();
|
||||
final LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(
|
||||
i -> isInPeriod(chargeDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
|
||||
paidBy.setInstallmentNumber(installment.getInstallmentNumber());
|
||||
}
|
||||
for (LoanChargePaidBy paidBy : entriesToProcess) {
|
||||
final LoanCharge loanCharge = paidBy.getLoanCharge();
|
||||
final LocalDate chargeDate = (chargeOnDueDate || loanCharge.isInstalmentFee())
|
||||
? paidBy.getLoanTransaction().getTransactionDate()
|
||||
: loanCharge.getDueDate();
|
||||
final LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(
|
||||
i -> isInPeriod(chargeDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
|
||||
if (installment != null) {
|
||||
paidBy.setInstallmentNumber(installment.getInstallmentNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retrieveListOfAccrualTransactions(final Loan loan) {
|
||||
return loan.getLoanTransactions().stream().filter(ACCRUAL_PREDICATE).sorted(LoanTransactionComparator.INSTANCE)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<LoanTransaction> retrieveListOfIncomePostingTransactions(final Loan loan) {
|
||||
return loan.getLoanTransactions().stream() //
|
||||
.filter(transaction -> transaction.isNotReversed() && transaction.isIncomePosting()) //
|
||||
return loanTransactionRepository.findNonReversedByLoanAndTypes(loan, ACCRUAL_TYPES).stream()
|
||||
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private LoanTransaction getTransactionForDate(final List<LoanTransaction> transactions, final LocalDate effectiveDate) {
|
||||
for (LoanTransaction loanTransaction : transactions) {
|
||||
if (DateUtils.isEqual(effectiveDate, loanTransaction.getTransactionDate())) {
|
||||
return loanTransaction;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isChargeOnDueDate() {
|
||||
final String chargeAccrualDateType = configurationDomainService.getAccrualDateConfigForCharge();
|
||||
return !ACCRUAL_ON_CHARGE_SUBMITTED_ON_DATE.equalsIgnoreCase(chargeAccrualDateType);
|
||||
@@ -1263,21 +1212,15 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
||||
}
|
||||
}
|
||||
|
||||
private void reverseTransactionsAfter(final List<LoanTransaction> accrualTransactions, final LocalDate effectiveDate,
|
||||
private void reverseTransactionsAfter(final Loan loan, final Set<LoanTransactionType> types, final LocalDate effectiveDate,
|
||||
final boolean addEvent) {
|
||||
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
||||
if (!accrualTransaction.isReversed() && DateUtils.isAfter(accrualTransaction.getTransactionDate(), effectiveDate)) {
|
||||
reverseAccrual(accrualTransaction, addEvent);
|
||||
}
|
||||
}
|
||||
loanTransactionRepository.findNonReversedByLoanAndTypesAndAfterDate(loan, types, effectiveDate)
|
||||
.forEach(transaction -> reverseAccrual(transaction, addEvent));
|
||||
}
|
||||
|
||||
private void reverseTransactionsOnOrAfter(final List<LoanTransaction> accrualTransactions, final LocalDate date) {
|
||||
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
||||
if (!accrualTransaction.isReversed() && !DateUtils.isBefore(accrualTransaction.getTransactionDate(), date)) {
|
||||
reverseAccrual(accrualTransaction, false);
|
||||
}
|
||||
}
|
||||
private void reverseTransactionsOnOrAfter(final Loan loan, final Set<LoanTransactionType> types, final LocalDate date) {
|
||||
loanTransactionRepository.findNonReversedByLoanAndTypesAndOnOrAfterDate(loan, types, date)
|
||||
.forEach(transaction -> reverseAccrual(transaction, false));
|
||||
}
|
||||
|
||||
private void reverseAccrual(final LoanTransaction transaction, final boolean addEvent) {
|
||||
|
||||
Reference in New Issue
Block a user