FINERACT-2080: use enums instead of integers in Loan, LoanProduct and LoanTransaction

This commit is contained in:
mark.vituska
2025-03-20 14:34:51 +01:00
committed by Adam Saghy
parent 8d2db7f80f
commit 993393b519
51 changed files with 467 additions and 291 deletions
@@ -19,6 +19,7 @@
package org.apache.fineract.accounting.producttoaccountmapping.service;
import java.util.Map;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.portfolio.savings.DepositAccountType;
@@ -29,7 +30,7 @@ public interface ProductToGLAccountMappingWritePlatformService {
void createSavingProductToGLAccountMapping(Long savingProductId, JsonCommand command, DepositAccountType accountType);
Map<String, Object> updateLoanProductToGLAccountMapping(Long loanProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId);
AccountingRuleType accountingRuleTypeId);
Map<String, Object> updateSavingsProductToGLAccountMapping(Long savingsProductId, JsonCommand command, boolean accountingRuleChanged,
int accountingRuleTypeId, DepositAccountType accountType);
@@ -380,14 +380,14 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
}
private void validateLoanStatus(LoanDataForExternalTransfer loanDataForExternalTransfer, boolean isDelayedSettlementEnabled) {
LoanStatus loanStatus = LoanStatus.fromInt(loanDataForExternalTransfer.getLoanStatus());
LoanStatus loanStatus = loanDataForExternalTransfer.getLoanStatus();
if (!getValidLoanStatusList(isDelayedSettlementEnabled).contains(loanStatus)) {
throw new ExternalAssetOwnerInitiateTransferException(String.format("Loan status %s is not valid for transfer.", loanStatus));
}
}
private void validateLoanStatusIntermediarySale(LoanDataForExternalTransfer loanDataForExternalTransfer) {
LoanStatus loanStatus = LoanStatus.fromInt(loanDataForExternalTransfer.getLoanStatus());
LoanStatus loanStatus = loanDataForExternalTransfer.getLoanStatus();
if (!ACTIVE_LOAN_STATUSES.contains(loanStatus)) {
throw new ExternalAssetOwnerInitiateTransferException(String.format("Loan status %s is not valid for transfer.", loanStatus));
}
@@ -185,7 +185,7 @@ public class ExternalAssetOwnersWriteServiceTest {
.thenReturn(Optional.of(testContext.loanDataForExternalTransfer));
when(testContext.externalAssetOwnerRepository.findByExternalId(any(ExternalId.class)))
.thenReturn(Optional.of(testContext.externalAssetOwner));
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus.getValue());
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus);
when(testContext.delayedSettlementAttributeService.isEnabled(testContext.loanProductId)).thenReturn(true);
// when
@@ -209,7 +209,7 @@ public class ExternalAssetOwnersWriteServiceTest {
// given
final JsonCommand command = createJsonCommand(testContext.jsonCommand, testContext.loanId);
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus.getValue());
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus);
when(testContext.delayedSettlementAttributeService.isEnabled(testContext.loanProductId)).thenReturn(true);
when(testContext.loanRepository.findLoanDataForExternalTransferByLoanId(testContext.loanId))
.thenReturn(Optional.of(testContext.loanDataForExternalTransfer));
@@ -242,7 +242,7 @@ public class ExternalAssetOwnersWriteServiceTest {
// given
final JsonCommand command = createJsonCommand(testContext.jsonCommand, testContext.loanId);
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus.getValue());
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus);
when(testContext.delayedSettlementAttributeService.isEnabled(testContext.loanProductId)).thenReturn(true);
when(testContext.loanRepository.findLoanDataForExternalTransferByLoanId(testContext.loanId))
.thenReturn(Optional.of(testContext.loanDataForExternalTransfer));
@@ -278,7 +278,7 @@ public class ExternalAssetOwnersWriteServiceTest {
// given
final JsonCommand command = createJsonCommand(testContext.jsonCommand, testContext.loanId);
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus.getValue());
when(testContext.loanDataForExternalTransfer.getLoanStatus()).thenReturn(loanStatus);
when(testContext.delayedSettlementAttributeService.isEnabled(testContext.loanProductId)).thenReturn(true);
when(testContext.loanRepository.findLoanDataForExternalTransferByLoanId(testContext.loanId))
.thenReturn(Optional.of(testContext.loanDataForExternalTransfer));
@@ -924,7 +924,7 @@ public class ExternalAssetOwnersWriteServiceTest {
lenient().when(loanDataForExternalTransfer.getId()).thenReturn(loanId);
lenient().when(loanDataForExternalTransfer.getExternalId()).thenReturn(new ExternalId(externalLoanId));
lenient().when(loanDataForExternalTransfer.getLoanStatus()).thenReturn(LoanStatus.ACTIVE.getValue());
lenient().when(loanDataForExternalTransfer.getLoanStatus()).thenReturn(LoanStatus.ACTIVE);
lenient().when(loanDataForExternalTransfer.getLoanProductId()).thenReturn(loanProductId);
lenient().when(loanDataForExternalTransfer.getLoanProductShortName()).thenReturn(loanProductShortName);
}
@@ -21,6 +21,7 @@ package org.apache.fineract.cob.data;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
@AllArgsConstructor
@Getter
@@ -28,7 +29,7 @@ public class LoanDataForExternalTransfer {
Long id;
ExternalId externalId;
Integer loanStatus;
LoanStatus loanStatus;
Long loanProductId;
String loanProductShortName;
}
@@ -266,7 +266,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
final MonetaryCurrency loanCurrency = loan.getCurrency();
LoanRepaymentScheduleInstallment latestInstallment = loan.getLastLoanRepaymentScheduleInstallment();
List<LoanTransaction> chargebackTransactions = loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null,
LoanTransactionType.CHARGEBACK.getValue());
LoanTransactionType.CHARGEBACK);
LocalDate overdueSinceDate = null;
CollectionData collectionData = CollectionData.template();
BigDecimal outstandingAmount = BigDecimal.ZERO;
@@ -324,7 +324,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
BigDecimal delinquentPenalty = BigDecimal.ZERO;
List<LoanTransaction> chargebackTransactions = loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null,
LoanTransactionType.CHARGEBACK.getValue());
LoanTransactionType.CHARGEBACK);
BigDecimal amountAvailable = installment.getTotalPaid(loanCurrency).getAmount();
for (LoanTransaction loanTransaction : chargebackTransactions) {
@@ -207,7 +207,7 @@ public class InterestPauseWritePlatformServiceImpl implements InterestPauseWrite
}
private void validateActiveLoan(Loan loan) {
if (!Objects.equals(loan.getLoanStatus(), ACTIVE.getValue())) {
if (!Objects.equals(loan.getLoanStatus(), ACTIVE)) {
throw new GeneralPlatformDomainRuleException("loan.must.be.active",
"Operations on interest pauses are restricted to active loans.");
}
@@ -181,16 +181,16 @@ public interface LoanApiConstants {
String INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE = "interestRecognitionOnDisbursementDate";
// Loan Summary Transaction Types
List<Integer> LOAN_SUMMARY_TRANSACTION_TYPES = List.of(LoanTransactionType.CHARGE_ADJUSTMENT.getValue(), //
LoanTransactionType.CHARGEBACK.getValue(), //
LoanTransactionType.CREDIT_BALANCE_REFUND.getValue(), //
LoanTransactionType.DOWN_PAYMENT.getValue(), //
LoanTransactionType.GOODWILL_CREDIT.getValue(), //
LoanTransactionType.INTEREST_PAYMENT_WAIVER.getValue(), //
LoanTransactionType.INTEREST_REFUND.getValue(), //
LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue(), //
LoanTransactionType.PAYOUT_REFUND.getValue(), //
LoanTransactionType.REPAYMENT.getValue() //
List<LoanTransactionType> LOAN_SUMMARY_TRANSACTION_TYPES = List.of(LoanTransactionType.CHARGE_ADJUSTMENT, //
LoanTransactionType.CHARGEBACK, //
LoanTransactionType.CREDIT_BALANCE_REFUND, //
LoanTransactionType.DOWN_PAYMENT, //
LoanTransactionType.GOODWILL_CREDIT, //
LoanTransactionType.INTEREST_PAYMENT_WAIVER, //
LoanTransactionType.INTEREST_REFUND, //
LoanTransactionType.MERCHANT_ISSUED_REFUND, //
LoanTransactionType.PAYOUT_REFUND, //
LoanTransactionType.REPAYMENT //
);
}
@@ -59,7 +59,7 @@ public class LoanStatusEnumData {
public interface Mapper {
default LoanStatusEnumData map(Loan source) {
return LoanEnumerations.status(source.getPlainStatus());
return LoanEnumerations.status(source.getStatus());
}
}
}
@@ -20,11 +20,12 @@ package org.apache.fineract.portfolio.loanaccount.data;
import java.math.BigDecimal;
import lombok.Data;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
@Data
public class LoanTransactionBalance {
private final Integer transactionType;
private final LoanTransactionType transactionType;
private final boolean reversed;
private final boolean manuallyAdjustedOrReversed;
private final BigDecimal amount;
@@ -0,0 +1,37 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.domain;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import org.apache.fineract.accounting.common.AccountingRuleType;
@Converter(autoApply = true)
public class AccountingRuleTypeConverter implements AttributeConverter<AccountingRuleType, Integer> {
@Override
public Integer convertToDatabaseColumn(AccountingRuleType attribute) {
return attribute == null ? null : attribute.getValue();
}
@Override
public AccountingRuleType convertToEntityAttribute(Integer dbData) {
return AccountingRuleType.fromInt(dbData);
}
}
@@ -50,8 +50,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
LoanStatus oldStatus = loan.getStatus();
LoanStatus newStatus = getNextStatus(loanEvent, loan);
if (newStatus != null) {
Integer newPlainStatus = newStatus.getValue();
loan.setLoanStatus(newPlainStatus);
loan.setLoanStatus(newStatus);
if (isNotLoanCreation(loanEvent)) {
// in case of Loan creation, a LoanCreatedBusinessEvent is also raised, no need to send a status change
@@ -85,12 +84,11 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
}
private LoanStatus getNextStatus(LoanEvent loanEvent, Loan loan) {
Integer plainFrom = loan.getPlainStatus();
if (loanEvent.equals(LoanEvent.LOAN_CREATED) && plainFrom == null) {
LoanStatus from = loan.getStatus();
if (loanEvent.equals(LoanEvent.LOAN_CREATED) && from == null) {
return submittedTransition();
}
LoanStatus from = loan.getStatus();
LoanStatus newState = null;
switch (loanEvent) {
@@ -28,6 +28,7 @@ import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
@@ -183,8 +184,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@JoinColumn(name = "glim_id")
private GroupLoanIndividualMonitoringAccount glim;
@Enumerated
@Column(name = "loan_type_enum", nullable = false)
private Integer loanType;
private AccountType loanType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", nullable = false)
@@ -227,12 +229,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
private Integer termFrequency;
@Setter()
@Enumerated
@Column(name = "term_period_frequency_enum", nullable = false)
private Integer termPeriodFrequencyType;
private PeriodFrequencyType termPeriodFrequencyType;
@Setter(AccessLevel.PACKAGE)
@Column(name = "loan_status_id", nullable = false)
private Integer loanStatus;
private LoanStatus loanStatus;
@Setter()
@Column(name = "sync_disbursement_with_meeting")
@@ -435,7 +438,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
@Setter
@Column(name = "loan_sub_status_id")
private Integer loanSubStatus;
private LoanSubStatus loanSubStatus;
@Column(name = "is_topup", nullable = false)
private boolean isTopup = false;
@@ -553,7 +556,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
this.client = client;
this.group = group;
this.loanType = loanType.getValue();
this.loanType = loanType;
this.fund = fund;
this.loanOfficer = loanOfficer;
this.loanPurpose = loanPurpose;
@@ -562,11 +565,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
this.transactionProcessingStrategyName = transactionProcessingStrategy.getName();
this.loanProduct = loanProduct;
if (loanStatus != null) {
this.loanStatus = loanStatus.getValue();
} else {
this.loanStatus = null;
}
this.loanStatus = loanStatus;
if (loanCharges != null && !loanCharges.isEmpty()) {
this.charges = associateChargesWithThisLoan(loanCharges);
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
@@ -607,10 +606,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
// Add net get net disbursal amount from charges and principal
this.netDisbursalAmount = this.approvedPrincipal.subtract(deriveSumTotalOfChargesDueAtDisbursement());
this.loanStatus = LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue();
this.loanStatus = LoanStatus.SUBMITTED_AND_PENDING_APPROVAL;
this.externalId = externalId;
this.termFrequency = loanApplicationTerms.getLoanTermFrequency();
this.termPeriodFrequencyType = loanApplicationTerms.getLoanTermPeriodFrequencyType().getValue();
this.termPeriodFrequencyType = loanApplicationTerms.getLoanTermPeriodFrequencyType();
this.expectedDisbursementDate = loanApplicationTerms.getExpectedDisbursementDate();
this.expectedFirstRepaymentOnDate = loanApplicationTerms.getRepaymentStartFromDate();
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
@@ -1827,10 +1826,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
public LoanStatus getStatus() {
return this.loanStatus == null ? null : LoanStatus.fromInt(this.loanStatus);
}
public Integer getPlainStatus() {
return this.loanStatus;
}
@@ -2450,11 +2445,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
public boolean isGroupLoan() {
return AccountType.fromInt(this.loanType).isGroupAccount();
return this.loanType.isGroupAccount();
}
public boolean isJLGLoan() {
return AccountType.fromInt(this.loanType).isJLGAccount();
return this.loanType.isJLGAccount();
}
public void updateInterestRateFrequencyType() {
@@ -2738,7 +2733,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public LoanApplicationTerms constructLoanApplicationTerms(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final Integer loanTermFrequency = this.termFrequency;
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
NthDayType nthDayType = null;
DayOfWeekType dayOfWeekType = null;
final List<DisbursementData> disbursementData = new ArrayList<>();
@@ -2782,7 +2776,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
interestChargedFromDate = getDisbursementDate();
}
return LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getCurrency(), loanTermFrequency, loanTermPeriodFrequencyType,
return LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getCurrency(), loanTermFrequency, this.termPeriodFrequencyType,
nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
@@ -3386,7 +3380,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
public boolean isForeclosure() {
boolean isForeClosure = false;
if (this.loanSubStatus != null) {
isForeClosure = LoanSubStatus.fromInt(loanSubStatus).isForeclosed();
isForeClosure = loanSubStatus.isForeclosed();
}
return isForeClosure;
@@ -3464,15 +3458,15 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
}
public boolean hasInvalidLoanType() {
return AccountType.fromInt(this.loanType).isInvalid();
return getLoanType().isInvalid();
}
public boolean isIndividualLoan() {
return AccountType.fromInt(this.loanType).isIndividualAccount();
return getLoanType().isIndividualAccount();
}
public AccountType getLoanType() {
return AccountType.fromInt(loanType);
return this.loanType == null ? AccountType.INVALID : this.loanType;
}
public void adjustNetDisbursalAmount(BigDecimal adjustedAmount) {
@@ -26,7 +26,8 @@ import org.springframework.data.repository.query.Param;
public interface LoanAccrualActivityRepository extends Repository<Loan, Long> {
@Query("select loan.id from Loan loan left join LoanTransaction lt on lt.loan = loan and lt.typeOf = 32 and lt.reversed = false and lt.dateOf = :currentDate inner join LoanRepaymentScheduleInstallment rs on rs.loan = loan and rs.isDownPayment = false and rs.additional = false and rs.dueDate = :currentDate where loan.loanRepaymentScheduleDetail.enableAccrualActivityPosting = true and loan.loanStatus = 300 and lt.id is null ")
Set<Long> fetchLoanIdsForAccrualActivityPosting(@Param("currentDate") LocalDate currentDate);
@Query("select loan.id from Loan loan left join LoanTransaction lt on lt.loan = loan and lt.typeOf = :loanType and lt.reversed = false and lt.dateOf = :currentDate inner join LoanRepaymentScheduleInstallment rs on rs.loan = loan and rs.isDownPayment = false and rs.additional = false and rs.dueDate = :currentDate where loan.loanRepaymentScheduleDetail.enableAccrualActivityPosting = true and loan.loanStatus = :loanStatus and lt.id is null ")
Set<Long> fetchLoanIdsForAccrualActivityPosting(@Param("currentDate") LocalDate currentDate,
@Param("loanType") LoanTransactionType loanTransactionType, @Param("loanStatus") LoanStatus loanStatus);
}
@@ -43,7 +43,7 @@ public interface LoanRepaymentScheduleInstallmentRepository
lrs.loan.loanProduct.delinquencyBucket IS NOT NULL
GROUP BY lrs.loan
""")
Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(@Param("loanStatus") Integer loanStatus,
Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(@Param("loanStatus") LoanStatus loanStatus,
@Param("businessDate") LocalDate businessDate, @Param("obligationsMet") boolean obligationsMet);
@Query("""
@@ -60,7 +60,7 @@ public interface LoanRepaymentScheduleInstallmentRepository
lrs.loan.id NOT IN :loanIds
GROUP BY lrs.loan
""")
Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(@Param("loanStatus") Integer loanStatus,
Collection<LoanScheduleDelinquencyData> fetchLoanScheduleDataByDueDateAndObligationsMet(@Param("loanStatus") LoanStatus loanStatus,
@Param("businessDate") LocalDate businessDate, @Param("obligationsMet") boolean obligationsMet,
@Param("loanIds") List<Long> loanIds);
@@ -22,10 +22,12 @@ import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.cob.data.LoanDataForExternalTransfer;
import org.apache.fineract.cob.data.LoanIdAndExternalIdAndAccountNo;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
@@ -68,43 +70,43 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
String FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_GROUP = "Select loan.loanProduct.id from Loan loan where "
+ "loan.group.id = :groupId and loan.loanStatus = :loanStatus and loan.client.id is NULL group by loan.loanProduct.id";
String DOES_CLIENT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.client.id = :clientId and loan.loanStatus in (100,200,300,303,304,700)";
String DOES_CLIENT_HAVE_LOANS_WITH_STATUSES = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.client.id = :clientId and loan.loanStatus in :loanStatuses";
String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)";
String DOES_PRODUCT_HAVE_LOANS_WITH_STATUSES = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in :loanStatuses";
String FIND_NON_CLOSED_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)";
String FIND_LOANS_BY_ACCOUNT_NUMBER_AND_STATUSES = "select loan from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in :loanStatuses";
String FIND_ALL_NON_CLOSED = "select loan.id from Loan loan where loan.loanStatus in (100,200,300,303,304)";
String FIND_ALL_BY_STATUSES = "select loan.id from Loan loan where loan.loanStatus in :loanStatuses";
String FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus = 300 and loan.client.id = :clientId";
String FIND_LOAN_BY_CLIENT_AND_STATUS = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus = :loanStatus and loan.client.id = :clientId";
String FIND_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber";
String FIND_LOAN_DATA_FOR_EXTERNAL_TRANSFER = "select new org.apache.fineract.cob.data.LoanDataForExternalTransfer(loan.id, loan.externalId, loan.loanStatus, loan.loanProduct.id, loan.loanProduct.shortName) from Loan loan where loan.id = :loanId";
String EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.externalId = :externalLoanId and loan.loanStatus in (100,200,300,303,304)";
String EXISTS_BY_EXTERNAL_LOAN_ID_AND_STATUSES = "select case when (count (loan) > 0) then 'true' else 'false' end from Loan loan where loan.externalId = :externalLoanId and loan.loanStatus in :loanStatuses";
String FIND_ID_BY_EXTERNAL_ID = "SELECT loan.id FROM Loan loan WHERE loan.externalId = :externalId";
String FIND_IDS_BY_EXTERNAL_IDS = "SELECT loan.id FROM Loan loan WHERE loan.externalId IN :externalIds ORDER BY loan.id ASC";
// should follow the logic of `FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE` query
String FIND_OLDEST_COB_PROCESSED_LOAN = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.loanStatus in (100,200,300,303,304) and loan.lastClosedBusinessDate = (select min(l.lastClosedBusinessDate) from Loan l where l"
+ ".loanStatus in (100,200,300,303,304) and l.lastClosedBusinessDate < :cobBusinessDate)";
// should follow the logic of `FIND_ALL_LOANS_BY_LAST_CLOSED_BUSINESS_DATE` query
String FIND_OLDEST_COB_PROCESSED_LOAN = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.loanStatus in :loanStatuses and loan.lastClosedBusinessDate = (select min(l.lastClosedBusinessDate) from Loan l where l"
+ ".loanStatus in :loanStatuses and l.lastClosedBusinessDate < :cobBusinessDate)";
String FIND_ALL_NON_CLOSED_LOANS_BEHIND_OR_NULL_BY_LOAN_IDS = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in (100,200,300,303,304) and (loan.lastClosedBusinessDate < :cobBusinessDate or "
String FIND_ALL_LOANS_BEHIND_OR_NULL_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 or "
+ "loan.lastClosedBusinessDate is null)";
String FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_AND_MIN_AND_MAX_LOAN_ID = "select loan.id from Loan loan where loan.id BETWEEN :minLoanId and :maxLoanId and loan.loanStatus in (100,200,300,303,304) and (:cobBusinessDate = loan.lastClosedBusinessDate or loan.lastClosedBusinessDate is NULL)";
String FIND_ALL_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_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 or loan.lastClosedBusinessDate is NULL)";
String FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_NOT_NULL_AND_MIN_AND_MAX_LOAN_ID = "select loan.id from Loan loan where loan.id BETWEEN :minLoanId and :maxLoanId and loan.loanStatus in (100,200,300,303,304) and :cobBusinessDate = loan.lastClosedBusinessDate";
String FIND_ALL_NON_CLOSED_LOANS_BEHIND_BY_LOAN_IDS = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in (100,200,300,303,304) and loan.lastClosedBusinessDate < :cobBusinessDate";
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_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";
String FIND_ALL_LOAN_IDS_BY_STATUS_ID = "SELECT loan.id FROM Loan loan WHERE loan.loanStatus = :statusId";
String FIND_ALL_LOAN_IDS_BY_STATUS = "SELECT loan.id FROM Loan loan WHERE loan.loanStatus = :loanStatus";
String LOANS_FOR_ACCRUAL = "select l from Loan l left join l.loanInterestRecalculationDetails recalcDetails "
+ "where l.loanStatus = 300 and l.isNpa = false and l.chargedOff = false "
+ "where l.loanStatus = :loanStatus and l.isNpa = false and l.chargedOff = false "
+ "and l.loanProduct.accountingRule = :accountingType "
+ "and (recalcDetails.isCompoundingToBePostedAsTransaction is null or recalcDetails.isCompoundingToBePostedAsTransaction = false) "
+ "and (exists (select ls.id from LoanRepaymentScheduleInstallment ls where ls.loan.id = l.id and ls.isDownPayment = false "
@@ -119,17 +121,17 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") LocalDate disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType);
@Param("loanType") AccountType loanType);
@Query(FIND_CLIENT_OR_JLG_LOANS_DISBURSED_AFTER)
List<Loan> getClientOrJLGLoansDisbursedAfter(@Param("disbursementDate") LocalDate disbursementDate, @Param("clientId") Long clientId);
@Query(FIND_MAX_GROUP_LOAN_COUNTER_QUERY)
Integer getMaxGroupLoanCounter(@Param("groupId") Long groupId, @Param("loanType") Integer loanType);
Integer getMaxGroupLoanCounter(@Param("groupId") Long groupId, @Param("loanType") AccountType loanType);
@Query(FIND_MAX_GROUP_LOAN_PRODUCT_COUNTER_QUERY)
Integer getMaxGroupLoanProductCounter(@Param("productId") Long productId, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType);
@Param("loanType") AccountType loanType);
@Query(FIND_MAX_CLIENT_OR_JLG_LOAN_COUNTER_QUERY)
Integer getMaxClientOrJLGLoanCounter(@Param("clientId") Long clientId);
@@ -139,14 +141,14 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(FIND_GROUP_LOANS_TO_UPDATE)
List<Loan> getGroupLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("groupId") Long groupId,
@Param("groupLoanType") Integer groupLoanType);
@Param("groupLoanType") AccountType groupLoanType);
@Query(FIND_CLIENT_OR_JLG_LOANS_TO_UPDATE)
List<Loan> getClientOrJLGLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("clientId") Long clientId);
@Query(FIND_GROUP_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER)
List<Loan> getGroupLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter,
@Param("groupId") Long groupId, @Param("groupLoanType") Integer groupLoanType);
@Param("groupId") Long groupId, @Param("groupLoanType") AccountType groupLoanType);
@Query(FIND_CLIENT_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER)
List<Loan> getClientLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter,
@@ -157,7 +159,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query("select loan from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId and loan.loanStatus IN :loanStatuses")
List<Loan> findByClientIdAndGroupIdAndLoanStatus(@Param("clientId") Long clientId, @Param("groupId") Long groupId,
@Param("loanStatuses") Collection<Integer> loanStatuses);
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query("select loan from Loan loan where loan.client.id = :clientId")
List<Loan> findLoanByClientId(@Param("clientId") Long clientId);
@@ -170,37 +172,41 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query("select loan from Loan loan where loan.id IN :ids and loan.loanStatus IN :loanStatuses and loan.loanType IN :loanTypes")
List<Loan> findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection<Long> ids,
@Param("loanStatuses") Collection<Integer> loanStatuses, @Param("loanTypes") Collection<Integer> loanTypes);
@Param("loanStatuses") Collection<LoanStatus> loanStatuses, @Param("loanTypes") Collection<AccountType> loanTypes);
@Query("select loan.id from Loan loan where loan.actualDisbursementDate > :disbursalDate order by loan.actualDisbursementDate")
List<Long> getLoansDisbursedAfter(@Param("disbursalDate") LocalDate disbursalDate);
@Query("select loan from Loan loan where loan.client.office.id IN :officeIds and loan.loanStatus IN :loanStatuses")
List<Loan> findByClientOfficeIdsAndLoanStatus(@Param("officeIds") Collection<Long> officeIds,
@Param("loanStatuses") Collection<Integer> loanStatuses);
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query("select loan from Loan loan where loan.group.office.id IN :officeIds and loan.loanStatus IN :loanStatuses")
List<Loan> findByGroupOfficeIdsAndLoanStatus(@Param("officeIds") Collection<Long> officeIds,
@Param("loanStatuses") Collection<Integer> loanStatuses);
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
/*** FIXME: Add more appropriate names for the query ***/
@Query(FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_CLIENT)
List<Long> findActiveLoansLoanProductIdsByClient(@Param("clientId") Long clientId, @Param("loanStatus") Integer loanStatus);
List<Long> findActiveLoansLoanProductIdsByClient(@Param("clientId") Long clientId, @Param("loanStatus") LoanStatus loanStatus);
@Query(FIND_ACTIVE_LOANS_PRODUCT_IDS_BY_GROUP)
List<Long> findActiveLoansLoanProductIdsByGroup(@Param("groupId") Long groupId, @Param("loanStatus") Integer loanStatus);
List<Long> findActiveLoansLoanProductIdsByGroup(@Param("groupId") Long groupId, @Param("loanStatus") LoanStatus loanStatus);
@Query(DOES_CLIENT_HAVE_NON_CLOSED_LOANS)
boolean doNonClosedLoanAccountsExistForClient(@Param("clientId") Long clientId);
@Query(DOES_CLIENT_HAVE_LOANS_WITH_STATUSES)
boolean doLoanAccountsWithLoansInStatusesExistForClient(@Param("clientId") Long clientId,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
@Query(DOES_PRODUCT_HAVE_LOANS_WITH_STATUSES)
boolean doLoanAccountsWithLoansInStatusesExistForProduct(@Param("productId") Long productId,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_NON_CLOSED_BY_ACCOUNT_NUMBER)
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
@Query(FIND_LOANS_BY_ACCOUNT_NUMBER_AND_STATUSES)
Loan findLoanByAccountNumberAndStatuses(@Param("accountNumber") String accountNumber,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT)
Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId);
@Query(FIND_LOAN_BY_CLIENT_AND_STATUS)
Loan findLoanByClientAndStatus(@Param("loanId") Long loanId, @Param("clientId") Long clientId,
@Param("loanStatus") LoanStatus loanStatus);
@Query(FIND_BY_ACCOUNT_NUMBER)
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber);
@@ -208,13 +214,14 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(FIND_LOAN_DATA_FOR_EXTERNAL_TRANSFER)
Optional<LoanDataForExternalTransfer> findLoanDataForExternalTransferByLoanId(@Param("loanId") Long loanId);
@Query(EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID)
boolean existsNonClosedLoanByExternalLoanId(@Param("externalLoanId") ExternalId externalLoanId);
@Query(EXISTS_BY_EXTERNAL_LOAN_ID_AND_STATUSES)
boolean existsLoanByExternalLoanIdAndStatuses(@Param("externalLoanId") ExternalId externalLoanId,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
boolean existsByExternalId(@Param("externalId") ExternalId externalId);
@Query(FIND_ALL_NON_CLOSED)
List<Long> findAllNonClosedLoanIds();
@Query(FIND_ALL_BY_STATUSES)
List<Long> findAllLoanIdsByStatuses(@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_ID_BY_EXTERNAL_ID)
Long findIdByExternalId(@Param("externalId") ExternalId externalId);
@@ -222,38 +229,42 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(FIND_IDS_BY_EXTERNAL_IDS)
List<Long> findIdsByExternalIds(@Param("externalIds") List<ExternalId> externalIds);
@Query(FIND_ALL_NON_CLOSED_LOANS_BEHIND_BY_LOAN_IDS)
List<LoanIdAndLastClosedBusinessDate> findAllNonClosedLoansBehindByLoanIds(@Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanIds") List<Long> loanIds);
@Query(FIND_ALL_LOANS_BEHIND_BY_LOAN_IDS_AND_STATUSES)
List<LoanIdAndLastClosedBusinessDate> findAllLoansBehindByLoanIdsAndStatuses(@Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanIds") List<Long> loanIds, @Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_ALL_NON_CLOSED_LOANS_BEHIND_OR_NULL_BY_LOAN_IDS)
List<LoanIdAndLastClosedBusinessDate> findAllNonClosedLoansBehindOrNullByLoanIds(@Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanIds") List<Long> loanIds);
@Query(FIND_ALL_LOANS_BEHIND_OR_NULL_BY_LOAN_IDS_AND_STATUSES)
List<LoanIdAndLastClosedBusinessDate> findAllLoansBehindOrNullByLoanIdsAndStatuses(@Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanIds") List<Long> loanIds, @Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_AND_MIN_AND_MAX_LOAN_ID)
List<Long> findAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(@Param("minLoanId") Long minLoanId,
@Param("maxLoanId") Long maxLoanId, @Param("cobBusinessDate") LocalDate cobBusinessDate);
@Query(FIND_ALL_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_AND_MIN_AND_MAX_LOAN_ID_AND_STATUSES)
List<Long> findAllLoansByLastClosedBusinessDateAndMinAndMaxLoanIdAndStatuses(@Param("minLoanId") Long minLoanId,
@Param("maxLoanId") Long maxLoanId, @Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_NOT_NULL_AND_MIN_AND_MAX_LOAN_ID)
List<Long> findAllNonClosedLoansByLastClosedBusinessDateNotNullAndMinAndMaxLoanId(@Param("minLoanId") Long minLoanId,
@Param("maxLoanId") Long maxLoanId, @Param("cobBusinessDate") LocalDate cobBusinessDate);
@Query(FIND_ALL_LOANS_BY_LAST_CLOSED_BUSINESS_DATE_NOT_NULL_AND_MIN_AND_MAX_LOAN_ID_AND_STATUSES)
List<Long> findAllLoansByLastClosedBusinessDateNotNullAndMinAndMaxLoanIdAndStatuses(@Param("minLoanId") Long minLoanId,
@Param("maxLoanId") Long maxLoanId, @Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_OLDEST_COB_PROCESSED_LOAN)
List<LoanIdAndLastClosedBusinessDate> findOldestCOBProcessedLoan(@Param("cobBusinessDate") LocalDate cobBusinessDate);
List<LoanIdAndLastClosedBusinessDate> findOldestCOBProcessedLoan(@Param("cobBusinessDate") LocalDate cobBusinessDate,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
@Query(FIND_ALL_STAYED_LOCKED_BY_COB_BUSINESS_DATE)
List<LoanIdAndExternalIdAndAccountNo> findAllStayedLockedByCobBusinessDate(@Param("cobBusinessDate") LocalDate cobBusinessDate);
@Query(FIND_ALL_LOAN_IDS_BY_STATUS_ID)
List<Long> findLoanIdByStatusId(@Param("statusId") Integer statusId);
@Query(FIND_ALL_LOAN_IDS_BY_STATUS)
List<Long> findLoanIdByStatus(@Param("loanStatus") LoanStatus loanStatus);
@Query(FIND_LOANS_FOR_PERIODIC_ACCRUAL)
List<Loan> findLoansForPeriodicAccrual(@Param("accountingType") Integer accountingType, @Param("tillDate") LocalDate tillDate,
@Param("futureCharges") boolean futureCharges);
List<Loan> findLoansForPeriodicAccrual(@Param("accountingType") AccountingRuleType accountingType,
@Param("tillDate") LocalDate tillDate, @Param("futureCharges") boolean futureCharges,
@Param("loanStatus") LoanStatus loanStatus);
@Query(FIND_LOANS_FOR_ADD_ACCRUAL)
List<Loan> findLoansForAddAccrual(@Param("accountingType") Integer accountingType, @Param("tillDate") LocalDate tillDate,
@Param("futureCharges") boolean futureCharges);
List<Loan> findLoansForAddAccrual(@Param("accountingType") AccountingRuleType accountingType, @Param("tillDate") LocalDate tillDate,
@Param("futureCharges") boolean futureCharges, @Param("loanStatus") LoanStatus loanStatus);
@Query(FIND_LOAN_BY_EXTERNAL_ID)
Optional<Loan> findByExternalId(@Param("externalId") ExternalId externalId);
@@ -25,8 +25,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Service;
@@ -41,6 +43,13 @@ import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class LoanRepositoryWrapper {
private static final Collection<LoanStatus> NON_CLOSED_LOAN_STATUSES = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE,
LoanStatus.TRANSFER_IN_PROGRESS, LoanStatus.TRANSFER_ON_HOLD));
private static final Collection<LoanStatus> NON_CLOSED_AND_OVERPAID_LOAN_STATUSES = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE,
LoanStatus.TRANSFER_IN_PROGRESS, LoanStatus.TRANSFER_ON_HOLD, LoanStatus.OVERPAID));
private final LoanRepository repository;
private final FineractProperties fineractProperties;
@@ -74,8 +83,8 @@ public class LoanRepositoryWrapper {
// Root Entities are enough
public Collection<Loan> findActiveLoansByLoanIdAndGroupId(Long clientId, Long groupId) {
final Collection<Integer> loanStatuses = new ArrayList<>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue(), LoanStatus.OVERPAID.getValue()));
final Collection<LoanStatus> loanStatuses = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE, LoanStatus.OVERPAID));
return this.repository.findByClientIdAndGroupIdAndLoanStatus(clientId, groupId, loanStatuses);
}
@@ -102,7 +111,7 @@ public class LoanRepositoryWrapper {
// Only root entities is enough
public List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") LocalDate disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType) {
@Param("loanType") AccountType loanType) {
return this.repository.getGroupLoansDisbursedAfter(disbursementDate, groupId, loanType);
}
@@ -112,12 +121,12 @@ public class LoanRepositoryWrapper {
return this.repository.getClientOrJLGLoansDisbursedAfter(disbursementDate, clientId);
}
public Integer getMaxGroupLoanCounter(@Param("groupId") Long groupId, @Param("loanType") Integer loanType) {
public Integer getMaxGroupLoanCounter(@Param("groupId") Long groupId, @Param("loanType") AccountType loanType) {
return this.repository.getMaxGroupLoanCounter(groupId, loanType);
}
public Integer getMaxGroupLoanProductCounter(@Param("productId") Long productId, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType) {
@Param("loanType") AccountType loanType) {
return this.repository.getMaxGroupLoanProductCounter(productId, groupId, loanType);
}
@@ -140,7 +149,7 @@ public class LoanRepositoryWrapper {
// Only root entities are enough
public List<Loan> getGroupLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("groupId") Long groupId,
@Param("groupLoanType") Integer groupLoanType) {
@Param("groupLoanType") AccountType groupLoanType) {
return this.repository.getGroupLoansToUpdateLoanCounter(loanCounter, groupId, groupLoanType);
}
@@ -151,7 +160,7 @@ public class LoanRepositoryWrapper {
// Only root entities are enough
public List<Loan> getGroupLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter,
@Param("groupId") Long groupId, @Param("groupLoanType") Integer groupLoanType) {
@Param("groupId") Long groupId, @Param("groupLoanType") AccountType groupLoanType) {
return this.repository.getGroupLoansToUpdateLoanProductCounter(loanProductCounter, groupId, groupLoanType);
}
@@ -193,7 +202,7 @@ public class LoanRepositoryWrapper {
// Looks like we need complete Data
public List<Loan> findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection<Long> ids,
@Param("loanStatuses") Collection<Integer> loanStatuses, @Param("loanTypes") Collection<Integer> loanTypes) {
@Param("loanStatuses") Collection<LoanStatus> loanStatuses, @Param("loanTypes") Collection<AccountType> loanTypes) {
List<Loan> loans = new ArrayList<>();
List<List<Long>> partitions = Lists.partition(ids.stream().toList(), fineractProperties.getQuery().getInClauseParameterSizeLimit());
partitions
@@ -213,7 +222,7 @@ public class LoanRepositoryWrapper {
// Repayments Schedule
public List<Loan> findByClientOfficeIdsAndLoanStatus(@Param("officeIds") Collection<Long> officeIds,
@Param("loanStatuses") Collection<Integer> loanStatuses) {
@Param("loanStatuses") Collection<LoanStatus> loanStatuses) {
List<Loan> loans = this.repository.findByClientOfficeIdsAndLoanStatus(officeIds, loanStatuses);
if (loans != null && loans.size() > 0) {
for (Loan loan : loans) {
@@ -225,7 +234,7 @@ public class LoanRepositoryWrapper {
// Repayments Schedule
public List<Loan> findByGroupOfficeIdsAndLoanStatus(@Param("officeIds") Collection<Long> officeIds,
@Param("loanStatuses") Collection<Integer> loanStatuses) {
@Param("loanStatuses") Collection<LoanStatus> loanStatuses) {
List<Loan> loans = this.repository.findByGroupOfficeIdsAndLoanStatus(officeIds, loanStatuses);
if (loans != null && loans.size() > 0) {
for (Loan loan : loans) {
@@ -235,34 +244,42 @@ public class LoanRepositoryWrapper {
return loans;
}
public List<Long> findActiveLoansLoanProductIdsByClient(@Param("clientId") Long clientId, @Param("loanStatus") Integer loanStatus) {
public List<Long> findActiveLoansLoanProductIdsByClient(@Param("clientId") Long clientId, @Param("loanStatus") LoanStatus loanStatus) {
return this.repository.findActiveLoansLoanProductIdsByClient(clientId, loanStatus);
}
public List<Long> findActiveLoansLoanProductIdsByGroup(@Param("groupId") Long groupId, @Param("loanStatus") Integer loanStatus) {
public List<Long> findActiveLoansLoanProductIdsByGroup(@Param("groupId") Long groupId, @Param("loanStatus") LoanStatus loanStatus) {
return this.repository.findActiveLoansLoanProductIdsByGroup(groupId, loanStatus);
}
public boolean doNonClosedLoanAccountsExistForClient(@Param("clientId") Long clientId) {
return this.repository.doNonClosedLoanAccountsExistForClient(clientId);
return this.repository.doLoanAccountsWithLoansInStatusesExistForClient(clientId, NON_CLOSED_AND_OVERPAID_LOAN_STATUSES);
}
public boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId) {
return this.repository.doNonClosedLoanAccountsExistForProduct(productId);
return this.repository.doLoanAccountsWithLoansInStatusesExistForProduct(productId, NON_CLOSED_AND_OVERPAID_LOAN_STATUSES);
}
public Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber) {
return this.repository.findNonClosedLoanByAccountNumber(accountNumber);
return this.repository.findLoanByAccountNumberAndStatuses(accountNumber, NON_CLOSED_LOAN_STATUSES);
}
public boolean existsNonClosedLoanByExternalLoanId(final ExternalId externalId) {
return this.repository.existsLoanByExternalLoanIdAndStatuses(externalId, NON_CLOSED_LOAN_STATUSES);
}
public boolean existLoanByExternalId(final ExternalId externalId) {
return this.repository.existsByExternalId(externalId);
}
public List<Long> findAllNonClosedLoanIds() {
return this.repository.findAllLoanIdsByStatuses(NON_CLOSED_LOAN_STATUSES);
}
// Looks like we need complete entity
@Transactional(readOnly = true)
public Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId) {
Loan loan = this.repository.findNonClosedLoanThatBelongsToClient(loanId, clientId);
Loan loan = this.repository.findLoanByClientAndStatus(loanId, clientId, LoanStatus.ACTIVE);
if (loan != null) {
loan.initializeTransactions();
}
@@ -274,15 +291,15 @@ public class LoanRepositoryWrapper {
}
public List<Long> findLoanIdsByStatusId(Integer statusId) {
return repository.findLoanIdByStatusId(statusId);
return repository.findLoanIdByStatus(LoanStatus.fromInt(statusId));
}
public List<Loan> findLoansForPeriodicAccrual(Integer accountingType, LocalDate tillDate, boolean futureCharges) {
return repository.findLoansForPeriodicAccrual(accountingType, tillDate, futureCharges);
public List<Loan> findLoansForPeriodicAccrual(AccountingRuleType accountingType, LocalDate tillDate, boolean futureCharges) {
return repository.findLoansForPeriodicAccrual(accountingType, tillDate, futureCharges, LoanStatus.ACTIVE);
}
public List<Loan> findLoansForAddAccrual(Integer accountingType, LocalDate tillDate, boolean futureCharges) {
return repository.findLoansForAddAccrual(accountingType, tillDate, futureCharges);
public List<Loan> findLoansForAddAccrual(AccountingRuleType accountingType, LocalDate tillDate, boolean futureCharges) {
return repository.findLoansForAddAccrual(accountingType, tillDate, futureCharges, LoanStatus.ACTIVE);
}
public List<Long> findIdByExternalIds(List<ExternalId> externalIds) {
@@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.domain;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter(autoApply = true)
public class LoanStatusConverter implements AttributeConverter<LoanStatus, Integer> {
@Override
public Integer convertToDatabaseColumn(LoanStatus attribute) {
return attribute == null ? null : attribute.getValue();
}
@Override
public LoanStatus convertToEntityAttribute(Integer dbData) {
return LoanStatus.fromInt(dbData);
}
}
@@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.domain;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter(autoApply = true)
public class LoanSubStatusConverter implements AttributeConverter<LoanSubStatus, Integer> {
@Override
public Integer convertToDatabaseColumn(LoanSubStatus attribute) {
return attribute == null ? null : attribute.getValue();
}
@Override
public LoanSubStatus convertToEntityAttribute(Integer dbData) {
return dbData == null ? null : LoanSubStatus.fromInt(dbData);
}
}
@@ -42,7 +42,7 @@ public class LoanSummaryBalancesRepository {
private final CriteriaQueryFactory criteriaQueryFactory;
public Collection<LoanTransactionBalance> retrieveLoanSummaryBalancesByTransactionType(final Long loanId,
final List<Integer> transactionTypes) {
final List<LoanTransactionType> transactionTypes) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<LoanTransactionBalance> query = cb.createQuery(LoanTransactionBalance.class);
@@ -75,7 +75,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
private PaymentDetail paymentDetail;
@Column(name = "transaction_type_enum", nullable = false)
private Integer typeOf;
private LoanTransactionType typeOf;
@Column(name = "transaction_date", nullable = false)
private LocalDate dateOf;
@@ -145,13 +145,12 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction incomePosting(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion,
final ExternalId externalId) {
final Integer typeOf = LoanTransactionType.INCOME_POSTING.getValue();
final BigDecimal principalPortion = BigDecimal.ZERO;
final BigDecimal overPaymentPortion = BigDecimal.ZERO;
final boolean reversed = false;
final PaymentDetail paymentDetail = null;
return new LoanTransaction(loan, office, typeOf, dateOf, amount, principalPortion, interestPortion, feeChargesPortion,
penaltyChargesPortion, overPaymentPortion, reversed, paymentDetail, externalId);
return new LoanTransaction(loan, office, LoanTransactionType.INCOME_POSTING, dateOf, amount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overPaymentPortion, reversed, paymentDetail, externalId);
}
public static LoanTransaction disbursement(final Loan loan, final Money amount, final PaymentDetail paymentDetail,
@@ -263,19 +262,19 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction accrueTransaction(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL.getValue(), dateOf, amount, null, interestPortion,
feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL, dateOf, amount, null, interestPortion, feeChargesPortion,
penaltyChargesPortion, null, false, null, externalId);
}
public static LoanTransaction accrualAdjustment(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal interestPortion, final BigDecimal feePortion, final BigDecimal penaltyPortion, final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL_ADJUSTMENT.getValue(), dateOf, amount, null, interestPortion,
feePortion, penaltyPortion, null, false, null, externalId);
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL_ADJUSTMENT, dateOf, amount, null, interestPortion, feePortion,
penaltyPortion, null, false, null, externalId);
}
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate,
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER, transferDate,
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
@@ -283,7 +282,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate,
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER, transferDate,
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
@@ -291,7 +290,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate,
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER, transferDate,
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId);
@@ -326,8 +325,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction creditBalanceRefund(final Loan loan, final Office office, final Money amount, final LocalDate paymentDate,
final ExternalId externalId, PaymentDetail paymentDetail) {
return new LoanTransaction(loan, office, LoanTransactionType.CREDIT_BALANCE_REFUND.getValue(), paymentDate, amount.getAmount(),
null, null, null, null, amount.getAmount(), false, paymentDetail, externalId);
return new LoanTransaction(loan, office, LoanTransactionType.CREDIT_BALANCE_REFUND, paymentDate, amount.getAmount(), null, null,
null, null, amount.getAmount(), false, paymentDetail, externalId);
}
public static LoanTransaction refundForActiveLoan(final Office office, final Money amount, final PaymentDetail paymentDetail,
@@ -339,8 +338,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
public static LoanTransaction interestRefund(final Loan loan, final Office office, final BigDecimal amount, final BigDecimal principal,
final BigDecimal interest, final BigDecimal feeCharges, final BigDecimal penaltyCharges, final PaymentDetail paymentDetail,
final LocalDate refundDate, final ExternalId externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.INTEREST_REFUND.getValue(), refundDate, amount, principal, interest,
feeCharges, penaltyCharges, amount, false, paymentDetail, externalId);
return new LoanTransaction(loan, office, LoanTransactionType.INTEREST_REFUND, refundDate, amount, principal, interest, feeCharges,
penaltyCharges, amount, false, paymentDetail, externalId);
}
public static boolean transactionAmountsMatch(final MonetaryCurrency currency, final LoanTransaction loanTransaction,
@@ -353,10 +352,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
&& loanTransaction.getOverPaymentPortion(currency).isEqualTo(newLoanTransaction.getOverPaymentPortion(currency));
}
public LoanTransaction(final Loan loan, final Office office, final Integer typeOf, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
final BigDecimal penaltyChargesPortion, final BigDecimal overPaymentPortion, final boolean reversed,
final PaymentDetail paymentDetail, final ExternalId externalId) {
public LoanTransaction(final Loan loan, final Office office, final LoanTransactionType typeOf, final LocalDate dateOf,
final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion,
final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion, final BigDecimal overPaymentPortion,
final boolean reversed, final PaymentDetail paymentDetail, final ExternalId externalId) {
this.loan = loan;
this.typeOf = typeOf;
@@ -403,14 +402,14 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
: null;
BigDecimal totalOutstanding = loan.getSummary().getTotalOutstanding();
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_OFF.getValue(), chargeOffDate, totalOutstanding,
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGE_OFF, chargeOffDate, totalOutstanding,
principalPortion, interestPortion, feePortion, penaltyPortion, null, false, null, externalId);
}
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final BigDecimal amount,
final LocalDate date, final ExternalId externalId) {
this.loan = loan;
this.typeOf = type.getValue();
this.typeOf = type;
this.amount = amount;
this.dateOf = date;
this.externalId = externalId;
@@ -421,7 +420,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final PaymentDetail paymentDetail,
final BigDecimal amount, final LocalDate date, final ExternalId externalId) {
this.loan = loan;
this.typeOf = type.getValue();
this.typeOf = type;
this.paymentDetail = paymentDetail;
this.amount = amount;
this.dateOf = date;
@@ -433,7 +432,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final PaymentDetail paymentDetail,
final BigDecimal amount, final LocalDate date, final ExternalId externalId, final String chargeRefundChargeType) {
this.loan = loan;
this.typeOf = type.getValue();
this.typeOf = type;
this.paymentDetail = paymentDetail;
this.amount = amount;
this.dateOf = date;
@@ -552,7 +551,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
}
public LoanTransactionType getTypeOf() {
return LoanTransactionType.fromInt(this.typeOf);
return this.typeOf == null ? LoanTransactionType.INVALID : this.typeOf;
}
public boolean isReversed() {
@@ -49,8 +49,8 @@ public interface LoanTransactionRepository extends JpaRepository<LoanTransaction
lt.loan.loanProduct.delinquencyBucket is not null
GROUP BY lt.loan
""")
Collection<LoanScheduleDelinquencyData> fetchLoanTransactionsByTypeAndLessOrEqualDate(@Param("transactionType") Integer transactionType,
@Param("businessDate") LocalDate businessDate);
Collection<LoanScheduleDelinquencyData> fetchLoanTransactionsByTypeAndLessOrEqualDate(
@Param("transactionType") LoanTransactionType transactionType, @Param("businessDate") LocalDate businessDate);
@Query(FIND_ID_BY_EXTERNAL_ID)
Long findIdByExternalId(@Param("externalId") ExternalId externalId);
@@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.domain;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter(autoApply = true)
public class LoanTransactionTypeConverter implements AttributeConverter<LoanTransactionType, Integer> {
@Override
public Integer convertToDatabaseColumn(LoanTransactionType attribute) {
return attribute == null ? null : attribute.getValue();
}
@Override
public LoanTransactionType convertToEntityAttribute(Integer dbData) {
return dbData == null ? null : LoanTransactionType.fromInt(dbData);
}
}
@@ -62,7 +62,7 @@ public class SetLoanDelinquencyTagsTasklet implements Tasklet {
// Read Loan Ids with Loan Transaction Charge back
Collection<LoanScheduleDelinquencyData> loanScheduleDelinquencyData = this.loanTransactionRepository
.fetchLoanTransactionsByTypeAndLessOrEqualDate(LoanTransactionType.CHARGEBACK.getValue(), businessDate);
.fetchLoanTransactionsByTypeAndLessOrEqualDate(LoanTransactionType.CHARGEBACK, businessDate);
List<Long> processedLoans = applyDelinquencyTagToLoans(loanScheduleDelinquencyData);
log.debug("{}: Records affected by setLoanDelinquencyTags: {}", ThreadLocalContextUtil.getTenant().getName(),
processedLoans.size());
@@ -70,10 +70,10 @@ public class SetLoanDelinquencyTagsTasklet implements Tasklet {
// Read Loan Ids with overdue installments
if (processedLoans.isEmpty()) {
loanScheduleDelinquencyData = this.loanRepaymentScheduleInstallmentRepository
.fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE.getValue(), businessDate, false);
.fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE, businessDate, false);
} else {
loanScheduleDelinquencyData = this.loanRepaymentScheduleInstallmentRepository
.fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE.getValue(), businessDate, false, processedLoans);
.fetchLoanScheduleDataByDueDateAndObligationsMet(LoanStatus.ACTIVE, businessDate, false, processedLoans);
}
applyDelinquencyTagToLoans(loanScheduleDelinquencyData);
@@ -153,7 +153,7 @@ public final class LoanApplicationTransitionValidator {
// validate loan state transition
final LoanStatus statusEnum = defaultLoanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_REJECTED, loan);
if (!statusEnum.hasStateOf(LoanStatus.fromInt(loan.getLoanStatus()))) {
if (!statusEnum.hasStateOf(loan.getLoanStatus())) {
final LocalDate rejectedOn = command.localDateValueOfParameterNamed(Loan.REJECTED_ON_DATE);
if (DateUtils.isBefore(rejectedOn, loan.getSubmittedOnDate())) {
final String errorMessage = "The date on which a loan is rejected cannot be before its submittal date: "
@@ -214,7 +214,7 @@ public final class LoanApplicationTransitionValidator {
// validate loan state transition
final LoanStatus statusEnum = defaultLoanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_WITHDRAWN, loan);
if (!statusEnum.hasStateOf(LoanStatus.fromInt(loan.getLoanStatus()))) {
if (!statusEnum.hasStateOf(loan.getLoanStatus())) {
LocalDate withdrawnOn = command.localDateValueOfParameterNamed(Loan.WITHDRAWN_ON_DATE);
if (withdrawnOn == null) {
withdrawnOn = command.localDateValueOfParameterNamed(Loan.EVENT_DATE);
@@ -32,6 +32,7 @@ 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;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -42,14 +43,15 @@ public class LoanTransactionReadService {
private final EntityManager entityManager;
public List<LoanTransaction> fetchLoanTransactionsByType(final Long loanId, final String externalId, final Integer transactionType) {
final List<Integer> transactionTypes = new ArrayList<>();
public List<LoanTransaction> fetchLoanTransactionsByType(final Long loanId, final String externalId,
final LoanTransactionType transactionType) {
final List<LoanTransactionType> transactionTypes = new ArrayList<>();
transactionTypes.add(transactionType);
return fetchLoanTransactionsByTypes(loanId, externalId, transactionTypes);
}
public List<LoanTransaction> fetchLoanTransactionsByTypes(final Long loanId, final String externalId,
final List<Integer> transactionTypes) {
final List<LoanTransactionType> transactionTypes) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<LoanTransaction> query = cb.createQuery(LoanTransaction.class);
@@ -132,7 +132,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
private LoanProductMinMaxConstraints loanProductMinMaxConstraints;
@Column(name = "accounting_type", nullable = false)
private Integer accountingRule;
private AccountingRuleType accountingRule;
@Column(name = "include_in_borrower_cycle")
private boolean includeInBorrowerCycle;
@@ -767,9 +767,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
defaultMinNominalInterestRatePerPeriod, defaultMaxNominalInterestRatePerPeriod, defaultMinNumberOfInstallments,
defaultMaxNumberOfInstallments);
if (accountingRuleType != null) {
this.accountingRule = accountingRuleType.getValue();
}
this.accountingRule = accountingRuleType;
this.includeInBorrowerCycle = includeInBorrowerCycle;
this.useBorrowerCycle = useBorrowerCycle;
@@ -994,10 +992,11 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
}
final String accountingTypeParamName = "accountingRule";
if (command.isChangeInIntegerParameterNamed(accountingTypeParamName, this.accountingRule)) {
Integer currentValue = this.accountingRule == null ? null : this.accountingRule.getValue();
if (command.isChangeInIntegerParameterNamed(accountingTypeParamName, currentValue)) {
final Integer newValue = command.integerValueOfParameterNamed(accountingTypeParamName);
actualChanges.put(accountingTypeParamName, newValue);
this.accountingRule = newValue;
this.accountingRule = AccountingRuleType.fromInt(newValue);
}
final String nameParamName = "name";
@@ -1414,19 +1413,19 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
}
public boolean isAccountingDisabled() {
return AccountingRuleType.NONE.getValue().equals(this.accountingRule);
return AccountingRuleType.NONE.equals(this.accountingRule);
}
public boolean isCashBasedAccountingEnabled() {
return AccountingRuleType.CASH_BASED.getValue().equals(this.accountingRule);
return AccountingRuleType.CASH_BASED.equals(this.accountingRule);
}
public boolean isUpfrontAccrualAccountingEnabled() {
return AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(this.accountingRule);
return AccountingRuleType.ACCRUAL_UPFRONT.equals(this.accountingRule);
}
public boolean isPeriodicAccrualAccountingEnabled() {
return AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(this.accountingRule);
return AccountingRuleType.ACCRUAL_PERIODIC.equals(this.accountingRule);
}
public Money getPrincipalAmount() {
@@ -357,13 +357,12 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
@Override
@Transactional
public Map<String, Object> updateLoanProductToGLAccountMapping(final Long loanProductId, final JsonCommand command,
final boolean accountingRuleChanged, final int accountingRuleTypeId) {
final boolean accountingRuleChanged, final AccountingRuleType accountingRuleType) {
/***
* Variable tracks all accounting mapping properties that have been updated
***/
Map<String, Object> changes = new HashMap<>();
final JsonElement element = this.fromApiJsonHelper.parse(command.json());
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountingRuleTypeId);
/***
* If the accounting rule has been changed, delete all existing mapping for the product and recreate a new set
@@ -21,6 +21,9 @@ package org.apache.fineract.cob.loan;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.data.LoanCOBParameter;
@@ -30,12 +33,17 @@ import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@RequiredArgsConstructor
public class RetrieveAllNonClosedLoanIdServiceImpl implements RetrieveLoanIdService {
private static final Collection<LoanStatus> NON_CLOSED_LOAN_STATUSES = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE,
LoanStatus.TRANSFER_IN_PROGRESS, LoanStatus.TRANSFER_ON_HOLD));
private final LoanRepository loanRepository;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@@ -69,30 +77,32 @@ public class RetrieveAllNonClosedLoanIdServiceImpl implements RetrieveLoanIdServ
@Override
public List<LoanIdAndLastClosedBusinessDate> retrieveLoanIdsBehindDate(LocalDate businessDate, List<Long> loanIds) {
return loanRepository.findAllNonClosedLoansBehindByLoanIds(businessDate, loanIds);
return loanRepository.findAllLoansBehindByLoanIdsAndStatuses(businessDate, loanIds, NON_CLOSED_LOAN_STATUSES);
}
@Override
public List<LoanIdAndLastClosedBusinessDate> retrieveLoanIdsBehindDateOrNull(LocalDate businessDate, List<Long> loanIds) {
return loanRepository.findAllNonClosedLoansBehindOrNullByLoanIds(businessDate, loanIds);
return loanRepository.findAllLoansBehindOrNullByLoanIdsAndStatuses(businessDate, loanIds, NON_CLOSED_LOAN_STATUSES);
}
@Override
public List<LoanIdAndLastClosedBusinessDate> retrieveLoanIdsOldestCobProcessed(LocalDate businessDate) {
return loanRepository.findOldestCOBProcessedLoan(businessDate);
return loanRepository.findOldestCOBProcessedLoan(businessDate, NON_CLOSED_LOAN_STATUSES);
}
@Override
public List<Long> retrieveAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(LoanCOBParameter loanCOBParameter,
boolean isCatchUp) {
if (isCatchUp) {
return loanRepository.findAllNonClosedLoansByLastClosedBusinessDateNotNullAndMinAndMaxLoanId(loanCOBParameter.getMinLoanId(),
loanCOBParameter.getMaxLoanId(), ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)
.minusDays(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND));
return loanRepository.findAllLoansByLastClosedBusinessDateNotNullAndMinAndMaxLoanIdAndStatuses(
loanCOBParameter.getMinLoanId(), loanCOBParameter.getMaxLoanId(), ThreadLocalContextUtil
.getBusinessDateByType(BusinessDateType.COB_DATE).minusDays(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND),
NON_CLOSED_LOAN_STATUSES);
} else {
return loanRepository.findAllNonClosedLoansByLastClosedBusinessDateAndMinAndMaxLoanId(loanCOBParameter.getMinLoanId(),
loanCOBParameter.getMaxLoanId(), ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)
.minusDays(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND));
return loanRepository.findAllLoansByLastClosedBusinessDateAndMinAndMaxLoanIdAndStatuses(
loanCOBParameter.getMinLoanId(), loanCOBParameter.getMaxLoanId(), ThreadLocalContextUtil
.getBusinessDateByType(BusinessDateType.COB_DATE).minusDays(LoanCOBConstant.NUMBER_OF_DAYS_BEHIND),
NON_CLOSED_LOAN_STATUSES);
}
}
@@ -89,7 +89,7 @@ import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.account.exception.DifferentCurrenciesException;
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
@@ -128,7 +128,7 @@ public class InteropServiceImpl implements InteropService {
private final NoteRepository noteRepository;
private final PaymentTypeRepository paymentTypeRepository;
private final InteropIdentifierRepository identifierRepository;
private final LoanRepository loanRepository;
private final LoanRepositoryWrapper loanRepositoryWrapper;
private final SavingsHelper savingsHelper;
private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper;
@@ -556,7 +556,7 @@ public class InteropServiceImpl implements InteropService {
}
private Loan validateAndGetLoan(String accountId) {
Loan loan = loanRepository.findNonClosedLoanByAccountNumber(accountId);
Loan loan = loanRepositoryWrapper.findNonClosedLoanByAccountNumber(accountId);
if (loan == null) {
throw new LoanNotFoundException(accountId);
}
@@ -28,7 +28,7 @@ import org.apache.fineract.interoperation.service.InteropService;
import org.apache.fineract.interoperation.service.InteropServiceImpl;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository;
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
@@ -49,14 +49,15 @@ public class InteroperationConfiguration {
public InteropService interopService(PlatformSecurityContext securityContext, InteropDataValidator interopDataValidator,
SavingsAccountRepository savingsAccountRepository, SavingsAccountTransactionRepository savingsAccountTransactionRepository,
ApplicationCurrencyRepository applicationCurrencyRepository, NoteRepository noteRepository,
PaymentTypeRepository paymentTypeRepository, InteropIdentifierRepository identifierRepository, LoanRepository loanRepository,
SavingsHelper savingsHelper, SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
PaymentTypeRepository paymentTypeRepository, InteropIdentifierRepository identifierRepository,
LoanRepositoryWrapper loanRepositoryWrapper, SavingsHelper savingsHelper,
SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
SavingsAccountDomainService savingsAccountService, JdbcTemplate jdbcTemplate,
PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer, DatabaseSpecificSQLGenerator sqlGenerator) {
return new InteropServiceImpl(securityContext, interopDataValidator, savingsAccountRepository, savingsAccountTransactionRepository,
applicationCurrencyRepository, noteRepository, paymentTypeRepository, identifierRepository, loanRepository, savingsHelper,
savingsAccountTransactionSummaryWrapper, savingsAccountService, jdbcTemplate, commandsSourceWritePlatformService,
toApiJsonSerializer, sqlGenerator);
applicationCurrencyRepository, noteRepository, paymentTypeRepository, identifierRepository, loanRepositoryWrapper,
savingsHelper, savingsAccountTransactionSummaryWrapper, savingsAccountService, jdbcTemplate,
commandsSourceWritePlatformService, toApiJsonSerializer, sqlGenerator);
}
}
@@ -23,6 +23,7 @@ import java.util.List;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -52,15 +53,16 @@ public interface CalendarInstanceRepository extends JpaRepository<CalendarInstan
Collection<CalendarInstance> findByCalendarIdAndEntityTypeId(Long calendarId, Integer entityTypeId);
/** Should use in clause, can I do it without creating a new class? **/
@Query("select ci from CalendarInstance ci where ci.entityId in (select loan.id from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId and (loan.loanStatus = 100 or loan.loanStatus = 200 or loan.loanStatus = 300)) and ci.entityTypeId = 3")
List<CalendarInstance> findCalendarInstancesForActiveLoansByGroupIdAndClientId(@Param("groupId") Long groupId,
@Param("clientId") Long clientId);
@Query("select ci from CalendarInstance ci where ci.entityId in (select loan.id from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId and loan.loanStatus in :loanStatuses) and ci.entityTypeId = 3")
List<CalendarInstance> findCalendarInstancesForLoansByGroupIdAndClientIdAndStatuses(@Param("groupId") Long groupId,
@Param("clientId") Long clientId, @Param("loanStatuses") Collection<LoanStatus> loanStatuses);
/**
* EntityType = 3 is for loan
*/
@Query("SELECT COUNT(ci.id) FROM CalendarInstance ci, Loan loan WHERE loan.id = ci.entityId AND ci.entityTypeId = 3 AND ci.calendar.id = :calendarId AND loan.loanStatus IN :loanStatuses ")
Integer countOfLoansSyncedWithCalendar(@Param("calendarId") Long calendarId, @Param("loanStatuses") Collection<Integer> loanStatuses);
Integer countOfLoansSyncedWithCalendar(@Param("calendarId") Long calendarId,
@Param("loanStatuses") Collection<LoanStatus> loanStatuses);
}
@@ -196,8 +196,8 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
Boolean areActiveEntitiesSynced = false;
final Long calendarId = command.entityId();
final Collection<Integer> loanStatuses = new ArrayList<>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
final Collection<LoanStatus> loanStatuses = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE));
final Integer numberOfActiveLoansSyncedWithThisCalendar = this.calendarInstanceRepository.countOfLoansSyncedWithCalendar(calendarId,
loanStatuses);
@@ -1065,7 +1065,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private void handleForeClosureTransactions(final Loan loan, final LoanTransaction repaymentTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
loan.setLoanSubStatus(LoanSubStatus.FORECLOSED.getValue());
loan.setLoanSubStatus(LoanSubStatus.FORECLOSED);
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
null, scheduleGeneratorDTO);
}
@@ -27,6 +27,8 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccrualActivityRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
@@ -46,7 +48,8 @@ public class AccrualActivityPostingTasklet implements Tasklet {
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
final LocalDate yesterday = DateUtils.getBusinessLocalDate().minusDays(1);
List<Throwable> errors = new ArrayList<>();
Set<Long> loanAccounts = loanAccrualActivityRepository.fetchLoanIdsForAccrualActivityPosting(yesterday);
Set<Long> loanAccounts = loanAccrualActivityRepository.fetchLoanIdsForAccrualActivityPosting(yesterday,
LoanTransactionType.ACCRUAL_ACTIVITY, LoanStatus.ACTIVE);
for (Long accountId : loanAccounts) {
try {
loanAccrualActivityProcessingService.makeAccrualActivityTransaction(accountId, yesterday);
@@ -68,8 +68,8 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
return RepeatStatus.FINISHED;
}
final Collection<Integer> loanStatuses = new ArrayList<>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
final Collection<LoanStatus> loanStatuses = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE));
final List<Holiday> holidays = holidayRepository.findUnprocessed();
for (final Holiday holiday : holidays) {
@@ -210,7 +210,7 @@ public final class LoanApplicationValidator {
expectedFirstRepaymentOnDate);
}
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType(),
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
loan.repaymentScheduleDetail().getNumberOfRepayments(), loan.repaymentScheduleDetail().getRepayEvery(),
loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
}
@@ -224,7 +224,7 @@ public final class LoanApplicationValidator {
expectedFirstRepaymentOnDate);
}
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType(),
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
loan.repaymentScheduleDetail().getNumberOfRepayments(), loan.repaymentScheduleDetail().getRepayEvery(),
loan.repaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
}
@@ -1850,11 +1850,9 @@ public final class LoanApplicationValidator {
final Long clientId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.clientIdParameterName, element);
if (groupId != null) {
activeLoansLoanProductIds = this.loanRepositoryWrapper.findActiveLoansLoanProductIdsByGroup(groupId,
LoanStatus.ACTIVE.getValue());
activeLoansLoanProductIds = this.loanRepositoryWrapper.findActiveLoansLoanProductIdsByGroup(groupId, LoanStatus.ACTIVE);
} else {
activeLoansLoanProductIds = this.loanRepositoryWrapper.findActiveLoansLoanProductIdsByClient(clientId,
LoanStatus.ACTIVE.getValue());
activeLoansLoanProductIds = this.loanRepositoryWrapper.findActiveLoansLoanProductIdsByClient(clientId, LoanStatus.ACTIVE);
}
checkForProductMixRestrictions(activeLoansLoanProductIds, productId);
}
@@ -56,40 +56,36 @@ public abstract class CommonLoanSummaryDataProvider implements LoanSummaryDataPr
BigDecimal totalUnpaidPayableDueInterest = BigDecimal.ZERO;
BigDecimal totalUnpaidPayableNotDueInterest = BigDecimal.ZERO;
totalChargeAdjustment = fetchLoanTransactionBalanceByType(loanTransactionBalances,
LoanTransactionType.CHARGE_ADJUSTMENT.getValue());
totalChargeAdjustment = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.CHARGE_ADJUSTMENT);
totalChargeAdjustmentReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances,
LoanTransactionType.CHARGE_ADJUSTMENT.getValue());
LoanTransactionType.CHARGE_ADJUSTMENT);
totalChargeback = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.CHARGEBACK.getValue());
totalChargeback = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.CHARGEBACK);
totalCreditBalanceRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances,
LoanTransactionType.CREDIT_BALANCE_REFUND.getValue());
totalCreditBalanceRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.CREDIT_BALANCE_REFUND);
totalCreditBalanceRefundReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances,
LoanTransactionType.CREDIT_BALANCE_REFUND.getValue());
LoanTransactionType.CREDIT_BALANCE_REFUND);
totalGoodwillCredit = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.GOODWILL_CREDIT.getValue());
totalGoodwillCredit = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.GOODWILL_CREDIT);
totalGoodwillCreditReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances,
LoanTransactionType.GOODWILL_CREDIT.getValue());
LoanTransactionType.GOODWILL_CREDIT);
totalInterestRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.INTEREST_REFUND.getValue());
totalInterestRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.INTEREST_REFUND);
totalInterestPaymentWaiver = fetchLoanTransactionBalanceByType(loanTransactionBalances,
LoanTransactionType.INTEREST_PAYMENT_WAIVER.getValue());
LoanTransactionType.INTEREST_PAYMENT_WAIVER);
totalMerchantRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances,
LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue());
totalMerchantRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.MERCHANT_ISSUED_REFUND);
totalMerchantRefundReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances,
LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue());
LoanTransactionType.MERCHANT_ISSUED_REFUND);
totalPayoutRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.PAYOUT_REFUND.getValue());
totalPayoutRefundReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances,
LoanTransactionType.PAYOUT_REFUND.getValue());
totalPayoutRefund = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.PAYOUT_REFUND);
totalPayoutRefundReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances, LoanTransactionType.PAYOUT_REFUND);
totalRepaymentTransaction = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.REPAYMENT.getValue())
.add(fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.DOWN_PAYMENT.getValue()));
totalRepaymentTransaction = fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.REPAYMENT)
.add(fetchLoanTransactionBalanceByType(loanTransactionBalances, LoanTransactionType.DOWN_PAYMENT));
totalRepaymentTransactionReversed = fetchLoanTransactionBalanceReversedByType(loanTransactionBalances,
LoanTransactionType.REPAYMENT.getValue());
LoanTransactionType.REPAYMENT);
if (repaymentSchedule != null) {
// Outstanding Interest on Past due installments
@@ -142,14 +138,14 @@ public abstract class CommonLoanSummaryDataProvider implements LoanSummaryDataPr
}
private static BigDecimal fetchLoanTransactionBalanceByType(final Collection<LoanTransactionBalance> loanTransactionBalances,
final Integer transactionType) {
final LoanTransactionType transactionType) {
final Optional<LoanTransactionBalance> optLoanTransactionBalance = loanTransactionBalances.stream()
.filter(balance -> balance.getTransactionType().equals(transactionType) && !balance.isReversed()).findFirst();
return optLoanTransactionBalance.isPresent() ? optLoanTransactionBalance.get().getAmount() : BigDecimal.ZERO;
}
private static BigDecimal fetchLoanTransactionBalanceReversedByType(final Collection<LoanTransactionBalance> loanTransactionBalances,
final Integer transactionType) {
final LoanTransactionType transactionType) {
final Optional<LoanTransactionBalance> optLoanTransactionBalance = loanTransactionBalances.stream()
.filter(balance -> balance.getTransactionType().equals(transactionType) && balance.isReversed()
&& balance.isManuallyAdjustedOrReversed())
@@ -147,8 +147,8 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi
if (MathUtil.isGreaterThanZero(feeChargesPortion) || MathUtil.isGreaterThanZero(penaltyChargesPortion)
|| MathUtil.isGreaterThanZero(interestPortion)) {
BigDecimal transactionAmount = MathUtil.add(feeChargesPortion, penaltyChargesPortion, interestPortion);
LoanTransaction newActivity = new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(),
closureDate, transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null,
LoanTransaction newActivity = new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY, closureDate,
transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null,
externalIdFactory.create());
makeAccrualActivityTransaction(loan, newActivity);
}
@@ -106,7 +106,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
@Override
@Transactional
public void addPeriodicAccruals(@NotNull LocalDate tillDate) throws JobExecutionException {
List<Loan> loans = loanRepositoryWrapper.findLoansForPeriodicAccrual(AccountingRuleType.ACCRUAL_PERIODIC.getValue(), tillDate,
List<Loan> loans = loanRepositoryWrapper.findLoansForPeriodicAccrual(AccountingRuleType.ACCRUAL_PERIODIC, tillDate,
!isChargeOnDueDate());
List<Throwable> errors = new ArrayList<>();
for (Loan loan : loans) {
@@ -141,7 +141,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
@Override
@Transactional
public void addAccruals(@NotNull LocalDate tillDate) throws JobExecutionException {
List<Loan> loans = loanRepositoryWrapper.findLoansForAddAccrual(AccountingRuleType.ACCRUAL_PERIODIC.getValue(), tillDate,
List<Loan> loans = loanRepositoryWrapper.findLoansForAddAccrual(AccountingRuleType.ACCRUAL_PERIODIC, tillDate,
!isChargeOnDueDate());
List<Throwable> errors = new ArrayList<>();
for (Loan loan : loans) {
@@ -61,6 +61,7 @@ import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.collateralmanagement.service.LoanCollateralAssembler;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.fund.domain.Fund;
import org.apache.fineract.portfolio.fund.domain.FundRepository;
import org.apache.fineract.portfolio.fund.exception.FundNotFoundException;
@@ -720,10 +721,10 @@ public class LoanAssemblerImpl implements LoanAssembler {
}
if (command.isChangeInIntegerParameterNamed(LoanApiConstants.loanTermFrequencyTypeParameterName,
loan.getTermPeriodFrequencyType())) {
loan.getTermPeriodFrequencyType().getValue())) {
final Integer newValue = command.integerValueOfParameterNamed(LoanApiConstants.loanTermFrequencyTypeParameterName);
changes.put(LoanApiConstants.loanTermFrequencyTypeParameterName, newValue);
loan.setTermPeriodFrequencyType(newValue);
loan.setTermPeriodFrequencyType(PeriodFrequencyType.fromInt(newValue));
}
if (command.isChangeInBigDecimalParameterNamed(LoanApiConstants.principalParameterName, loan.getApprovedPrincipal())) {
@@ -78,8 +78,8 @@ public class LoanTransactionAssembler {
BigDecimal penaltyChargesPortion = installment.getPenaltyCharges();
BigDecimal transactionAmount = MathUtil.add(interestPortion, feeChargesPortion, penaltyChargesPortion);
return MathUtil.isGreaterThanZero(transactionAmount)
? new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), transactionDate,
transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId)
? new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY, transactionDate, transactionAmount,
null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId)
: null;
}
}
@@ -1003,7 +1003,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.setClosedOnDate(transactionDate);
loan.setActualMaturityDate(transactionDate);
loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, loan);
} else if (LoanStatus.fromInt(loan.getLoanStatus()).isOverpaid()) {
} else if (loan.getLoanStatus().isOverpaid()) {
if (loan.getTotalOverpaid() == null || BigDecimal.ZERO.compareTo(loan.getTotalOverpaid()) == 0) {
loan.setOverpaidOnDate(null);
}
@@ -1306,8 +1306,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.orElseThrow(() -> new LoanTransactionNotFoundException(command.entityId(), command.getLoanId()));
Loan loan = this.loanAssembler.assembleFrom(loanId);
if (loan.getStatus().isClosed() && loan.getLoanSubStatus() != null
&& loan.getLoanSubStatus().equals(LoanSubStatus.FORECLOSED.getValue())) {
if (loan.getStatus().isClosed() && loan.getLoanSubStatus() != null && loan.getLoanSubStatus().equals(LoanSubStatus.FORECLOSED)) {
final String defaultUserMessage = "The loan cannot reopened as it is foreclosed.";
throw new LoanForeclosureException("loan.cannot.be.reopened.as.it.is.foreclosured", defaultUserMessage, loanId);
}
@@ -2067,9 +2066,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
final Collection<Integer> loanStatuses = new ArrayList<>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
final Collection<Integer> loanTypes = new ArrayList<>(Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue()));
final Collection<LoanStatus> loanStatuses = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE));
final Collection<AccountType> loanTypes = new ArrayList<>(Arrays.asList(AccountType.GROUP, AccountType.JLG));
final Collection<Long> loanIds = new ArrayList<>(loanCalendarInstances.size());
// loop through loanCalendarInstances to get loan ids
for (final CalendarInstance calendarInstance : loanCalendarInstances) {
@@ -2134,10 +2133,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.isGroupLoan()) {
if (loan.loanProduct().isIncludeInBorrowerCycle()) {
loansToUpdate = this.loanRepositoryWrapper.getGroupLoansToUpdateLoanCounter(loan.getCurrentLoanCounter(), loan.getGroupId(),
AccountType.GROUP.getValue());
AccountType.GROUP);
} else {
loansToUpdate = this.loanRepositoryWrapper.getGroupLoansToUpdateLoanProductCounter(loan.getLoanProductLoanCounter(),
loan.getGroupId(), AccountType.GROUP.getValue());
loan.getGroupId(), AccountType.GROUP);
}
} else {
@@ -2162,7 +2161,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (loan.isGroupLoan()) {
final List<Loan> loansToUpdateForLoanCounter = this.loanRepositoryWrapper.getGroupLoansDisbursedAfter(actualDisbursementDate,
loan.getGroupId(), AccountType.GROUP.getValue());
loan.getGroupId(), AccountType.GROUP);
final Integer newLoanCounter = getNewGroupLoanCounter(loan);
final Integer newLoanProductCounter = getNewGroupLoanProductCounter(loan);
updateLoanCounter(loan, loansToUpdateForLoanCounter, newLoanCounter, newLoanProductCounter);
@@ -2177,7 +2176,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private Integer getNewGroupLoanCounter(final Loan loan) {
Integer maxClientLoanCounter = this.loanRepositoryWrapper.getMaxGroupLoanCounter(loan.getGroupId(), AccountType.GROUP.getValue());
Integer maxClientLoanCounter = this.loanRepositoryWrapper.getMaxGroupLoanCounter(loan.getGroupId(), AccountType.GROUP);
if (maxClientLoanCounter == null) {
maxClientLoanCounter = 1;
} else {
@@ -2189,7 +2188,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private Integer getNewGroupLoanProductCounter(final Loan loan) {
Integer maxLoanProductLoanCounter = this.loanRepositoryWrapper.getMaxGroupLoanProductCounter(loan.loanProduct().getId(),
loan.getGroupId(), AccountType.GROUP.getValue());
loan.getGroupId(), AccountType.GROUP);
if (maxLoanProductLoanCounter == null) {
maxLoanProductLoanCounter = 1;
} else {
@@ -63,18 +63,16 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
private static void simulateRepaymentForDisbursements(LoanTransaction lt, final AtomicReference<BigDecimal> refundFinal,
List<LoanTransaction> collect) {
collect.add(new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), lt.getTypeOf().getValue(), lt.getDateOf(), lt.getAmount(),
collect.add(new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), lt.getTypeOf(), lt.getDateOf(), lt.getAmount(),
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
if (lt.getTypeOf().isDisbursement() && MathUtil.isGreaterThanZero(refundFinal.get())) {
if (lt.getAmount().compareTo(refundFinal.get()) <= 0) {
collect.add(
new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT.getValue(), lt.getDateOf(), lt.getAmount(),
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
collect.add(new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT, lt.getDateOf(), lt.getAmount(),
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
refundFinal.set(refundFinal.get().subtract(lt.getAmount()));
} else {
collect.add(
new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT.getValue(), lt.getDateOf(), refundFinal.get(),
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
collect.add(new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT, lt.getDateOf(), refundFinal.get(),
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
refundFinal.set(BigDecimal.ZERO);
}
}
@@ -176,8 +176,8 @@ public class LoanReAgingServiceImpl {
Money txPrincipal = loan.getTotalPrincipalOutstandingUntil(transactionDate);
BigDecimal txPrincipalAmount = txPrincipal.getAmount();
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAGE.getValue(), transactionDate, txPrincipalAmount,
txPrincipalAmount, ZERO, ZERO, ZERO, null, false, null, txExternalId);
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAGE, transactionDate, txPrincipalAmount, txPrincipalAmount,
ZERO, ZERO, ZERO, null, false, null, txExternalId);
}
private LoanReAgeParameter createReAgeParameter(LoanTransaction reAgeTransaction, JsonCommand command) {
@@ -156,7 +156,7 @@ public class LoanReAmortizationServiceImpl {
Money txPrincipal = loan.getTotalPrincipalOutstandingUntil(transactionDate);
BigDecimal txPrincipalAmount = txPrincipal.getAmount();
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAMORTIZE.getValue(), transactionDate, txPrincipalAmount,
return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.REAMORTIZE, transactionDate, txPrincipalAmount,
txPrincipalAmount, ZERO, ZERO, ZERO, null, false, null, txExternalId);
}
}
@@ -1704,8 +1704,8 @@ public final class LoanProductDataValidator {
}
if (actualValue) {
Integer ruleType = accountingRuleType;
if (ruleType == null) {
ruleType = loanProduct.getAccountingRule();
if (ruleType == null && loanProduct.getAccountingRule() != null) {
ruleType = loanProduct.getAccountingRule().getValue();
}
if (!AccountingValidations.isAccrualBasedAccounting(ruleType)) {
baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_ACCRUAL_ACTIVITY_POSTING)
@@ -22,6 +22,8 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -52,6 +54,7 @@ import org.apache.fineract.portfolio.group.exception.ClientNotInGroupException;
import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.service.LoanOfficerService;
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
@@ -151,8 +154,10 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
final CalendarInstance sourceGroupCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId(
sourceGroup.getId(), CalendarEntityType.GROUPS.getValue(), CalendarType.COLLECTION.getValue());
// get all customer loans synced with this group calendar Instance
final Collection<LoanStatus> activeLoanStatuses = new ArrayList<>(
Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, LoanStatus.APPROVED, LoanStatus.ACTIVE));
final List<CalendarInstance> activeLoanCalendarInstances = this.calendarInstanceRepository
.findCalendarInstancesForActiveLoansByGroupIdAndClientId(sourceGroup.getId(), client.getId());
.findCalendarInstancesForLoansByGroupIdAndClientIdAndStatuses(sourceGroup.getId(), client.getId(), activeLoanStatuses);
/**
* if a calendar is present in the source group along with loans synced with it, we should ensure that the
@@ -337,7 +337,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
delinquencyEffectivePauseHelper, configurationDomainService);
Loan loan = Mockito.spy(Loan.class);
ReflectionTestUtils.setField(loan, "loanStatus", 300);
ReflectionTestUtils.setField(loan, "loanStatus", LoanStatus.ACTIVE);
LoanTransaction transaction1 = Mockito.mock(LoanTransaction.class);
LoanTransaction transaction2 = Mockito.mock(LoanTransaction.class);
CollectionData collectionData = Mockito.mock(CollectionData.class);
@@ -154,7 +154,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, LoanTransactionType.CHARGEBACK.getValue()))
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, LoanTransactionType.CHARGEBACK))
.thenReturn(Collections.emptyList());
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency);
@@ -223,7 +223,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, LoanTransactionType.CHARGEBACK.getValue()))
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, LoanTransactionType.CHARGEBACK))
.thenReturn(Collections.emptyList());
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
when(loan.getCurrency()).thenReturn(currency);
@@ -279,7 +279,7 @@ public class LoanDelinquencyDomainServiceTest {
when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
when(loan.getCurrency()).thenReturn(currency);
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, LoanTransactionType.CHARGEBACK.getValue()))
when(loanTransactionReadService.fetchLoanTransactionsByType(loan.getId(), null, LoanTransactionType.CHARGEBACK))
.thenReturn(Arrays.asList(loanTransaction));
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList, businessDate)).thenReturn(0L);
@@ -142,7 +142,6 @@ class DefaultLoanLifecycleStateMachineTest {
Loan loan = Mockito.mock(Loan.class);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
Mockito.when(loan.getCurrency()).thenReturn(currency);
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
Mockito.when(loan.getSummary()).thenReturn(loanSummary);
@@ -150,7 +149,7 @@ class DefaultLoanLifecycleStateMachineTest {
// when
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
// then
verify(loan, Mockito.times(1)).setLoanStatus(LoanStatus.ACTIVE.getValue());
verify(loan, Mockito.times(1)).setLoanStatus(LoanStatus.ACTIVE);
verify(businessEventNotifierService).notifyPostBusinessEvent(any(LoanStatusChangedBusinessEvent.class));
}
@@ -162,7 +161,6 @@ class DefaultLoanLifecycleStateMachineTest {
Loan loan = Mockito.mock(Loan.class);
LoanSummary loanSummary = Mockito.mock(LoanSummary.class);
Mockito.when(loan.getCurrency()).thenReturn(currency);
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(zero);
Mockito.when(loan.getSummary()).thenReturn(loanSummary);
@@ -170,7 +168,7 @@ class DefaultLoanLifecycleStateMachineTest {
// when
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
// then
verify(loan, Mockito.times(1)).setLoanStatus(LoanStatus.CLOSED_OBLIGATIONS_MET.getValue());
verify(loan, Mockito.times(1)).setLoanStatus(LoanStatus.CLOSED_OBLIGATIONS_MET);
verify(businessEventNotifierService).notifyPostBusinessEvent(any(LoanStatusChangedBusinessEvent.class));
}
@@ -179,13 +177,12 @@ class DefaultLoanLifecycleStateMachineTest {
// given
Money overpayment = Money.of(new MonetaryCurrency("USD", 2, null), BigDecimal.TEN);
Loan loan = Mockito.mock(Loan.class);
Mockito.when(loan.getPlainStatus()).thenReturn(LoanStatus.OVERPAID.getValue());
Mockito.when(loan.getStatus()).thenReturn(LoanStatus.OVERPAID);
Mockito.when(loan.getTotalOverpaidAsMoney()).thenReturn(overpayment);
// when
underTest.transition(LoanEvent.LOAN_DISBURSED, loan);
// then
verify(loan, Mockito.never()).setLoanStatus(LoanStatus.ACTIVE.getValue());
verify(loan, Mockito.never()).setLoanStatus(LoanStatus.ACTIVE);
verify(businessEventNotifierService, Mockito.never()).notifyPostBusinessEvent(any(LoanStatusChangedBusinessEvent.class));
}
@@ -422,9 +419,7 @@ class DefaultLoanLifecycleStateMachineTest {
private Loan createLoanWithStatus(LoanStatus status) {
Loan result = new Loan();
if (status != null) {
result.setLoanStatus(status.getValue());
}
result.setLoanStatus(status);
return result;
}
}
@@ -208,40 +208,40 @@ public class LoanTest {
LoanTransaction lt1 = new LoanTransaction();
ReflectionTestUtils.setField(lt1, "id", 1L);
ReflectionTestUtils.setField(lt1, "typeOf", LoanTransactionType.DISBURSEMENT.getValue());
ReflectionTestUtils.setField(lt1, "typeOf", LoanTransactionType.DISBURSEMENT);
ReflectionTestUtils.setField(lt1, "dateOf", today);
ReflectionTestUtils.setField(lt1, "createdDate", now);
LoanTransaction lt2 = new LoanTransaction();
ReflectionTestUtils.setField(lt2, "id", 2L);
ReflectionTestUtils.setField(lt2, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER.getValue());
ReflectionTestUtils.setField(lt2, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER);
ReflectionTestUtils.setField(lt2, "dateOf", today);
ReflectionTestUtils.setField(lt2, "createdDate", now);
LoanTransaction lt3 = new LoanTransaction();
ReflectionTestUtils.setField(lt3, "id", 3L);
ReflectionTestUtils.setField(lt3, "typeOf", LoanTransactionType.REPAYMENT.getValue());
ReflectionTestUtils.setField(lt3, "typeOf", LoanTransactionType.REPAYMENT);
ReflectionTestUtils.setField(lt3, "dateOf", today);
ReflectionTestUtils.setField(lt3, "createdDate", now);
ReflectionTestUtils.setField(lt3, "reversed", true);
LoanTransaction lt4 = new LoanTransaction();
ReflectionTestUtils.setField(lt4, "id", 4L);
ReflectionTestUtils.setField(lt4, "typeOf", LoanTransactionType.WAIVE_INTEREST.getValue());
ReflectionTestUtils.setField(lt4, "typeOf", LoanTransactionType.WAIVE_INTEREST);
ReflectionTestUtils.setField(lt4, "dateOf", today);
ReflectionTestUtils.setField(lt4, "createdDate", now);
LoanTransaction lt5 = new LoanTransaction();
ReflectionTestUtils.setField(lt5, "id", 5L);
ReflectionTestUtils.setField(lt5, "typeOf", LoanTransactionType.INCOME_POSTING.getValue());
ReflectionTestUtils.setField(lt5, "typeOf", LoanTransactionType.INCOME_POSTING);
ReflectionTestUtils.setField(lt5, "dateOf", today);
ReflectionTestUtils.setField(lt5, "createdDate", now);
LoanTransaction lt6 = new LoanTransaction();
ReflectionTestUtils.setField(lt6, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER.getValue());
ReflectionTestUtils.setField(lt6, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER);
ReflectionTestUtils.setField(lt6, "dateOf", today.minusDays(2));
LoanTransaction lt7 = new LoanTransaction();
ReflectionTestUtils.setField(lt7, "id", 7L);
ReflectionTestUtils.setField(lt7, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER.getValue());
ReflectionTestUtils.setField(lt7, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER);
ReflectionTestUtils.setField(lt7, "dateOf", today.minusDays(2));
LoanTransaction lt8 = new LoanTransaction();
ReflectionTestUtils.setField(lt8, "id", 8L);
ReflectionTestUtils.setField(lt8, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER.getValue());
ReflectionTestUtils.setField(lt8, "typeOf", LoanTransactionType.WITHDRAW_TRANSFER);
ReflectionTestUtils.setField(lt8, "dateOf", today.minusDays(2));
ReflectionTestUtils.setField(lt8, "createdDate", now.minusMinutes(2));