FINERACT-2080: Extract loanLifecycleStateMachine from Loan entity, refactor transition functionality
This commit is contained in:
committed by
Adam Saghy
parent
79c006774e
commit
64dddfca48
+112
-5
@@ -19,21 +19,21 @@
|
|||||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
// TODO: introduce tests for the state machine
|
// TODO: introduce tests for the state machine
|
||||||
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachine {
|
public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachine {
|
||||||
|
|
||||||
private static Logger LOG = LoggerFactory.getLogger(DefaultLoanLifecycleStateMachine.class);
|
|
||||||
|
|
||||||
private static final List<LoanStatus> ALLOWED_LOAN_STATUSES = List.of(LoanStatus.values());
|
private static final List<LoanStatus> ALLOWED_LOAN_STATUSES = List.of(LoanStatus.values());
|
||||||
private final BusinessEventNotifierService businessEventNotifierService;
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
|
|
||||||
@@ -46,7 +46,29 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
|||||||
@Override
|
@Override
|
||||||
public void transition(final LoanEvent loanEvent, final Loan loan) {
|
public void transition(final LoanEvent loanEvent, final Loan loan) {
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
internalTransition(loanEvent, loan);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void determineAndTransition(final Loan loan, final LocalDate transactionDate) {
|
||||||
|
if (loan.getStatus() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
|
final LoanStatusTransition transition = determineTransition(loan, loan.getStatus(), transactionDate);
|
||||||
|
|
||||||
|
if (MathUtil.isEmpty(loan.getTotalOverpaid())) {
|
||||||
|
loan.setOverpaidOnDate(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transition.transitionNeeded()) {
|
||||||
|
internalTransition(transition.event(), loan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalTransition(final LoanEvent loanEvent, final Loan loan) {
|
||||||
LoanStatus oldStatus = loan.getStatus();
|
LoanStatus oldStatus = loan.getStatus();
|
||||||
LoanStatus newStatus = getNextStatus(loanEvent, loan);
|
LoanStatus newStatus = getNextStatus(loanEvent, loan);
|
||||||
if (newStatus != null) {
|
if (newStatus != null) {
|
||||||
@@ -58,7 +80,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set mandatory field states based on new status after the transition
|
// set mandatory field states based on new status after the transition
|
||||||
LOG.debug("Transitioning loan {} status from {} to {}", loan.getId(), oldStatus, newStatus);
|
log.debug("Transitioning loan {} status from {} to {}", loan.getId(), oldStatus, newStatus);
|
||||||
switch (newStatus) {
|
switch (newStatus) {
|
||||||
case SUBMITTED_AND_PENDING_APPROVAL -> {
|
case SUBMITTED_AND_PENDING_APPROVAL -> {
|
||||||
loan.setApprovedOnDate(null);
|
loan.setApprovedOnDate(null);
|
||||||
@@ -276,4 +298,89 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
|||||||
|
|
||||||
return allowed;
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoanStatusTransition determineTransition(final Loan loan, final LoanStatus currentStatus, final LocalDate transactionDate) {
|
||||||
|
final boolean hasOutstanding = loan.getSummary().getTotalOutstanding(loan.getCurrency()).isGreaterThanZero();
|
||||||
|
final boolean isRepaidInFull = loan.getSummary().isRepaidInFull(loan.getCurrency());
|
||||||
|
final boolean isOverpaid = MathUtil.isGreaterThanZero(loan.getTotalOverpaid());
|
||||||
|
final boolean isAllChargesPaid = loan.getLoanCharges().stream().allMatch(
|
||||||
|
charge -> !charge.isActive() || charge.amount().compareTo(BigDecimal.ZERO) <= 0 || charge.isPaid() || charge.isWaived());
|
||||||
|
|
||||||
|
if (currentStatus.isOverpaid()) {
|
||||||
|
return determineTransitionFromOverpaid(loan, isOverpaid, isRepaidInFull, isAllChargesPaid, hasOutstanding, transactionDate);
|
||||||
|
} else if (currentStatus.isClosedObligationsMet()) {
|
||||||
|
return determineTransitionFromClosedObligationsMet(loan, isOverpaid, hasOutstanding, transactionDate);
|
||||||
|
} else if (currentStatus.isActive()) {
|
||||||
|
return determineTransitionFromActive(loan, isOverpaid, isRepaidInFull, isAllChargesPaid, transactionDate);
|
||||||
|
} else if (currentStatus.isClosedWrittenOff() || currentStatus.isClosedWithOutsandingAmountMarkedForReschedule()) {
|
||||||
|
return determineTransitionFromClosedWrittenOffOrRescheduled(loan, hasOutstanding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoanStatusTransition.noTransition(currentStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanStatusTransition determineTransitionFromOverpaid(final Loan loan, final boolean isOverpaid, final boolean isRepaidInFull,
|
||||||
|
final boolean isAllChargesPaid, final boolean hasOutstanding, final LocalDate transactionDate) {
|
||||||
|
if (!isOverpaid) {
|
||||||
|
if (isRepaidInFull && isAllChargesPaid) {
|
||||||
|
loan.setClosedOnDate(transactionDate);
|
||||||
|
loan.setActualMaturityDate(transactionDate);
|
||||||
|
return LoanStatusTransition.to(LoanStatus.CLOSED_OBLIGATIONS_MET, LoanEvent.LOAN_CREDIT_BALANCE_REFUND);
|
||||||
|
} else if (hasOutstanding) {
|
||||||
|
loan.handleMaturityDateActivate();
|
||||||
|
return LoanStatusTransition.to(LoanStatus.ACTIVE, LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LoanStatusTransition.noTransition(LoanStatus.OVERPAID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanStatusTransition determineTransitionFromClosedObligationsMet(final Loan loan, final boolean isOverpaid,
|
||||||
|
final boolean hasOutstanding, LocalDate transactionDate) {
|
||||||
|
if (isOverpaid) {
|
||||||
|
loan.setOverpaidOnDate(transactionDate);
|
||||||
|
loan.setClosedOnDate(null);
|
||||||
|
loan.setActualMaturityDate(null);
|
||||||
|
return LoanStatusTransition.to(LoanStatus.OVERPAID, LoanEvent.LOAN_OVERPAYMENT);
|
||||||
|
} else if (hasOutstanding) {
|
||||||
|
loan.setClosedOnDate(null);
|
||||||
|
loan.setActualMaturityDate(null);
|
||||||
|
loan.handleMaturityDateActivate();
|
||||||
|
return LoanStatusTransition.to(LoanStatus.ACTIVE, LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||||
|
}
|
||||||
|
return LoanStatusTransition.noTransition(LoanStatus.CLOSED_OBLIGATIONS_MET);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanStatusTransition determineTransitionFromActive(final Loan loan, final boolean isOverpaid, final boolean isRepaidInFull,
|
||||||
|
final boolean isAllChargesPaid, final LocalDate transactionDate) {
|
||||||
|
if (isOverpaid) {
|
||||||
|
loan.setOverpaidOnDate(transactionDate);
|
||||||
|
loan.setActualMaturityDate(null);
|
||||||
|
return LoanStatusTransition.to(LoanStatus.OVERPAID, LoanEvent.LOAN_OVERPAYMENT);
|
||||||
|
} else if (isRepaidInFull && isAllChargesPaid) {
|
||||||
|
loan.setClosedOnDate(transactionDate);
|
||||||
|
loan.setActualMaturityDate(transactionDate);
|
||||||
|
return LoanStatusTransition.to(LoanStatus.CLOSED_OBLIGATIONS_MET, LoanEvent.REPAID_IN_FULL);
|
||||||
|
}
|
||||||
|
return LoanStatusTransition.noTransition(LoanStatus.ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoanStatusTransition determineTransitionFromClosedWrittenOffOrRescheduled(final Loan loan, final boolean hasOutstanding) {
|
||||||
|
if (hasOutstanding) {
|
||||||
|
return LoanStatusTransition.to(LoanStatus.ACTIVE, LoanEvent.LOAN_ADJUST_TRANSACTION);
|
||||||
|
}
|
||||||
|
return LoanStatusTransition.noTransition(loan.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
private record LoanStatusTransition(LoanStatus targetStatus, LoanEvent event, boolean transitionNeeded) {
|
||||||
|
|
||||||
|
public static LoanStatusTransition to(final LoanStatus targetStatus, final LoanEvent event) {
|
||||||
|
return new LoanStatusTransition(targetStatus, event, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LoanStatusTransition noTransition(final LoanStatus currentStatus) {
|
||||||
|
return new LoanStatusTransition(currentStatus, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-150
@@ -45,10 +45,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -82,7 +80,6 @@ import org.apache.fineract.portfolio.collateral.domain.LoanCollateral;
|
|||||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||||
import org.apache.fineract.portfolio.group.domain.Group;
|
import org.apache.fineract.portfolio.group.domain.Group;
|
||||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||||
@@ -90,7 +87,6 @@ import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransact
|
|||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
|
||||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
|
||||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||||
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
@@ -298,6 +294,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@Column(name = "loan_product_counter")
|
@Column(name = "loan_product_counter")
|
||||||
private Integer loanProductCounter;
|
private Integer loanProductCounter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private Set<LoanCharge> charges = new HashSet<>();
|
private Set<LoanCharge> charges = new HashSet<>();
|
||||||
|
|
||||||
@@ -321,15 +318,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private List<LoanTransaction> loanTransactions = new ArrayList<>();
|
private List<LoanTransaction> loanTransactions = new ArrayList<>();
|
||||||
|
|
||||||
|
@Setter
|
||||||
@Embedded
|
@Embedded
|
||||||
private LoanSummary summary;
|
private LoanSummary summary;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private boolean accountNumberRequiresAutoGeneration;
|
private boolean accountNumberRequiresAutoGeneration;
|
||||||
|
|
||||||
@Transient
|
|
||||||
private LoanLifecycleStateMachine loanLifecycleStateMachine;
|
|
||||||
|
|
||||||
@Setter()
|
@Setter()
|
||||||
@Column(name = "principal_amount_proposed", scale = 6, precision = 19, nullable = false)
|
@Column(name = "principal_amount_proposed", scale = 6, precision = 19, nullable = false)
|
||||||
private BigDecimal proposedPrincipal;
|
private BigDecimal proposedPrincipal;
|
||||||
@@ -638,40 +633,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return collateral;
|
return collateral;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLoanCharge(final LoanCharge loanCharge) {
|
|
||||||
loanCharge.update(this);
|
|
||||||
|
|
||||||
final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
|
||||||
BigDecimal chargeAmt;
|
|
||||||
BigDecimal totalChargeAmt = BigDecimal.ZERO;
|
|
||||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
|
||||||
chargeAmt = loanCharge.getPercentage();
|
|
||||||
if (loanCharge.isInstalmentFee()) {
|
|
||||||
totalChargeAmt = calculatePerInstallmentChargeAmount(loanCharge);
|
|
||||||
} else if (loanCharge.isOverdueInstallmentCharge()) {
|
|
||||||
totalChargeAmt = loanCharge.amountOutstanding();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
chargeAmt = loanCharge.amountOrPercentage();
|
|
||||||
}
|
|
||||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, fetchNumberOfInstallmensAfterExceptions(), totalChargeAmt);
|
|
||||||
|
|
||||||
// NOTE: must add new loan charge to set of loan charges before
|
|
||||||
// reprocessing the repayment schedule.
|
|
||||||
if (this.charges == null) {
|
|
||||||
this.charges = new HashSet<>();
|
|
||||||
}
|
|
||||||
this.charges.add(loanCharge);
|
|
||||||
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
|
||||||
|
|
||||||
// store Id's of existing loan transactions and existing reversed loan transactions
|
|
||||||
final SingleLoanChargeRepaymentScheduleProcessingWrapper wrapper = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
|
|
||||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), loanCharge);
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_ADDED, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a loanTransaction for "Apply Charge Event" with transaction date set to "suppliedTransactionDate". The
|
* Creates a loanTransaction for "Apply Charge Event" with transaction date set to "suppliedTransactionDate". The
|
||||||
* newly created transaction is also added to the Loan on which this method is called.
|
* newly created transaction is also added to the Loan on which this method is called.
|
||||||
@@ -771,9 +732,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LoanDisbursementDetails addLoanDisbursementDetails(final LocalDate expectedDisbursementDate, final BigDecimal principal) {
|
public LoanDisbursementDetails addLoanDisbursementDetails(final LocalDate expectedDisbursementDate, final BigDecimal principal) {
|
||||||
final LocalDate actualDisbursementDate = null;
|
final LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, null, principal,
|
||||||
final LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate,
|
this.netDisbursalAmount, false);
|
||||||
principal, this.netDisbursalAmount, false);
|
|
||||||
disbursementDetails.updateLoan(this);
|
disbursementDetails.updateLoan(this);
|
||||||
this.disbursementDetails.add(disbursementDetails);
|
this.disbursementDetails.add(disbursementDetails);
|
||||||
return disbursementDetails;
|
return disbursementDetails;
|
||||||
@@ -992,11 +952,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanSummaryAndStatus() {
|
|
||||||
updateLoanSummaryDerivedFields();
|
|
||||||
doPostLoanTransactionChecks(getLastUserTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInterestRecalculationEnabledForProduct() {
|
private boolean isInterestRecalculationEnabledForProduct() {
|
||||||
return this.loanProduct.isInterestRecalculationEnabled();
|
return this.loanProduct.isInterestRecalculationEnabled();
|
||||||
}
|
}
|
||||||
@@ -1111,35 +1066,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return maturityDate;
|
return maturityDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> undoApproval(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
final LoanStatus currentStatus = getStatus();
|
|
||||||
final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVAL_UNDO, this);
|
|
||||||
if (!statusEnum.hasStateOf(currentStatus)) {
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, this);
|
|
||||||
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(this.loanStatus));
|
|
||||||
|
|
||||||
this.approvedOnDate = null;
|
|
||||||
this.approvedBy = null;
|
|
||||||
|
|
||||||
if (this.approvedPrincipal.compareTo(this.proposedPrincipal) != 0) {
|
|
||||||
this.approvedPrincipal = this.proposedPrincipal;
|
|
||||||
this.loanRepaymentScheduleDetail.setPrincipal(this.proposedPrincipal);
|
|
||||||
|
|
||||||
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, this.proposedPrincipal);
|
|
||||||
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, this.proposedPrincipal);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
actualChanges.put(APPROVED_ON_DATE, "");
|
|
||||||
|
|
||||||
this.loanOfficerHistory.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return actualChanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Long> findExistingTransactionIds() {
|
public List<Long> findExistingTransactionIds() {
|
||||||
return getLoanTransactions().stream() //
|
return getLoanTransactions().stream() //
|
||||||
.filter(loanTransaction -> loanTransaction.getId() != null) //
|
.filter(loanTransaction -> loanTransaction.getId() != null) //
|
||||||
@@ -1161,18 +1087,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canDisburse() {
|
|
||||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSED, this);
|
|
||||||
|
|
||||||
boolean isMultiTrancheDisburse = false;
|
|
||||||
LoanStatus actualLoanStatus = getStatus();
|
|
||||||
if ((actualLoanStatus.isActive() || actualLoanStatus.isClosedObligationsMet() || actualLoanStatus.isOverpaid())
|
|
||||||
&& isAllTranchesNotDisbursed()) {
|
|
||||||
isMultiTrancheDisburse = true;
|
|
||||||
}
|
|
||||||
return !statusEnum.hasStateOf(actualLoanStatus) || isMultiTrancheDisburse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
|
public Collection<LoanDisbursementDetails> fetchUndisbursedDetail() {
|
||||||
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
|
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
|
||||||
LocalDate date = null;
|
LocalDate date = null;
|
||||||
@@ -1304,60 +1218,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
|
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean doPostLoanTransactionChecks(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
boolean statusChanged = checkAndHandleLoanStatus(transactionDate, loanLifecycleStateMachine);
|
|
||||||
resetOverpaidDateIfNeeded();
|
|
||||||
return statusChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkAndHandleLoanStatus(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
boolean statusChanged = false;
|
|
||||||
boolean isOverpaid = MathUtil.isGreaterThanZero(totalOverpaid);
|
|
||||||
|
|
||||||
if (isOverpaid) {
|
|
||||||
handleLoanOverpayment(transactionDate, loanLifecycleStateMachine);
|
|
||||||
statusChanged = true;
|
|
||||||
} else if (this.summary.isRepaidInFull(getCurrency())) {
|
|
||||||
handleLoanRepaymentInFull(transactionDate, loanLifecycleStateMachine);
|
|
||||||
statusChanged = true;
|
|
||||||
} else {
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return statusChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetOverpaidDateIfNeeded() {
|
|
||||||
if (MathUtil.isEmpty(totalOverpaid)) {
|
|
||||||
this.overpaidOnDate = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLoanRepaymentInFull(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
boolean isAllChargesPaid = this.charges.stream() //
|
|
||||||
.allMatch(charge -> !charge.isActive() || charge.amount().compareTo(BigDecimal.ZERO) <= 0 || charge.isPaid()
|
|
||||||
|| charge.isWaived());
|
|
||||||
|
|
||||||
if (isAllChargesPaid) {
|
|
||||||
this.closedOnDate = transactionDate;
|
|
||||||
this.actualMaturityDate = transactionDate;
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this);
|
|
||||||
|
|
||||||
} else if (getStatus().isOverpaid()) {
|
|
||||||
if (MathUtil.isEmpty(totalOverpaid)) {
|
|
||||||
this.overpaidOnDate = null;
|
|
||||||
}
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLoanOverpayment(LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
this.overpaidOnDate = transactionDate;
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, this);
|
|
||||||
this.closedOnDate = null;
|
|
||||||
this.actualMaturityDate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isChronologicallyLatestRepaymentOrWaiver(final LoanTransaction loanTransaction) {
|
public boolean isChronologicallyLatestRepaymentOrWaiver(final LoanTransaction loanTransaction) {
|
||||||
boolean isChronologicallyLatestRepaymentOrWaiver = true;
|
boolean isChronologicallyLatestRepaymentOrWaiver = true;
|
||||||
|
|
||||||
@@ -1852,10 +1712,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return receivableInterest;
|
return receivableInterest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
this.loanLifecycleStateMachine = loanLifecycleStateMachine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSyncDisbursementWithMeeting() {
|
public boolean isSyncDisbursementWithMeeting() {
|
||||||
return this.syncDisbursementWithMeeting != null && this.syncDisbursementWithMeeting;
|
return this.syncDisbursementWithMeeting != null && this.syncDisbursementWithMeeting;
|
||||||
}
|
}
|
||||||
@@ -2486,10 +2342,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
Money[] receivables = retrieveIncomeOutstandingTillDate(closureDate);
|
Money[] receivables = retrieveIncomeOutstandingTillDate(closureDate);
|
||||||
Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
|
Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
|
||||||
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
||||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
|
||||||
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||||
return new LoanRepaymentScheduleInstallment(null, 0, currentDate, currentDate, totalPrincipal.getAmount(),
|
return new LoanRepaymentScheduleInstallment(null, 0, currentDate, currentDate, totalPrincipal.getAmount(),
|
||||||
receivables[0].getAmount(), receivables[1].getAmount(), receivables[2].getAmount(), false, compoundingDetails);
|
receivables[0].getAmount(), receivables[1].getAmount(), receivables[2].getAmount(), false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money[] retrieveIncomeOutstandingTillDate(final LocalDate paymentDate) {
|
private Money[] retrieveIncomeOutstandingTillDate(final LocalDate paymentDate) {
|
||||||
|
|||||||
+18
@@ -18,9 +18,27 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
public interface LoanLifecycleStateMachine {
|
public interface LoanLifecycleStateMachine {
|
||||||
|
|
||||||
LoanStatus dryTransition(LoanEvent loanEvent, Loan loan);
|
LoanStatus dryTransition(LoanEvent loanEvent, Loan loan);
|
||||||
|
|
||||||
void transition(LoanEvent loanEvent, Loan loan);
|
void transition(LoanEvent loanEvent, Loan loan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the appropriate loan status based on the loan's current financial condition. This method is designed
|
||||||
|
* to be called at the end of processing to ensure accurate status transitions.
|
||||||
|
*
|
||||||
|
* For example, it can determine whether a loan that was OVERPAID should transition to: - CLOSED_OBLIGATIONS_MET (if
|
||||||
|
* overpayment amount is now zero) - ACTIVE (if there is still outstanding balance) - Remain as OVERPAID (if still
|
||||||
|
* overpaid)
|
||||||
|
*
|
||||||
|
* @param loan
|
||||||
|
* The loan whose status should be evaluated
|
||||||
|
* @param transactionDate
|
||||||
|
* The date of the transaction that may have affected the loan status
|
||||||
|
*/
|
||||||
|
void determineAndTransition(Loan loan, LocalDate transactionDate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -39,8 +39,6 @@ public interface LoanAssembler {
|
|||||||
|
|
||||||
Loan assembleFrom(JsonCommand command);
|
Loan assembleFrom(JsonCommand command);
|
||||||
|
|
||||||
void setHelpers(Loan loanAccount);
|
|
||||||
|
|
||||||
void accountNumberGeneration(JsonCommand command, Loan loan);
|
void accountNumberGeneration(JsonCommand command, Loan loan);
|
||||||
|
|
||||||
CodeValue findCodeValueByIdIfProvided(Long codeValueId);
|
CodeValue findCodeValueByIdIfProvided(Long codeValueId);
|
||||||
|
|||||||
+42
-7
@@ -32,6 +32,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachin
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
|
||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||||
@@ -41,6 +42,7 @@ public class LoanChargeService {
|
|||||||
|
|
||||||
private final LoanChargeValidator loanChargeValidator;
|
private final LoanChargeValidator loanChargeValidator;
|
||||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||||
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
|
|
||||||
public void recalculateAllCharges(final Loan loan) {
|
public void recalculateAllCharges(final Loan loan) {
|
||||||
Set<LoanCharge> charges = loan.getActiveCharges();
|
Set<LoanCharge> charges = loan.getActiveCharges();
|
||||||
@@ -76,9 +78,8 @@ public class LoanChargeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeChargePayment(final Loan loan, final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public void makeChargePayment(final Loan loan, final Long chargeId, final List<Long> existingTransactionIds,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingReversedTransactionIds, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||||
final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
|
||||||
loanChargeValidator.validateChargePaymentNotInFuture(paymentTransaction);
|
loanChargeValidator.validateChargePaymentNotInFuture(paymentTransaction);
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
@@ -88,11 +89,11 @@ public class LoanChargeService {
|
|||||||
charge = loanCharge;
|
charge = loanCharge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleChargePaidTransaction(loan, charge, paymentTransaction, loanLifecycleStateMachine, installmentNumber);
|
handleChargePaidTransaction(loan, charge, paymentTransaction, installmentNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleChargePaidTransaction(final Loan loan, final LoanCharge charge, final LoanTransaction chargesPayment,
|
public void handleChargePaidTransaction(final Loan loan, final LoanCharge charge, final LoanTransaction chargesPayment,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) {
|
final Integer installmentNumber) {
|
||||||
chargesPayment.updateLoan(loan);
|
chargesPayment.updateLoan(loan);
|
||||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge,
|
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge,
|
||||||
chargesPayment.getAmount(loan.getCurrency()).getAmount(), installmentNumber);
|
chargesPayment.getAmount(loan.getCurrency()).getAmount(), installmentNumber);
|
||||||
@@ -118,7 +119,41 @@ public class LoanChargeService {
|
|||||||
new TransactionCtx(loan.getCurrency(), chargePaymentInstallments, loanCharges,
|
new TransactionCtx(loan.getCurrency(), chargePaymentInstallments, loanCharges,
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, chargesPayment.getTransactionDate());
|
||||||
loan.doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addLoanCharge(final Loan loan, final LoanCharge loanCharge) {
|
||||||
|
loanCharge.update(loan);
|
||||||
|
|
||||||
|
final BigDecimal amount = loan.calculateAmountPercentageAppliedTo(loanCharge);
|
||||||
|
BigDecimal chargeAmt;
|
||||||
|
BigDecimal totalChargeAmt = BigDecimal.ZERO;
|
||||||
|
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
|
chargeAmt = loanCharge.getPercentage();
|
||||||
|
if (loanCharge.isInstalmentFee()) {
|
||||||
|
totalChargeAmt = loan.calculatePerInstallmentChargeAmount(loanCharge);
|
||||||
|
} else if (loanCharge.isOverdueInstallmentCharge()) {
|
||||||
|
totalChargeAmt = loanCharge.amountOutstanding();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chargeAmt = loanCharge.amountOrPercentage();
|
||||||
|
}
|
||||||
|
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, loan.fetchNumberOfInstallmensAfterExceptions(), totalChargeAmt);
|
||||||
|
|
||||||
|
// NOTE: must add new loan charge to set of loan charges before
|
||||||
|
// reprocessing the repayment schedule.
|
||||||
|
if (loan.getLoanCharges() == null) {
|
||||||
|
loan.setCharges(new HashSet<>());
|
||||||
|
}
|
||||||
|
loan.getLoanCharges().add(loanCharge);
|
||||||
|
loan.setSummary(loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement()));
|
||||||
|
|
||||||
|
// store Id's of existing loan transactions and existing reversed loan transactions
|
||||||
|
final SingleLoanChargeRepaymentScheduleProcessingWrapper wrapper = new SingleLoanChargeRepaymentScheduleProcessingWrapper();
|
||||||
|
wrapper.reprocess(loan.getCurrency(), loan.getDisbursementDate(), loan.getRepaymentScheduleInstallments(), loanCharge);
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_ADDED, loan);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -21,7 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
|
||||||
public interface LoanDownPaymentHandlerService {
|
public interface LoanDownPaymentHandlerService {
|
||||||
@@ -30,6 +29,5 @@ public interface LoanDownPaymentHandlerService {
|
|||||||
LoanTransaction disbursementTransaction, Loan loan);
|
LoanTransaction disbursementTransaction, Loan loan);
|
||||||
|
|
||||||
void handleRepaymentOrRecoveryOrWaiverTransaction(Loan loan, LoanTransaction newTransactionDetail,
|
void handleRepaymentOrRecoveryOrWaiverTransaction(Loan loan, LoanTransaction newTransactionDetail,
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanTransaction transactionForAdjustment,
|
LoanTransaction transactionForAdjustment, ScheduleGeneratorDTO scheduleGeneratorDTO);
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -61,6 +61,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
|||||||
private final LoanRefundValidator loanRefundValidator;
|
private final LoanRefundValidator loanRefundValidator;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||||
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command,
|
public LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command,
|
||||||
@@ -77,8 +78,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
public void handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
|
final LoanTransaction adjustedTransaction, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
|
||||||
if (loanTransaction.isRecoveryRepayment()) {
|
if (loanTransaction.isRecoveryRepayment()) {
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, loan);
|
||||||
}
|
}
|
||||||
@@ -158,13 +158,13 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
|||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
}
|
}
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FIXME: Vishwas, skipping post loan transaction checks for Loan recoveries
|
* FIXME: Vishwas, skipping post loan transaction checks for Loan recoveries
|
||||||
**/
|
**/
|
||||||
if (loanTransaction.isNotRecoveryRepayment()) {
|
if (loanTransaction.isNotRecoveryRepayment()) {
|
||||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
|
||||||
|
} else {
|
||||||
|
loan.updateLoanSummaryDerivedFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
||||||
@@ -223,8 +223,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
|||||||
loanDownPaymentTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(downPaymentTransaction.getTransactionDate(),
|
loanDownPaymentTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(downPaymentTransaction.getTransactionDate(),
|
||||||
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||||
|
|
||||||
handleRepaymentOrRecoveryOrWaiverTransaction(loan, downPaymentTransaction, loan.getLoanLifecycleStateMachine(), null,
|
handleRepaymentOrRecoveryOrWaiverTransaction(loan, downPaymentTransaction, null, scheduleGeneratorDTO);
|
||||||
scheduleGeneratorDTO);
|
|
||||||
return downPaymentTransaction;
|
return downPaymentTransaction;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+9
-21
@@ -21,7 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
@@ -35,9 +34,9 @@ public class LoanRefundService {
|
|||||||
|
|
||||||
private final LoanRefundValidator loanRefundValidator;
|
private final LoanRefundValidator loanRefundValidator;
|
||||||
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
||||||
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
|
|
||||||
public void makeRefund(final Loan loan, final LoanTransaction loanTransaction,
|
public void makeRefund(final Loan loan, final LoanTransaction loanTransaction, final List<Long> existingTransactionIds,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
|
||||||
final List<Long> existingReversedTransactionIds) {
|
final List<Long> existingReversedTransactionIds) {
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
@@ -49,8 +48,7 @@ public class LoanRefundService {
|
|||||||
if (loanTransaction.isNotZero()) {
|
if (loanTransaction.isNotZero()) {
|
||||||
loan.addLoanTransaction(loanTransaction);
|
loan.addLoanTransaction(loanTransaction);
|
||||||
}
|
}
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
|
||||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate extractTransactionDate(final Loan loan, final LoanTransaction loanTransaction) {
|
public LocalDate extractTransactionDate(final Loan loan, final LoanTransaction loanTransaction) {
|
||||||
@@ -59,18 +57,16 @@ public class LoanRefundService {
|
|||||||
return loanTransactionDate;
|
return loanTransactionDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeRefundForActiveLoan(final Loan loan, final LoanTransaction loanTransaction,
|
public void makeRefundForActiveLoan(final Loan loan, final LoanTransaction loanTransaction, final List<Long> existingTransactionIds,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
|
||||||
final List<Long> existingReversedTransactionIds) {
|
final List<Long> existingReversedTransactionIds) {
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
|
|
||||||
handleRefundTransaction(loan, loanTransaction, loanLifecycleStateMachine);
|
handleRefundTransaction(loan, loanTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void creditBalanceRefund(final Loan loan, final LoanTransaction newCreditBalanceRefundTransaction,
|
public void creditBalanceRefund(final Loan loan, final LoanTransaction newCreditBalanceRefundTransaction,
|
||||||
final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||||
final List<Long> existingReversedTransactionIds) {
|
|
||||||
loanRefundValidator.validateCreditBalanceRefund(loan, newCreditBalanceRefundTransaction);
|
loanRefundValidator.validateCreditBalanceRefund(loan, newCreditBalanceRefundTransaction);
|
||||||
|
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
@@ -78,17 +74,10 @@ public class LoanRefundService {
|
|||||||
|
|
||||||
loan.getLoanTransactions().add(newCreditBalanceRefundTransaction);
|
loan.getLoanTransactions().add(newCreditBalanceRefundTransaction);
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, newCreditBalanceRefundTransaction.getTransactionDate());
|
||||||
|
|
||||||
if (MathUtil.isEmpty(loan.getTotalOverpaid())) {
|
|
||||||
loan.setOverpaidOnDate(null);
|
|
||||||
loan.setClosedOnDate(newCreditBalanceRefundTransaction.getTransactionDate());
|
|
||||||
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_CREDIT_BALANCE_REFUND, loan);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleRefundTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
private void handleRefundTransaction(final Loan loan, final LoanTransaction loanTransaction) {
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, loan);
|
||||||
|
|
||||||
loanTransaction.updateLoan(loan);
|
loanTransaction.updateLoan(loan);
|
||||||
@@ -110,7 +99,6 @@ public class LoanRefundService {
|
|||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
|
||||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-24
@@ -211,12 +211,12 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void reverseTransfersWithFromAccountType(final Long accountNumber, final PortfolioAccountType accountTypeId) {
|
public void reverseTransfersWithFromAccountType(final Long accountNumber, final PortfolioAccountType accountTypeId) {
|
||||||
List<AccountTransferTransaction> acccountTransfers = null;
|
List<AccountTransferTransaction> accountTransfers = null;
|
||||||
if (accountTypeId.isLoanAccount()) {
|
if (accountTypeId.isLoanAccount()) {
|
||||||
acccountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber);
|
accountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber);
|
||||||
}
|
}
|
||||||
if (acccountTransfers != null && acccountTransfers.size() > 0) {
|
if (accountTransfers != null && !accountTransfers.isEmpty()) {
|
||||||
undoTransactions(acccountTransfers);
|
undoTransactions(accountTransfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -225,35 +225,31 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
@Transactional
|
@Transactional
|
||||||
public void reverseTransfersWithFromAccountTransactions(final Collection<Long> fromTransactionIds,
|
public void reverseTransfersWithFromAccountTransactions(final Collection<Long> fromTransactionIds,
|
||||||
final PortfolioAccountType accountTypeId) {
|
final PortfolioAccountType accountTypeId) {
|
||||||
List<AccountTransferTransaction> acccountTransfers = new ArrayList<>();
|
List<AccountTransferTransaction> accountTransfers = new ArrayList<>();
|
||||||
if (accountTypeId.isLoanAccount()) {
|
if (accountTypeId.isLoanAccount()) {
|
||||||
List<List<Long>> partitions = Lists.partition(fromTransactionIds.stream().toList(),
|
List<List<Long>> partitions = Lists.partition(fromTransactionIds.stream().toList(),
|
||||||
fineractProperties.getQuery().getInClauseParameterSizeLimit());
|
fineractProperties.getQuery().getInClauseParameterSizeLimit());
|
||||||
partitions.forEach(partition -> acccountTransfers.addAll(this.accountTransferRepository.findByFromLoanTransactions(partition)));
|
partitions.forEach(partition -> accountTransfers.addAll(this.accountTransferRepository.findByFromLoanTransactions(partition)));
|
||||||
}
|
}
|
||||||
if (acccountTransfers.size() > 0) {
|
if (!accountTransfers.isEmpty()) {
|
||||||
undoTransactions(acccountTransfers);
|
undoTransactions(accountTransfers);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void reverseAllTransactions(final Long accountId, final PortfolioAccountType accountTypeId) {
|
public void reverseAllTransactions(final Long accountId, final PortfolioAccountType accountTypeId) {
|
||||||
List<AccountTransferTransaction> acccountTransfers = null;
|
List<AccountTransferTransaction> accountTransfers = null;
|
||||||
if (accountTypeId.isLoanAccount()) {
|
if (accountTypeId.isLoanAccount()) {
|
||||||
acccountTransfers = this.accountTransferRepository.findAllByLoanId(accountId);
|
accountTransfers = this.accountTransferRepository.findAllByLoanId(accountId);
|
||||||
}
|
}
|
||||||
if (acccountTransfers != null && acccountTransfers.size() > 0) {
|
if (accountTransfers != null && !accountTransfers.isEmpty()) {
|
||||||
undoTransactions(acccountTransfers);
|
undoTransactions(accountTransfers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void undoTransactions(final List<AccountTransferTransaction> accountTransfers) {
|
||||||
* @param acccountTransfers
|
for (final AccountTransferTransaction accountTransfer : accountTransfers) {
|
||||||
*/
|
|
||||||
private void undoTransactions(final List<AccountTransferTransaction> acccountTransfers) {
|
|
||||||
for (final AccountTransferTransaction accountTransfer : acccountTransfers) {
|
|
||||||
if (accountTransfer.getFromLoanTransaction() != null) {
|
if (accountTransfer.getFromLoanTransaction() != null) {
|
||||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
|
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
|
||||||
}
|
}
|
||||||
@@ -298,14 +294,12 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||||
} else {
|
} else {
|
||||||
toLoanAccount = accountTransferDTO.getLoan();
|
toLoanAccount = accountTransferDTO.getLoan();
|
||||||
this.loanAccountAssembler.setHelpers(toLoanAccount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fromSavingsAccount = accountTransferDetails.fromSavingsAccount();
|
fromSavingsAccount = accountTransferDetails.fromSavingsAccount();
|
||||||
this.savingsAccountAssembler.setHelpers(fromSavingsAccount);
|
this.savingsAccountAssembler.setHelpers(fromSavingsAccount);
|
||||||
toLoanAccount = accountTransferDetails.toLoanAccount();
|
toLoanAccount = accountTransferDetails.toLoanAccount();
|
||||||
this.loanAccountAssembler.setHelpers(toLoanAccount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer,
|
final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer,
|
||||||
@@ -414,12 +408,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
||||||
} else {
|
} else {
|
||||||
fromLoanAccount = accountTransferDTO.getLoan();
|
fromLoanAccount = accountTransferDTO.getLoan();
|
||||||
this.loanAccountAssembler.setHelpers(fromLoanAccount);
|
|
||||||
}
|
}
|
||||||
toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill);
|
toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill);
|
||||||
} else {
|
} else {
|
||||||
fromLoanAccount = accountTransferDetails.fromLoanAccount();
|
fromLoanAccount = accountTransferDetails.fromLoanAccount();
|
||||||
this.loanAccountAssembler.setHelpers(fromLoanAccount);
|
|
||||||
toSavingsAccount = accountTransferDetails.toSavingsAccount();
|
toSavingsAccount = accountTransferDetails.toSavingsAccount();
|
||||||
this.savingsAccountAssembler.setHelpers(toSavingsAccount);
|
this.savingsAccountAssembler.setHelpers(toSavingsAccount);
|
||||||
}
|
}
|
||||||
@@ -472,14 +464,12 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
||||||
} else {
|
} else {
|
||||||
fromLoanAccount = accountTransferDTO.getFromLoan();
|
fromLoanAccount = accountTransferDTO.getFromLoan();
|
||||||
this.loanAccountAssembler.setHelpers(fromLoanAccount);
|
|
||||||
}
|
}
|
||||||
Loan toLoanAccount = null;
|
Loan toLoanAccount = null;
|
||||||
if (accountTransferDTO.getToLoan() == null) {
|
if (accountTransferDTO.getToLoan() == null) {
|
||||||
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||||
} else {
|
} else {
|
||||||
toLoanAccount = accountTransferDTO.getToLoan();
|
toLoanAccount = accountTransferDTO.getToLoan();
|
||||||
this.loanAccountAssembler.setHelpers(toLoanAccount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExternalId externalIdForDisbursement = accountTransferDTO.getTxnExternalId();
|
ExternalId externalIdForDisbursement = accountTransferDTO.getTxnExternalId();
|
||||||
|
|||||||
+15
-21
@@ -141,7 +141,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
private final PostDatedChecksRepository postDatedChecksRepository;
|
private final PostDatedChecksRepository postDatedChecksRepository;
|
||||||
private final LoanCollateralManagementRepository loanCollateralManagementRepository;
|
private final LoanCollateralManagementRepository loanCollateralManagementRepository;
|
||||||
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||||
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
|
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
|
||||||
@@ -259,8 +259,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
loanDownPaymentTransactionValidator.validateRepaymentTypeAccountStatus(loan, newRepaymentTransaction, event);
|
loanDownPaymentTransactionValidator.validateRepaymentTypeAccountStatus(loan, newRepaymentTransaction, event);
|
||||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, event,
|
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, event,
|
||||||
newRepaymentTransaction.getTransactionDate());
|
newRepaymentTransaction.getTransactionDate());
|
||||||
makeRepayment(loan, newRepaymentTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
makeRepayment(loan, newRepaymentTransaction, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
|
||||||
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
@@ -421,8 +420,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
LoanEvent.LOAN_CHARGE_PAYMENT);
|
LoanEvent.LOAN_CHARGE_PAYMENT);
|
||||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_CHARGE_PAYMENT,
|
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_CHARGE_PAYMENT,
|
||||||
newPaymentTransaction.getTransactionDate());
|
newPaymentTransaction.getTransactionDate());
|
||||||
loanChargeService.makeChargePayment(loan, chargeId, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
loanChargeService.makeChargePayment(loan, chargeId, existingTransactionIds, existingReversedTransactionIds,
|
||||||
existingReversedTransactionIds, newPaymentTransaction, installmentNumber);
|
newPaymentTransaction, installmentNumber);
|
||||||
}
|
}
|
||||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -520,8 +519,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newRefundTransaction.getTransactionDate(), workingDays,
|
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newRefundTransaction.getTransactionDate(), workingDays,
|
||||||
allowTransactionsOnNonWorkingDay);
|
allowTransactionsOnNonWorkingDay);
|
||||||
|
|
||||||
loanRefundService.makeRefund(loan, newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
loanRefundService.makeRefund(loan, newRefundTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||||
existingReversedTransactionIds);
|
|
||||||
|
|
||||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction);
|
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction);
|
||||||
this.loanRepositoryWrapper.saveAndFlush(loan);
|
this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||||
@@ -655,8 +653,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CREDIT_BALANCE_REFUND);
|
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CREDIT_BALANCE_REFUND);
|
||||||
loanTransactionValidator.validateRefundDateIsAfterLastRepayment(loan, newCreditBalanceRefundTransaction.getTransactionDate());
|
loanTransactionValidator.validateRefundDateIsAfterLastRepayment(loan, newCreditBalanceRefundTransaction.getTransactionDate());
|
||||||
|
|
||||||
loanRefundService.creditBalanceRefund(loan, newCreditBalanceRefundTransaction, defaultLoanLifecycleStateMachine,
|
loanRefundService.creditBalanceRefund(loan, newCreditBalanceRefundTransaction, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds);
|
existingReversedTransactionIds);
|
||||||
|
|
||||||
newCreditBalanceRefundTransaction = this.loanTransactionRepository.saveAndFlush(newCreditBalanceRefundTransaction);
|
newCreditBalanceRefundTransaction = this.loanTransactionRepository.saveAndFlush(newCreditBalanceRefundTransaction);
|
||||||
|
|
||||||
@@ -709,8 +707,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
allowTransactionsOnNonWorkingDay);
|
allowTransactionsOnNonWorkingDay);
|
||||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REFUND,
|
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REFUND,
|
||||||
newRefundTransaction.getTransactionDate());
|
newRefundTransaction.getTransactionDate());
|
||||||
loanRefundService.makeRefundForActiveLoan(loan, newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
loanRefundService.makeRefundForActiveLoan(loan, newRefundTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||||
existingReversedTransactionIds);
|
|
||||||
|
|
||||||
this.loanTransactionRepository.saveAndFlush(newRefundTransaction);
|
this.loanTransactionRepository.saveAndFlush(newRefundTransaction);
|
||||||
|
|
||||||
@@ -777,7 +774,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
loanForeclosureValidator.validateForForeclosure(loan, payment.getTransactionDate());
|
loanForeclosureValidator.validateForForeclosure(loan, payment.getTransactionDate());
|
||||||
}
|
}
|
||||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_FORECLOSURE);
|
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_FORECLOSURE);
|
||||||
handleForeClosureTransactions(loan, payment, defaultLoanLifecycleStateMachine, scheduleGeneratorDTO);
|
handleForeClosureTransactions(loan, payment, scheduleGeneratorDTO);
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
@@ -918,8 +915,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||||
loan.doPostLoanTransactionChecks(transactionDate, defaultLoanLifecycleStateMachine);
|
|
||||||
|
|
||||||
switch (loanTransactionType) {
|
switch (loanTransactionType) {
|
||||||
case MERCHANT_ISSUED_REFUND -> businessEventNotifierService
|
case MERCHANT_ISSUED_REFUND -> businessEventNotifierService
|
||||||
@@ -1062,21 +1058,19 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("null")
|
@SuppressWarnings("null")
|
||||||
private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction,
|
private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction, final List<Long> existingTransactionIds,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
|
||||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loan, repaymentTransaction, "created");
|
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loan, repaymentTransaction, "created");
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
|
|
||||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
|
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, null, scheduleGeneratorDTO);
|
||||||
null, scheduleGeneratorDTO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleForeClosureTransactions(final Loan loan, final LoanTransaction repaymentTransaction,
|
private void handleForeClosureTransactions(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
loan.setLoanSubStatus(LoanSubStatus.FORECLOSED);
|
loan.setLoanSubStatus(LoanSubStatus.FORECLOSED);
|
||||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
|
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, null, scheduleGeneratorDTO);
|
||||||
null, scheduleGeneratorDTO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -160,7 +160,7 @@ public class LoanScheduleAssembler {
|
|||||||
private final LoanUtilService loanUtilService;
|
private final LoanUtilService loanUtilService;
|
||||||
private final LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler;
|
private final LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler;
|
||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||||
private final LoanDisbursementService loanDisbursementService;
|
private final LoanDisbursementService loanDisbursementService;
|
||||||
private final LoanChargeService loanChargeService;
|
private final LoanChargeService loanChargeService;
|
||||||
@@ -1479,7 +1479,7 @@ public class LoanScheduleAssembler {
|
|||||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
|
|
||||||
final Map<String, Object> actualChanges = new HashMap<>();
|
final Map<String, Object> actualChanges = new HashMap<>();
|
||||||
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, loan);
|
||||||
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(loan.getStatus()));
|
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(loan.getStatus()));
|
||||||
|
|
||||||
LocalDate approvedOn = command.localDateValueOfParameterNamed(APPROVED_ON_DATE);
|
LocalDate approvedOn = command.localDateValueOfParameterNamed(APPROVED_ON_DATE);
|
||||||
|
|||||||
+2
-8
@@ -35,7 +35,6 @@ import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
@@ -63,7 +62,6 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||||
private final LoanUtilService loanUtilService;
|
private final LoanUtilService loanUtilService;
|
||||||
private final LoanRepositoryWrapper loanRepository;
|
private final LoanRepositoryWrapper loanRepository;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
|
||||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -204,16 +202,12 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
}
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms constructLoanApplicationTerms(final Loan loan) {
|
private LoanApplicationTerms constructLoanApplicationTerms(final Loan loan) {
|
||||||
final LocalDate recalculateFrom = null;
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
|
||||||
return loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO, loan);
|
return loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO, loan);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Loan fetchLoan(final Long accountId) {
|
private Loan fetchLoan(final Long accountId) {
|
||||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
return loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine);
|
|
||||||
|
|
||||||
return loanAccount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-12
@@ -30,6 +30,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||||
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
|
||||||
@@ -51,7 +52,6 @@ import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
@@ -84,8 +84,6 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
import org.apache.fineract.portfolio.loanaccount.service.ReprocessLoanTransactionsService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
import org.apache.fineract.portfolio.loanaccount.service.schedule.LoanScheduleComponent;
|
||||||
import org.apache.fineract.useradministration.domain.AppUser;
|
import org.apache.fineract.useradministration.domain.AppUser;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.dao.DataIntegrityViolationException;
|
import org.springframework.dao.DataIntegrityViolationException;
|
||||||
import org.springframework.dao.NonTransientDataAccessException;
|
import org.springframework.dao.NonTransientDataAccessException;
|
||||||
@@ -93,11 +91,11 @@ import org.springframework.orm.jpa.JpaSystemException;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
|
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
|
|
||||||
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
||||||
|
|
||||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||||
@@ -258,17 +256,15 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
|
|
||||||
if (rescheduleFromDate != null && endDate != null && emi != null) {
|
if (rescheduleFromDate != null && endDate != null && emi != null) {
|
||||||
LoanTermVariations parent = null;
|
LoanTermVariations parent = null;
|
||||||
LocalDate rescheduleFromLocDate = rescheduleFromDate;
|
|
||||||
LocalDate endDateLocDate = endDate;
|
|
||||||
final Integer termType = LoanTermVariationType.EMI_AMOUNT.getValue();
|
final Integer termType = LoanTermVariationType.EMI_AMOUNT.getValue();
|
||||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (!DateUtils.isBefore(installment.getDueDate(), rescheduleFromLocDate)
|
if (!DateUtils.isBefore(installment.getDueDate(), rescheduleFromDate)
|
||||||
&& !DateUtils.isAfter(installment.getDueDate(), endDateLocDate)) {
|
&& !DateUtils.isAfter(installment.getDueDate(), endDate)) {
|
||||||
createLoanTermVariations(loanRescheduleRequest, termType, loan, installment.getDueDate(), installment.getDueDate(),
|
createLoanTermVariations(loanRescheduleRequest, termType, loan, installment.getDueDate(), installment.getDueDate(),
|
||||||
loanRescheduleRequestToTermVariationMappings, isActive, true, emi, parent);
|
loanRescheduleRequestToTermVariationMappings, isActive, true, emi, parent);
|
||||||
}
|
}
|
||||||
if (DateUtils.isAfter(installment.getDueDate(), endDateLocDate)) {
|
if (DateUtils.isAfter(installment.getDueDate(), endDate)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -430,8 +426,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
.determineProcessor(loan.transactionProcessingStrategy());
|
.determineProcessor(loan.transactionProcessingStrategy());
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),
|
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),
|
||||||
loanApplicationTerms.getInterestMethod());
|
loanApplicationTerms.getInterestMethod());
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
|
|
||||||
loan.setHelpers(loanLifecycleStateMachine);
|
|
||||||
final LoanScheduleDTO loanScheduleDTO = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
|
final LoanScheduleDTO loanScheduleDTO = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
|
||||||
loan, loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
loan, loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
||||||
|
|
||||||
@@ -565,7 +559,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
private void handleDataIntegrityViolation(final NonTransientDataAccessException dve) {
|
private void handleDataIntegrityViolation(final NonTransientDataAccessException dve) {
|
||||||
LOG.error("Error occured.", dve);
|
log.error("Error occurred.", dve);
|
||||||
throw ErrorHandler.getMappable(dve, "error.msg.loan.reschedule.unknown.data.integrity.issue",
|
throw ErrorHandler.getMappable(dve, "error.msg.loan.reschedule.unknown.data.integrity.issue",
|
||||||
"Unknown data integrity issue with resource.");
|
"Unknown data integrity issue with resource.");
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-10
@@ -199,7 +199,7 @@ public final class LoanApplicationValidator {
|
|||||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||||
private final HolidayRepository holidayRepository;
|
private final HolidayRepository holidayRepository;
|
||||||
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||||
private final LoanUtilService loanUtilService;
|
private final LoanUtilService loanUtilService;
|
||||||
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
||||||
@@ -799,7 +799,6 @@ public final class LoanApplicationValidator {
|
|||||||
|
|
||||||
private void fixedLengthValidations(final JsonElement element) {
|
private void fixedLengthValidations(final JsonElement element) {
|
||||||
validateOrThrow("loan", baseDataValidator -> {
|
validateOrThrow("loan", baseDataValidator -> {
|
||||||
boolean isInterestBearing = false;
|
|
||||||
final String transactionProcessingStrategy = this.fromApiJsonHelper
|
final String transactionProcessingStrategy = this.fromApiJsonHelper
|
||||||
.extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
|
.extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
|
||||||
final Integer numberOfRepayments = this.fromApiJsonHelper
|
final Integer numberOfRepayments = this.fromApiJsonHelper
|
||||||
@@ -809,7 +808,7 @@ public final class LoanApplicationValidator {
|
|||||||
|
|
||||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper
|
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper
|
||||||
.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRatePerPeriodParameterName, element);
|
.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRatePerPeriodParameterName, element);
|
||||||
isInterestBearing = interestRatePerPeriod != null && interestRatePerPeriod.compareTo(BigDecimal.ZERO) > 0;
|
final boolean isInterestBearing = interestRatePerPeriod != null && interestRatePerPeriod.compareTo(BigDecimal.ZERO) > 0;
|
||||||
loanProductDataValidator.fixedLengthValidations(transactionProcessingStrategy, isInterestBearing, numberOfRepayments,
|
loanProductDataValidator.fixedLengthValidations(transactionProcessingStrategy, isInterestBearing, numberOfRepayments,
|
||||||
repaymentEvery, element, baseDataValidator);
|
repaymentEvery, element, baseDataValidator);
|
||||||
});
|
});
|
||||||
@@ -1267,7 +1266,7 @@ public final class LoanApplicationValidator {
|
|||||||
final Set<String> supportedParameters = new HashSet<>(Arrays.asList(LoanApiConstants.idParameterName,
|
final Set<String> supportedParameters = new HashSet<>(Arrays.asList(LoanApiConstants.idParameterName,
|
||||||
LoanApiConstants.clientCollateralIdParameterName, LoanApiConstants.quantityParameterName));
|
LoanApiConstants.clientCollateralIdParameterName, LoanApiConstants.quantityParameterName));
|
||||||
final JsonArray array = topLevelJsonElement.get(LoanApiConstants.collateralParameterName).getAsJsonArray();
|
final JsonArray array = topLevelJsonElement.get(LoanApiConstants.collateralParameterName).getAsJsonArray();
|
||||||
if (array.size() > 0) {
|
if (!array.isEmpty()) {
|
||||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||||
for (int i = 1; i <= array.size(); i++) {
|
for (int i = 1; i <= array.size(); i++) {
|
||||||
final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();
|
final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();
|
||||||
@@ -1688,7 +1687,7 @@ public final class LoanApplicationValidator {
|
|||||||
final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName,
|
final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName,
|
||||||
element);
|
element);
|
||||||
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
|
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
|
||||||
if (variationArray != null && variationArray.size() > 0) {
|
if (variationArray != null && !variationArray.isEmpty()) {
|
||||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
||||||
boolean isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam,
|
boolean isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam,
|
||||||
element);
|
element);
|
||||||
@@ -1890,15 +1889,14 @@ public final class LoanApplicationValidator {
|
|||||||
? this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, element)
|
? this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, element)
|
||||||
: originalExpectedDisbursementDate;
|
: originalExpectedDisbursementDate;
|
||||||
|
|
||||||
String defaultUserMessage = "";
|
|
||||||
if (DateUtils.isBefore(submittedOnDate, startDate)) {
|
if (DateUtils.isBefore(submittedOnDate, startDate)) {
|
||||||
defaultUserMessage = "submittedOnDate cannot be before the loan product startDate.";
|
String defaultUserMessage = "submittedOnDate cannot be before the loan product startDate.";
|
||||||
throw new LoanApplicationDateException("submitted.on.date.cannot.be.before.the.loan.product.start.date", defaultUserMessage,
|
throw new LoanApplicationDateException("submitted.on.date.cannot.be.before.the.loan.product.start.date", defaultUserMessage,
|
||||||
submittedOnDate.toString(), startDate.toString());
|
submittedOnDate.toString(), startDate.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closeDate != null && DateUtils.isAfter(submittedOnDate, closeDate)) {
|
if (closeDate != null && DateUtils.isAfter(submittedOnDate, closeDate)) {
|
||||||
defaultUserMessage = "submittedOnDate cannot be after the loan product closeDate.";
|
String defaultUserMessage = "submittedOnDate cannot be after the loan product closeDate.";
|
||||||
throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.product.close.date", defaultUserMessage,
|
throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.product.close.date", defaultUserMessage,
|
||||||
submittedOnDate.toString(), closeDate.toString());
|
submittedOnDate.toString(), closeDate.toString());
|
||||||
}
|
}
|
||||||
@@ -2024,7 +2022,6 @@ public final class LoanApplicationValidator {
|
|||||||
baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
|
baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
|
||||||
|
|
||||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
loan.setHelpers(defaultLoanLifecycleStateMachine);
|
|
||||||
|
|
||||||
final Client client = loan.client();
|
final Client client = loan.client();
|
||||||
if (client != null && client.isNotActive()) {
|
if (client != null && client.isNotActive()) {
|
||||||
@@ -2116,7 +2113,7 @@ public final class LoanApplicationValidator {
|
|||||||
throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, approvedOnDate);
|
throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, approvedOnDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
final LoanStatus newStatus = defaultLoanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVED, loan);
|
final LoanStatus newStatus = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVED, loan);
|
||||||
if (newStatus.hasStateOf(loan.getStatus())) {
|
if (newStatus.hasStateOf(loan.getStatus())) {
|
||||||
final String defaultUserMessage = "Loan is already approved.";
|
final String defaultUserMessage = "Loan is already approved.";
|
||||||
final ApiParameterError error = ApiParameterError
|
final ApiParameterError error = ApiParameterError
|
||||||
|
|||||||
+40
-25
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.service;
|
package org.apache.fineract.portfolio.loanaccount.service;
|
||||||
|
|
||||||
|
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.APPROVED_ON_DATE;
|
||||||
|
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.PARAM_STATUS;
|
||||||
import static org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType.SAME_AS_REPAYMENT_PERIOD;
|
import static org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType.SAME_AS_REPAYMENT_PERIOD;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
@@ -29,6 +31,7 @@ import java.time.LocalDate;
|
|||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -75,7 +78,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
@@ -87,6 +89,7 @@ import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationVa
|
|||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator;
|
||||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||||
|
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||||
import org.apache.fineract.portfolio.note.domain.Note;
|
import org.apache.fineract.portfolio.note.domain.Note;
|
||||||
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
import org.apache.fineract.portfolio.note.domain.NoteRepository;
|
||||||
import org.apache.fineract.portfolio.savings.data.GroupSavingsIndividualMonitoringAccountData;
|
import org.apache.fineract.portfolio.savings.data.GroupSavingsIndividualMonitoringAccountData;
|
||||||
@@ -108,7 +111,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||||
private final NoteRepository noteRepository;
|
private final NoteRepository noteRepository;
|
||||||
private final LoanAssembler loanAssembler;
|
private final LoanAssembler loanAssembler;
|
||||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
|
||||||
private final CalendarRepository calendarRepository;
|
private final CalendarRepository calendarRepository;
|
||||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||||
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
||||||
@@ -121,7 +123,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
private final GLIMAccountInfoRepository glimRepository;
|
private final GLIMAccountInfoRepository glimRepository;
|
||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
private final GSIMReadPlatformService gsimReadPlatformService;
|
private final GSIMReadPlatformService gsimReadPlatformService;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||||
private final LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator;
|
private final LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator;
|
||||||
private final LoanScheduleService loanScheduleService;
|
private final LoanScheduleService loanScheduleService;
|
||||||
@@ -505,9 +507,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult approveGLIMLoanAppication(final Long loanId, final JsonCommand command) {
|
public CommandProcessingResult approveGLIMLoanAppication(final Long loanId, final JsonCommand command) {
|
||||||
|
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(loanId).orElseThrow();
|
||||||
final Long parentLoanId = loanId;
|
|
||||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(parentLoanId).orElseThrow();
|
|
||||||
JsonArray approvalFormData = command.arrayOfParameterNamed("approvalFormData");
|
JsonArray approvalFormData = command.arrayOfParameterNamed("approvalFormData");
|
||||||
|
|
||||||
JsonObject jsonObject = null;
|
JsonObject jsonObject = null;
|
||||||
@@ -579,11 +579,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult undoGLIMLoanApplicationApproval(final Long loanId, final JsonCommand command) {
|
public CommandProcessingResult undoGLIMLoanApplicationApproval(final Long loanId, final JsonCommand command) {
|
||||||
|
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(loanId).orElseThrow();
|
||||||
// GroupLoanIndividualMonitoringAccount
|
|
||||||
// glimAccount=glimRepository.findOne(loanId);
|
|
||||||
final Long parentLoanId = loanId;
|
|
||||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(parentLoanId).orElseThrow();
|
|
||||||
List<Loan> childLoans = this.loanRepository.findByGlimId(loanId);
|
List<Loan> childLoans = this.loanRepository.findByGlimId(loanId);
|
||||||
|
|
||||||
CommandProcessingResult result = null;
|
CommandProcessingResult result = null;
|
||||||
@@ -610,19 +606,16 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult undoApplicationApproval(final Long loanId, final JsonCommand command) {
|
public CommandProcessingResult undoApplicationApproval(final Long loanId, final JsonCommand command) {
|
||||||
|
|
||||||
this.loanApplicationValidator.validateForUndo(command.json());
|
this.loanApplicationValidator.validateForUndo(command.json());
|
||||||
|
|
||||||
Loan loan = retrieveLoanBy(loanId);
|
Loan loan = retrieveLoanBy(loanId);
|
||||||
loanApplicationTransitionValidator.checkClientOrGroupActive(loan);
|
loanApplicationTransitionValidator.checkClientOrGroupActive(loan);
|
||||||
|
|
||||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_APPROVAL_UNDO);
|
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_APPROVAL_UNDO);
|
||||||
final Map<String, Object> changes = loan.undoApproval(defaultLoanLifecycleStateMachine);
|
final Map<String, Object> changes = undoApproval(loan);
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
|
|
||||||
// If loan approved amount is not same as loan amount demanded, then
|
// If loan approved amount is not same as loan amount demanded, then
|
||||||
// during undo, restore the demand amount to principal amount.
|
// during undo, restore the demand amount to principal amount.
|
||||||
|
|
||||||
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName)
|
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName)
|
||||||
|| changes.containsKey(LoanApiConstants.disbursementPrincipalParameterName)) {
|
|| changes.containsKey(LoanApiConstants.disbursementPrincipalParameterName)) {
|
||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = null;
|
||||||
@@ -655,11 +648,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult rejectGLIMApplicationApproval(final Long glimId, final JsonCommand command) {
|
public CommandProcessingResult rejectGLIMApplicationApproval(final Long glimId, final JsonCommand command) {
|
||||||
|
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(glimId).orElseThrow();
|
||||||
// GroupLoanIndividualMonitoringAccount
|
|
||||||
// glimAccount=glimRepository.findOne(loanId);
|
|
||||||
final Long parentLoanId = glimId;
|
|
||||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(parentLoanId).orElseThrow();
|
|
||||||
List<Loan> childLoans = this.loanRepository.findByGlimId(glimId);
|
List<Loan> childLoans = this.loanRepository.findByGlimId(glimId);
|
||||||
|
|
||||||
CommandProcessingResult result = null;
|
CommandProcessingResult result = null;
|
||||||
@@ -699,7 +688,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
|
|
||||||
// loan application rejection
|
// loan application rejection
|
||||||
final AppUser currentUser = getAppUserIfPresent();
|
final AppUser currentUser = getAppUserIfPresent();
|
||||||
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, loan);
|
||||||
final Map<String, Object> changes = loanAssembler.updateLoanApplicationAttributesForRejection(loan, command, currentUser);
|
final Map<String, Object> changes = loanAssembler.updateLoanApplicationAttributesForRejection(loan, command, currentUser);
|
||||||
|
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
@@ -737,7 +726,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
|
|
||||||
// loan application withdrawal
|
// loan application withdrawal
|
||||||
final AppUser currentUser = getAppUserIfPresent();
|
final AppUser currentUser = getAppUserIfPresent();
|
||||||
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, loan);
|
||||||
final Map<String, Object> changes = loanAssembler.updateLoanApplicationAttributesForWithdrawal(loan, command, currentUser);
|
final Map<String, Object> changes = loanAssembler.updateLoanApplicationAttributesForWithdrawal(loan, command, currentUser);
|
||||||
// Release attached collaterals
|
// Release attached collaterals
|
||||||
if (loan.getLoanType().isIndividualAccount()) {
|
if (loan.getLoanType().isIndividualAccount()) {
|
||||||
@@ -764,9 +753,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Loan retrieveLoanBy(final Long loanId) {
|
private Loan retrieveLoanBy(final Long loanId) {
|
||||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
return loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||||
loan.setHelpers(defaultLoanLifecycleStateMachine);
|
|
||||||
return loan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AppUser getAppUserIfPresent() {
|
private AppUser getAppUserIfPresent() {
|
||||||
@@ -862,4 +849,32 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
|||||||
loan.updateLoanCollateral(loanCollateralManagements);
|
loan.updateLoanCollateral(loanCollateralManagements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> undoApproval(final Loan loan) {
|
||||||
|
final Map<String, Object> actualChanges = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
final LoanStatus currentStatus = loan.getStatus();
|
||||||
|
final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVAL_UNDO, loan);
|
||||||
|
if (!statusEnum.hasStateOf(currentStatus)) {
|
||||||
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, loan);
|
||||||
|
actualChanges.put(PARAM_STATUS, LoanEnumerations.status(loan.getStatus()));
|
||||||
|
|
||||||
|
loan.setApprovedOnDate(null);
|
||||||
|
loan.setApprovedBy(null);
|
||||||
|
|
||||||
|
if (loan.getApprovedPrincipal().compareTo(loan.getProposedPrincipal()) != 0) {
|
||||||
|
loan.setApprovedPrincipal(loan.getProposedPrincipal());
|
||||||
|
loan.getLoanRepaymentScheduleDetail().setPrincipal(loan.getProposedPrincipal());
|
||||||
|
|
||||||
|
actualChanges.put(LoanApiConstants.approvedLoanAmountParameterName, loan.getProposedPrincipal());
|
||||||
|
actualChanges.put(LoanApiConstants.disbursementPrincipalParameterName, loan.getProposedPrincipal());
|
||||||
|
}
|
||||||
|
|
||||||
|
actualChanges.put(APPROVED_ON_DATE, "");
|
||||||
|
|
||||||
|
loan.getLoanOfficerHistory().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualChanges;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-20
@@ -76,7 +76,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCreditAllocationRule;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCreditAllocationRule;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRule;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
@@ -123,7 +122,6 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
private final ConfigurationDomainService configurationDomainService;
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||||
private final RateAssembler rateAssembler;
|
private final RateAssembler rateAssembler;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
|
||||||
private final ExternalIdFactory externalIdFactory;
|
private final ExternalIdFactory externalIdFactory;
|
||||||
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
|
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
|
||||||
private final GLIMAccountInfoRepository glimRepository;
|
private final GLIMAccountInfoRepository glimRepository;
|
||||||
@@ -147,31 +145,17 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loan assembleFrom(final Long accountId, final boolean loadLazyCollections) {
|
public Loan assembleFrom(final Long accountId, final boolean loadLazyCollections) {
|
||||||
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(accountId, loadLazyCollections);
|
return loanRepository.findOneWithNotFoundDetection(accountId, loadLazyCollections);
|
||||||
setHelpers(loanAccount);
|
|
||||||
|
|
||||||
return loanAccount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loan assembleFrom(final ExternalId externalId) {
|
public Loan assembleFrom(final ExternalId externalId) {
|
||||||
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(externalId, true);
|
return loanRepository.findOneWithNotFoundDetection(externalId, true);
|
||||||
setHelpers(loanAccount);
|
|
||||||
|
|
||||||
return loanAccount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loan assembleFrom(final ExternalId externalId, final boolean loadLazyCollections) {
|
public Loan assembleFrom(final ExternalId externalId, final boolean loadLazyCollections) {
|
||||||
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(externalId, loadLazyCollections);
|
return loanRepository.findOneWithNotFoundDetection(externalId, loadLazyCollections);
|
||||||
setHelpers(loanAccount);
|
|
||||||
|
|
||||||
return loanAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setHelpers(final Loan loanAccount) {
|
|
||||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -298,7 +282,6 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
|||||||
loanSchedule.updateLoanSchedule(loanApplication, loanScheduleModel);
|
loanSchedule.updateLoanSchedule(loanApplication, loanScheduleModel);
|
||||||
|
|
||||||
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
||||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine);
|
|
||||||
// TODO: review
|
// TODO: review
|
||||||
loanChargeService.recalculateAllCharges(loanApplication);
|
loanChargeService.recalculateAllCharges(loanApplication);
|
||||||
topUpLoanConfiguration(element, loanApplication);
|
topUpLoanConfiguration(element, loanApplication);
|
||||||
|
|||||||
+9
-13
@@ -162,7 +162,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
private final LoanWritePlatformService loanWritePlatformService;
|
private final LoanWritePlatformService loanWritePlatformService;
|
||||||
private final LoanUtilService loanUtilService;
|
private final LoanUtilService loanUtilService;
|
||||||
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
|
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
|
||||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||||
private final FromJsonHelper fromApiJsonHelper;
|
private final FromJsonHelper fromApiJsonHelper;
|
||||||
private final ConfigurationDomainService configurationDomainService;
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
@@ -181,6 +181,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
private final LoanAccountService loanAccountService;
|
private final LoanAccountService loanAccountService;
|
||||||
private final LoanAdjustmentService loanAdjustmentService;
|
private final LoanAdjustmentService loanAdjustmentService;
|
||||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||||
|
private final LoanChargeService loanChargeService;
|
||||||
|
|
||||||
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
|
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
|
||||||
return DateUtils.isAfter(loanCharge.getDueDate(), e.getFromDate()) && !DateUtils.isAfter(loanCharge.getDueDate(), e.getDueDate());
|
return DateUtils.isAfter(loanCharge.getDueDate(), e.getFromDate()) && !DateUtils.isAfter(loanCharge.getDueDate(), e.getDueDate());
|
||||||
@@ -305,7 +306,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
} else {
|
} else {
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
}
|
}
|
||||||
loan.doPostLoanTransactionChecks(transactionDate, loan.getLoanLifecycleStateMachine());
|
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,15 +572,13 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||||
final LoanTransaction waiveTransaction = waiveLoanCharge(loan, loanCharge, defaultLoanLifecycleStateMachine, changes,
|
final LoanTransaction waiveTransaction = waiveLoanCharge(loan, loanCharge, changes, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge,
|
existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge, externalId);
|
||||||
externalId);
|
|
||||||
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), DateUtils.getBusinessLocalDate())) {
|
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), DateUtils.getBusinessLocalDate())) {
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loanTransactionRepository.saveAndFlush(waiveTransaction);
|
this.loanTransactionRepository.saveAndFlush(waiveTransaction);
|
||||||
@@ -928,7 +927,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
}
|
}
|
||||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
||||||
loan.updateLoanSummaryAndStatus();
|
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||||
|
|
||||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
@@ -1109,7 +1108,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
|
|
||||||
loanChargeValidator.validateChargeAdditionForDisbursedLoan(loan, loanCharge);
|
loanChargeValidator.validateChargeAdditionForDisbursedLoan(loan, loanCharge);
|
||||||
loanChargeValidator.validateChargeHasValidSpecifiedDateIfApplicable(loan, loanCharge, loan.getDisbursementDate());
|
loanChargeValidator.validateChargeHasValidSpecifiedDateIfApplicable(loan, loanCharge, loan.getDisbursementDate());
|
||||||
loan.addLoanCharge(loanCharge);
|
loanChargeService.addLoanCharge(loan, loanCharge);
|
||||||
loanCharge = this.loanChargeRepository.saveAndFlush(loanCharge);
|
loanCharge = this.loanChargeRepository.saveAndFlush(loanCharge);
|
||||||
|
|
||||||
// we want to apply charge transactions only for those loans charges that are applied when a loan is active and
|
// we want to apply charge transactions only for those loans charges that are applied when a loan is active and
|
||||||
@@ -1433,8 +1432,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanTransaction waiveLoanCharge(final Loan loan, final LoanCharge loanCharge,
|
public LoanTransaction waiveLoanCharge(final Loan loan, final LoanCharge loanCharge, final Map<String, Object> changes,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final Integer loanInstallmentNumber,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final Integer loanInstallmentNumber,
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge, final ExternalId externalId) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge, final ExternalId externalId) {
|
||||||
final Money amountWaived = loanCharge.waive(loan.getCurrency(), loanInstallmentNumber);
|
final Money amountWaived = loanCharge.waive(loan.getCurrency(), loanInstallmentNumber);
|
||||||
@@ -1512,9 +1510,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
|||||||
loan.getActiveCharges());
|
loan.getActiveCharges());
|
||||||
}
|
}
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, waiveLoanChargeTransaction.getTransactionDate());
|
||||||
|
|
||||||
loan.doPostLoanTransactionChecks(waiveLoanChargeTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
|
|
||||||
return waiveLoanChargeTransaction;
|
return waiveLoanChargeTransaction;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -62,6 +62,7 @@ public class LoanDisbursementService {
|
|||||||
private final LoanChargeValidator loanChargeValidator;
|
private final LoanChargeValidator loanChargeValidator;
|
||||||
private final LoanDisbursementValidator loanDisbursementValidator;
|
private final LoanDisbursementValidator loanDisbursementValidator;
|
||||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||||
|
private final LoanChargeService loanChargeService;
|
||||||
|
|
||||||
public void updateDisbursementDetails(final Loan loan, final JsonCommand jsonCommand, final Map<String, Object> actualChanges) {
|
public void updateDisbursementDetails(final Loan loan, final JsonCommand jsonCommand, final Map<String, Object> actualChanges) {
|
||||||
final List<Long> disbursementList = loan.fetchDisbursementIds();
|
final List<Long> disbursementList = loan.fetchDisbursementIds();
|
||||||
@@ -251,7 +252,7 @@ public class LoanDisbursementService {
|
|||||||
|
|
||||||
loanChargeValidator.validateChargeAdditionForDisbursedLoan(loan, loanCharge);
|
loanChargeValidator.validateChargeAdditionForDisbursedLoan(loan, loanCharge);
|
||||||
loanChargeValidator.validateChargeHasValidSpecifiedDateIfApplicable(loan, loanCharge, loan.getDisbursementDate());
|
loanChargeValidator.validateChargeHasValidSpecifiedDateIfApplicable(loan, loanCharge, loan.getDisbursementDate());
|
||||||
loan.addLoanCharge(loanCharge);
|
loanChargeService.addLoanCharge(loan, loanCharge);
|
||||||
}
|
}
|
||||||
actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
|
actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
|
||||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||||
|
|||||||
+34
-116
@@ -368,7 +368,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final Locale locale = command.extractLocale();
|
final Locale locale = command.extractLocale();
|
||||||
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
|
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
|
||||||
|
|
||||||
if (loan.canDisburse()) {
|
if (canDisburse(loan)) {
|
||||||
// Get netDisbursalAmount from disbursal screen field.
|
// Get netDisbursalAmount from disbursal screen field.
|
||||||
final BigDecimal netDisbursalAmount = command
|
final BigDecimal netDisbursalAmount = command
|
||||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
|
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
|
||||||
@@ -756,8 +756,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
// disbursement and actual disbursement happens on same date
|
// disbursement and actual disbursement happens on same date
|
||||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_DISBURSED);
|
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_DISBURSED);
|
||||||
updateLoanCounters(loan, actualDisbursementDate);
|
updateLoanCounters(loan, actualDisbursementDate);
|
||||||
boolean canDisburse = loan.canDisburse();
|
if (canDisburse(loan)) {
|
||||||
if (canDisburse) {
|
|
||||||
Money amountBeforeAdjust = loan.getPrincipal();
|
Money amountBeforeAdjust = loan.getPrincipal();
|
||||||
Money disburseAmount = loanDisbursementService.adjustDisburseAmount(loan, command, actualDisbursementDate);
|
Money disburseAmount = loanDisbursementService.adjustDisburseAmount(loan, command, actualDisbursementDate);
|
||||||
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincipal());
|
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincipal());
|
||||||
@@ -960,76 +959,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
chargeRefundChargeType);
|
chargeRefundChargeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleLoanOverpayment(Loan loan, LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
loan.setOverpaidOnDate(transactionDate);
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
|
|
||||||
loan.setClosedOnDate(null);
|
|
||||||
loan.setActualMaturityDate(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLoanRepaymentInFull(final Loan loan, final LocalDate transactionDate,
|
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
|
|
||||||
boolean isAllChargesPaid = true;
|
|
||||||
for (final LoanCharge loanCharge : loan.getCharges()) {
|
|
||||||
if (loanCharge.isActive() && loanCharge.amount().compareTo(BigDecimal.ZERO) > 0
|
|
||||||
&& !(loanCharge.isPaid() || loanCharge.isWaived())) {
|
|
||||||
isAllChargesPaid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isAllChargesPaid) {
|
|
||||||
loan.setClosedOnDate(transactionDate);
|
|
||||||
loan.setActualMaturityDate(transactionDate);
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, loan);
|
|
||||||
} else if (loan.getLoanStatus().isOverpaid()) {
|
|
||||||
if (loan.getTotalOverpaid() == null || BigDecimal.ZERO.compareTo(loan.getTotalOverpaid()) == 0) {
|
|
||||||
loan.setOverpaidOnDate(null);
|
|
||||||
}
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean doPostLoanTransactionChecks(final Loan loan, final LocalDate transactionDate,
|
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
boolean statusChanged = false;
|
|
||||||
boolean isOverpaid = loan.getTotalOverpaid() != null && loan.getTotalOverpaid().compareTo(BigDecimal.ZERO) > 0;
|
|
||||||
if (isOverpaid) {
|
|
||||||
// FIXME - kw - update account balance to negative amount.
|
|
||||||
handleLoanOverpayment(loan, transactionDate, loanLifecycleStateMachine);
|
|
||||||
statusChanged = true;
|
|
||||||
} else if (loan.getSummary().isRepaidInFull(loan.getCurrency())) {
|
|
||||||
handleLoanRepaymentInFull(loan, transactionDate, loanLifecycleStateMachine);
|
|
||||||
statusChanged = true;
|
|
||||||
} else {
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan);
|
|
||||||
}
|
|
||||||
if (loan.getTotalOverpaid() == null || BigDecimal.ZERO.compareTo(loan.getTotalOverpaid()) == 0) {
|
|
||||||
loan.setOverpaidOnDate(null);
|
|
||||||
}
|
|
||||||
return statusChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO check
|
|
||||||
public boolean isChronologicallyTheLatestTransaction(final LoanTransaction loanTransaction,
|
|
||||||
final List<LoanTransaction> loanTransactions) {
|
|
||||||
boolean isChronologicallyLatestRepaymentOrWaiver = true;
|
|
||||||
|
|
||||||
final LocalDate currentTransactionDate = loanTransaction.getTransactionDate();
|
|
||||||
for (final LoanTransaction previousTransaction : loanTransactions) {
|
|
||||||
if (!previousTransaction.isDisbursement() && previousTransaction.isNotReversed()
|
|
||||||
&& (DateUtils.isBefore(currentTransactionDate, previousTransaction.getTransactionDate())
|
|
||||||
|| (DateUtils.isEqual(currentTransactionDate, previousTransaction.getTransactionDate())
|
|
||||||
&& ((loanTransaction.getId() == null && previousTransaction.getId() == null)
|
|
||||||
|| (loanTransaction.getId() != null && (previousTransaction.getId() == null
|
|
||||||
|| loanTransaction.getId().compareTo(previousTransaction.getId()) < 0)))))) {
|
|
||||||
isChronologicallyLatestRepaymentOrWaiver = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isChronologicallyLatestRepaymentOrWaiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recalculateLoanWithInterestPaymentWaiverTxn(Loan loan, LoanTransaction newInterestPaymentWaiverTransaction) {
|
private void recalculateLoanWithInterestPaymentWaiverTxn(Loan loan, LoanTransaction newInterestPaymentWaiverTransaction) {
|
||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = null;
|
||||||
LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
|
LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
|
||||||
@@ -1078,9 +1007,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
reprocessChangedLoanTransactions(loan, scheduleGeneratorDTO);
|
reprocessChangedLoanTransactions(loan, scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
|
||||||
|
|
||||||
doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reprocessChangedLoanTransactions(Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
private void reprocessChangedLoanTransactions(Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
@@ -1411,7 +1338,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
LoanTransactionRelationTypeEnum.CHARGEBACK);
|
LoanTransactionRelationTypeEnum.CHARGEBACK);
|
||||||
this.loanTransactionRelationRepository.save(loanTransactionRelation);
|
this.loanTransactionRelationRepository.save(loanTransactionRelation);
|
||||||
|
|
||||||
handleChargebackTransaction(loan, newTransaction, loanLifecycleStateMachine);
|
handleChargebackTransaction(loan, newTransaction);
|
||||||
|
|
||||||
newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction);
|
newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction);
|
||||||
|
|
||||||
@@ -1502,8 +1429,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||||
waiveInterestTransaction.getTransactionDate());
|
waiveInterestTransaction.getTransactionDate());
|
||||||
waiveInterest(loan, waiveInterestTransaction, loanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds,
|
waiveInterest(loan, waiveInterestTransaction, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
scheduleGeneratorDTO);
|
|
||||||
|
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
@@ -1598,8 +1524,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.withLoanId(loanId) //
|
.withLoanId(loanId) //
|
||||||
.with(changes);
|
.with(changes);
|
||||||
|
|
||||||
final Optional<LoanTransaction> loanTransactionOptional = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
|
final Optional<LoanTransaction> loanTransactionOptional = closeAsWrittenOff(loan, command, changes, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||||
|
|
||||||
if (loanTransactionOptional.isPresent()) {
|
if (loanTransactionOptional.isPresent()) {
|
||||||
final LoanTransaction loanTransaction = loanTransactionOptional.get();
|
final LoanTransaction loanTransaction = loanTransactionOptional.get();
|
||||||
@@ -1666,8 +1592,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
||||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CLOSED);
|
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CLOSED);
|
||||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.REPAID_IN_FULL, closureDate);
|
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.REPAID_IN_FULL, closureDate);
|
||||||
final Optional<LoanTransaction> loanTransactionOptional = close(loan, command, loanLifecycleStateMachine, changes,
|
final Optional<LoanTransaction> loanTransactionOptional = close(loan, command, changes, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
@@ -1747,7 +1673,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
changes.put("locale", command.locale());
|
changes.put("locale", command.locale());
|
||||||
changes.put("dateFormat", command.dateFormat());
|
changes.put("dateFormat", command.dateFormat());
|
||||||
|
|
||||||
closeAsMarkedForReschedule(loan, command, loanLifecycleStateMachine, changes);
|
closeAsMarkedForReschedule(loan, command, changes);
|
||||||
|
|
||||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
@@ -1816,8 +1742,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public LoanTransaction initiateLoanTransfer(final Loan loan, final LocalDate transferDate) {
|
public LoanTransaction initiateLoanTransfer(final Loan loan, final LocalDate transferDate) {
|
||||||
|
|
||||||
this.loanAssembler.setHelpers(loan);
|
|
||||||
checkClientOrGroupActive(loan);
|
checkClientOrGroupActive(loan);
|
||||||
validateTransactionsForTransfer(loan, transferDate);
|
validateTransactionsForTransfer(loan, transferDate);
|
||||||
|
|
||||||
@@ -1828,7 +1752,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
ExternalId externalId = externalIdFactory.create();
|
ExternalId externalId = externalIdFactory.create();
|
||||||
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId);
|
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId);
|
||||||
loan.addLoanTransaction(newTransferTransaction);
|
loan.addLoanTransaction(newTransferTransaction);
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
|
||||||
|
|
||||||
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
|
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
|
||||||
@@ -1843,7 +1766,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
@Override
|
@Override
|
||||||
public LoanTransaction acceptLoanTransfer(final Loan loan, final LocalDate transferDate, final Office acceptedInOffice,
|
public LoanTransaction acceptLoanTransfer(final Loan loan, final LocalDate transferDate, final Office acceptedInOffice,
|
||||||
final Staff loanOfficer) {
|
final Staff loanOfficer) {
|
||||||
this.loanAssembler.setHelpers(loan);
|
|
||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanAcceptTransferBusinessEvent(loan));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanAcceptTransferBusinessEvent(loan));
|
||||||
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
|
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
|
||||||
@@ -1851,7 +1773,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate,
|
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate,
|
||||||
externalId);
|
externalId);
|
||||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
|
||||||
if (loan.getTotalOverpaid() != null) {
|
if (loan.getTotalOverpaid() != null) {
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
|
||||||
} else {
|
} else {
|
||||||
@@ -1873,7 +1794,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public LoanTransaction withdrawLoanTransfer(final Loan loan, final LocalDate transferDate) {
|
public LoanTransaction withdrawLoanTransfer(final Loan loan, final LocalDate transferDate) {
|
||||||
this.loanAssembler.setHelpers(loan);
|
|
||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanWithdrawTransferBusinessEvent(loan));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanWithdrawTransferBusinessEvent(loan));
|
||||||
|
|
||||||
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
||||||
@@ -1884,7 +1804,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate,
|
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate,
|
||||||
externalId);
|
externalId);
|
||||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
|
||||||
|
|
||||||
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
|
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
|
||||||
@@ -1899,9 +1818,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void rejectLoanTransfer(final Loan loan) {
|
public void rejectLoanTransfer(final Loan loan) {
|
||||||
this.loanAssembler.setHelpers(loan);
|
|
||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
|
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
|
||||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||||
@@ -2281,7 +2198,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
|
||||||
|
|
||||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
||||||
undoWrittenOff(loan, loanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds);
|
undoWrittenOff(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||||
@@ -3079,8 +2996,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction,
|
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction) {
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
|
||||||
loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction);
|
loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction);
|
||||||
|
|
||||||
loan.addLoanTransaction(chargebackTransaction);
|
loan.addLoanTransaction(chargebackTransaction);
|
||||||
@@ -3093,10 +3009,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||||
}
|
}
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, chargebackTransaction.getTransactionDate());
|
||||||
if (!loan.doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), loanLifecycleStateMachine)) {
|
|
||||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGEBACK, loan);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateIsMultiDisbursalLoanAndDisbursedMoreThanOneTranche(Loan loan) {
|
private void validateIsMultiDisbursalLoanAndDisbursedMoreThanOneTranche(Loan loan) {
|
||||||
@@ -3232,8 +3145,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loan.getLoanTransactions().retainAll(retainTransactions);
|
loan.getLoanTransactions().retainAll(retainTransactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<LoanTransaction> closeAsWrittenOff(final Loan loan, final JsonCommand command,
|
private Optional<LoanTransaction> closeAsWrittenOff(final Loan loan, final JsonCommand command, final Map<String, Object> changes,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final AppUser currentUser,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final AppUser currentUser,
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
closeDisbursements(loan, scheduleGeneratorDTO);
|
closeDisbursements(loan, scheduleGeneratorDTO);
|
||||||
@@ -3308,12 +3220,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||||
LocalDate lastLoanTransactionDate = loan.getLatestTransactionDate();
|
LocalDate lastLoanTransactionDate = loan.getLatestTransactionDate();
|
||||||
loan.doPostLoanTransactionChecks(lastLoanTransactionDate, loanLifecycleStateMachine);
|
loanLifecycleStateMachine.determineAndTransition(loan, lastLoanTransactionDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<LoanTransaction> close(final Loan loan, final JsonCommand command,
|
private Optional<LoanTransaction> close(final Loan loan, final JsonCommand command, final Map<String, Object> changes,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
@@ -3458,25 +3369,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
||||||
actualChanges.put("undolastdisbursal", "true");
|
actualChanges.put("undolastdisbursal", "true");
|
||||||
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
||||||
loan.updateLoanSummaryDerivedFields();
|
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||||
|
|
||||||
loan.doPostLoanTransactionChecks(loan.getLastUserTransactionDate(), loanLifecycleStateMachine);
|
|
||||||
|
|
||||||
return actualChanges;
|
return actualChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction,
|
private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction, final List<Long> existingTransactionIds,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
|
||||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
|
|
||||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction,
|
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction, null,
|
||||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void undoWrittenOff(final Loan loan, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
private void undoWrittenOff(final Loan loan, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
final LoanTransaction writeOffTransaction = loan.findWriteOffTransaction();
|
final LoanTransaction writeOffTransaction = loan.findWriteOffTransaction();
|
||||||
@@ -3528,8 +3435,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
* Behaviour added to comply with capability of previous mifos product to support easier transition to fineract
|
* Behaviour added to comply with capability of previous mifos product to support easier transition to fineract
|
||||||
* platform.
|
* platform.
|
||||||
*/
|
*/
|
||||||
public void closeAsMarkedForReschedule(final Loan loan, final JsonCommand command,
|
public void closeAsMarkedForReschedule(final Loan loan, final JsonCommand command, final Map<String, Object> changes) {
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes) {
|
|
||||||
final LocalDate rescheduledOn = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
final LocalDate rescheduledOn = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
||||||
|
|
||||||
loan.setClosedOnDate(rescheduledOn);
|
loan.setClosedOnDate(rescheduledOn);
|
||||||
@@ -3545,4 +3451,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
loanTransactionValidator.validateLoanRescheduleDate(loan);
|
loanTransactionValidator.validateLoanRescheduleDate(loan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canDisburse(final Loan loan) {
|
||||||
|
final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSED, loan);
|
||||||
|
|
||||||
|
boolean isMultiTrancheDisburse = false;
|
||||||
|
LoanStatus actualLoanStatus = loan.getStatus();
|
||||||
|
if ((actualLoanStatus.isActive() || actualLoanStatus.isClosedObligationsMet() || actualLoanStatus.isOverpaid())
|
||||||
|
&& loan.isAllTranchesNotDisbursed()) {
|
||||||
|
isMultiTrancheDisburse = true;
|
||||||
|
}
|
||||||
|
return !statusEnum.hasStateOf(actualLoanStatus) || isMultiTrancheDisburse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -141,8 +141,8 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(),
|
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(),
|
||||||
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||||
|
|
||||||
adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionToAdjust, existingTransactionIds,
|
adjustExistingTransaction(loan, newTransactionDetail, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||||
existingReversedTransactionIds, scheduleGeneratorDTO, reversalTxnExternalId);
|
scheduleGeneratorDTO, reversalTxnExternalId);
|
||||||
|
|
||||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||||
@@ -185,7 +185,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
this.accountTransfersWritePlatformService.reverseTransfersWithFromAccountTransactions(transactionIds,
|
this.accountTransfersWritePlatformService.reverseTransfersWithFromAccountTransactions(transactionIds,
|
||||||
PortfolioAccountType.LOAN);
|
PortfolioAccountType.LOAN);
|
||||||
}
|
}
|
||||||
loan.updateLoanSummaryAndStatus();
|
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||||
|
|
||||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||||
false);
|
false);
|
||||||
@@ -221,9 +221,9 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void adjustExistingTransaction(final Loan loan, final LoanTransaction newTransactionDetail,
|
public void adjustExistingTransaction(final Loan loan, final LoanTransaction newTransactionDetail,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
|
final LoanTransaction transactionForAdjustment, final List<Long> existingTransactionIds,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO,
|
||||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final ExternalId reversalExternalId) {
|
final ExternalId reversalExternalId) {
|
||||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
|
|
||||||
@@ -268,8 +268,8 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
|
if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
|
||||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail,
|
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail, transactionForAdjustment,
|
||||||
loanLifecycleStateMachine, transactionForAdjustment, scheduleGeneratorDTO);
|
scheduleGeneratorDTO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+33
-33
@@ -201,21 +201,19 @@ public class LoanAccountConfiguration {
|
|||||||
public LoanApplicationWritePlatformService loanApplicationWritePlatformService(PlatformSecurityContext context,
|
public LoanApplicationWritePlatformService loanApplicationWritePlatformService(PlatformSecurityContext context,
|
||||||
LoanApplicationTransitionValidator loanApplicationTransitionValidator, LoanApplicationValidator loanApplicationValidator,
|
LoanApplicationTransitionValidator loanApplicationTransitionValidator, LoanApplicationValidator loanApplicationValidator,
|
||||||
LoanRepositoryWrapper loanRepositoryWrapper, NoteRepository noteRepository, LoanAssembler loanAssembler,
|
LoanRepositoryWrapper loanRepositoryWrapper, NoteRepository noteRepository, LoanAssembler loanAssembler,
|
||||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
|
||||||
CalendarRepository calendarRepository, CalendarInstanceRepository calendarInstanceRepository,
|
CalendarRepository calendarRepository, CalendarInstanceRepository calendarInstanceRepository,
|
||||||
SavingsAccountRepositoryWrapper savingsAccountRepository, AccountAssociationsRepository accountAssociationsRepository,
|
SavingsAccountRepositoryWrapper savingsAccountRepository, AccountAssociationsRepository accountAssociationsRepository,
|
||||||
BusinessEventNotifierService businessEventNotifierService, LoanScheduleAssembler loanScheduleAssembler,
|
BusinessEventNotifierService businessEventNotifierService, LoanScheduleAssembler loanScheduleAssembler,
|
||||||
LoanUtilService loanUtilService, CalendarReadPlatformService calendarReadPlatformService,
|
LoanUtilService loanUtilService, CalendarReadPlatformService calendarReadPlatformService,
|
||||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository glimRepository,
|
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository glimRepository,
|
||||||
LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
|
LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
|
||||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService) {
|
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService) {
|
||||||
return new LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, loanApplicationTransitionValidator,
|
return new LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, loanApplicationTransitionValidator,
|
||||||
loanApplicationValidator, loanRepositoryWrapper, noteRepository, loanAssembler,
|
loanApplicationValidator, loanRepositoryWrapper, noteRepository, loanAssembler, calendarRepository,
|
||||||
loanRepaymentScheduleTransactionProcessorFactory, calendarRepository, calendarInstanceRepository, savingsAccountRepository,
|
calendarInstanceRepository, savingsAccountRepository, accountAssociationsRepository, businessEventNotifierService,
|
||||||
accountAssociationsRepository, businessEventNotifierService, loanScheduleAssembler, loanUtilService,
|
loanScheduleAssembler, loanUtilService, calendarReadPlatformService, entityDatatableChecksWritePlatformService,
|
||||||
calendarReadPlatformService, entityDatatableChecksWritePlatformService, glimRepository, loanRepository,
|
glimRepository, loanRepository, gsimReadPlatformService, loanLifecycleStateMachine, loanAccrualsProcessingService,
|
||||||
gsimReadPlatformService, defaultLoanLifecycleStateMachine, loanAccrualsProcessingService,
|
|
||||||
loanDownPaymentTransactionValidator, loanScheduleService);
|
loanDownPaymentTransactionValidator, loanScheduleService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +233,7 @@ public class LoanAccountConfiguration {
|
|||||||
LoanCollateralAssembler collateralAssembler,
|
LoanCollateralAssembler collateralAssembler,
|
||||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||||
HolidayRepository holidayRepository, ConfigurationDomainService configurationDomainService,
|
HolidayRepository holidayRepository, ConfigurationDomainService configurationDomainService,
|
||||||
WorkingDaysRepositoryWrapper workingDaysRepository, RateAssembler rateAssembler,
|
WorkingDaysRepositoryWrapper workingDaysRepository, RateAssembler rateAssembler, ExternalIdFactory externalIdFactory,
|
||||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, ExternalIdFactory externalIdFactory,
|
|
||||||
AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, GLIMAccountInfoRepository glimRepository,
|
AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, GLIMAccountInfoRepository glimRepository,
|
||||||
AccountNumberGenerator accountNumberGenerator, GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService,
|
AccountNumberGenerator accountNumberGenerator, GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService,
|
||||||
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||||
@@ -247,10 +244,10 @@ public class LoanAccountConfiguration {
|
|||||||
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
||||||
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
||||||
loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository,
|
loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository,
|
||||||
rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository,
|
rateAssembler, externalIdFactory, accountNumberFormatRepository, glimRepository, accountNumberGenerator,
|
||||||
accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService,
|
glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService, loanDisbursementDetailsAssembler,
|
||||||
loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService,
|
loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService, loanDisbursementService, loanChargeService,
|
||||||
loanDisbursementService, loanChargeService, loanOfficerService, loanSchedule);
|
loanOfficerService, loanSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -293,7 +290,7 @@ public class LoanAccountConfiguration {
|
|||||||
AccountTransfersWritePlatformService accountTransfersWritePlatformService, LoanRepositoryWrapper loanRepositoryWrapper,
|
AccountTransfersWritePlatformService accountTransfersWritePlatformService, LoanRepositoryWrapper loanRepositoryWrapper,
|
||||||
JournalEntryWritePlatformService journalEntryWritePlatformService, LoanAccountDomainService loanAccountDomainService,
|
JournalEntryWritePlatformService journalEntryWritePlatformService, LoanAccountDomainService loanAccountDomainService,
|
||||||
LoanChargeRepository loanChargeRepository, LoanWritePlatformService loanWritePlatformService, LoanUtilService loanUtilService,
|
LoanChargeRepository loanChargeRepository, LoanWritePlatformService loanWritePlatformService, LoanUtilService loanUtilService,
|
||||||
LoanChargeReadPlatformService loanChargeReadPlatformService, LoanLifecycleStateMachine defaultLoanLifecycleStateMachine,
|
LoanChargeReadPlatformService loanChargeReadPlatformService, LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, FromJsonHelper fromApiJsonHelper,
|
AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, FromJsonHelper fromApiJsonHelper,
|
||||||
ConfigurationDomainService configurationDomainService,
|
ConfigurationDomainService configurationDomainService,
|
||||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||||
@@ -304,16 +301,16 @@ public class LoanAccountConfiguration {
|
|||||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanChargeValidator loanChargeValidator,
|
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanChargeValidator loanChargeValidator,
|
||||||
LoanScheduleService loanScheduleService, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
LoanScheduleService loanScheduleService, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||||
LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService,
|
LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService,
|
||||||
LoanAccountingBridgeMapper loanAccountingBridgeMapper) {
|
LoanAccountingBridgeMapper loanAccountingBridgeMapper, LoanChargeService loanChargeService) {
|
||||||
return new LoanChargeWritePlatformServiceImpl(loanChargeApiJsonValidator, loanAssembler, chargeRepository,
|
return new LoanChargeWritePlatformServiceImpl(loanChargeApiJsonValidator, loanAssembler, chargeRepository,
|
||||||
businessEventNotifierService, loanTransactionRepository, accountTransfersWritePlatformService, loanRepositoryWrapper,
|
businessEventNotifierService, loanTransactionRepository, accountTransfersWritePlatformService, loanRepositoryWrapper,
|
||||||
journalEntryWritePlatformService, loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService,
|
journalEntryWritePlatformService, loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService,
|
||||||
loanChargeReadPlatformService, defaultLoanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper,
|
loanChargeReadPlatformService, loanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper,
|
||||||
configurationDomainService, loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory,
|
configurationDomainService, loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory,
|
||||||
accountTransferDetailRepository, loanChargeAssembler, paymentDetailWritePlatformService, noteRepository,
|
accountTransferDetailRepository, loanChargeAssembler, paymentDetailWritePlatformService, noteRepository,
|
||||||
loanAccrualTransactionBusinessEventService, loanAccrualsProcessingService, loanDownPaymentTransactionValidator,
|
loanAccrualTransactionBusinessEventService, loanAccrualsProcessingService, loanDownPaymentTransactionValidator,
|
||||||
loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService, loanAdjustmentService,
|
loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService, loanAdjustmentService,
|
||||||
loanAccountingBridgeMapper);
|
loanAccountingBridgeMapper, loanChargeService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -395,7 +392,7 @@ public class LoanAccountConfiguration {
|
|||||||
RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler,
|
RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler,
|
||||||
PostDatedChecksRepository postDatedChecksRepository,
|
PostDatedChecksRepository postDatedChecksRepository,
|
||||||
LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository,
|
LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository,
|
||||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
||||||
ExternalIdFactory externalIdFactory, LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService,
|
ExternalIdFactory externalIdFactory, LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService,
|
||||||
ErrorHandler errorHandler, LoanDownPaymentHandlerService loanDownPaymentHandlerService,
|
ErrorHandler errorHandler, LoanDownPaymentHandlerService loanDownPaymentHandlerService,
|
||||||
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||||
@@ -415,12 +412,12 @@ public class LoanAccountConfiguration {
|
|||||||
loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
|
loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
|
||||||
guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, codeValueRepository,
|
guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, codeValueRepository,
|
||||||
cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||||
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
|
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, loanLifecycleStateMachine, loanAccountLockService,
|
||||||
loanAccountLockService, externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler,
|
externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService,
|
||||||
loanDownPaymentHandlerService, loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator,
|
loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator,
|
||||||
loanDownPaymentTransactionValidator, loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService,
|
loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService, reprocessLoanTransactionsService,
|
||||||
reprocessLoanTransactionsService, loanAccountService, journalEntryPoster, loanAdjustmentService, loanAccountingBridgeMapper,
|
loanAccountService, journalEntryPoster, loanAdjustmentService, loanAccountingBridgeMapper, loanMapper,
|
||||||
loanMapper, loanTransactionProcessingService);
|
loanTransactionProcessingService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -437,10 +434,10 @@ public class LoanAccountConfiguration {
|
|||||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService,
|
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService,
|
||||||
LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator,
|
LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator,
|
||||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
LoanTransactionProcessingService loanTransactionProcessingService, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
||||||
reprocessLoanTransactionsService, loanTransactionProcessingService);
|
reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -452,15 +449,17 @@ public class LoanAccountConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
||||||
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
||||||
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService);
|
LoanChargeService loanChargeService) {
|
||||||
|
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService,
|
||||||
|
loanChargeService);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(LoanChargeService.class)
|
@ConditionalOnMissingBean(LoanChargeService.class)
|
||||||
public LoanChargeService loanChargeService(LoanChargeValidator loanChargeValidator,
|
public LoanChargeService loanChargeService(LoanChargeValidator loanChargeValidator,
|
||||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
LoanTransactionProcessingService loanTransactionProcessingService, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService);
|
return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -480,9 +479,10 @@ public class LoanAccountConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(LoanRefundService.class)
|
@ConditionalOnMissingBean(LoanRefundService.class)
|
||||||
public LoanRefundService loanRefundService(LoanRefundValidator loanRefundValidator,
|
public LoanRefundService loanRefundService(final LoanRefundValidator loanRefundValidator,
|
||||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
final LoanTransactionProcessingService loanTransactionProcessingService,
|
||||||
return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService);
|
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+4
-4
@@ -241,8 +241,8 @@ class LoanAdjustmentServiceImplTest {
|
|||||||
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
|
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
underTest.adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment,
|
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Verify that related transaction is reversed and event is triggered
|
// Verify that related transaction is reversed and event is triggered
|
||||||
@@ -286,8 +286,8 @@ class LoanAdjustmentServiceImplTest {
|
|||||||
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
|
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
underTest.adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment,
|
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds,
|
||||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Verify that no related transactions are reversed
|
// Verify that no related transactions are reversed
|
||||||
|
|||||||
+42
-1
@@ -30,8 +30,11 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||||
@@ -41,7 +44,9 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
|||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
|
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||||
|
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO;
|
||||||
@@ -50,9 +55,12 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper;
|
||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator;
|
||||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||||
@@ -150,6 +158,18 @@ class LoanChargeWritePlatformServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
private LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoanChargeService loanChargeService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ChargeCalculationType chargeCalculationType;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SingleLoanChargeRepaymentScheduleProcessingWrapper wrapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||||
@@ -158,8 +178,13 @@ class LoanChargeWritePlatformServiceImplTest {
|
|||||||
when(chargeDefinition.getCurrencyCode()).thenReturn(CURRENCY_CODE);
|
when(chargeDefinition.getCurrencyCode()).thenReturn(CURRENCY_CODE);
|
||||||
when(loanChargeAssembler.createNewFromJson(loan, chargeDefinition, jsonCommand)).thenReturn(loanCharge);
|
when(loanChargeAssembler.createNewFromJson(loan, chargeDefinition, jsonCommand)).thenReturn(loanCharge);
|
||||||
when(loan.repaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
|
when(loan.repaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
|
||||||
|
when(loanRepaymentScheduleDetail.getLoanScheduleType()).thenReturn(LoanScheduleType.CUMULATIVE);
|
||||||
|
when(loan.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
|
||||||
when(loan.hasCurrencyCodeOf(CURRENCY_CODE)).thenReturn(true);
|
when(loan.hasCurrencyCodeOf(CURRENCY_CODE)).thenReturn(true);
|
||||||
when(loanCharge.getChargePaymentMode()).thenReturn(ChargePaymentMode.REGULAR);
|
when(loanCharge.getChargePaymentMode()).thenReturn(ChargePaymentMode.REGULAR);
|
||||||
|
when(loanCharge.getChargeCalculation()).thenReturn(chargeCalculationType);
|
||||||
|
when(chargeCalculationType.isPercentageBased()).thenReturn(false);
|
||||||
|
when(loanCharge.amountOrPercentage()).thenReturn(BigDecimal.TEN);
|
||||||
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
|
||||||
when(loanChargeRepository.saveAndFlush(any(LoanCharge.class))).thenReturn(loanCharge);
|
when(loanChargeRepository.saveAndFlush(any(LoanCharge.class))).thenReturn(loanCharge);
|
||||||
when(loan.getCurrency()).thenReturn(monetaryCurrency);
|
when(loan.getCurrency()).thenReturn(monetaryCurrency);
|
||||||
@@ -170,12 +195,23 @@ class LoanChargeWritePlatformServiceImplTest {
|
|||||||
when(loan.findExistingTransactionIds()).thenReturn(existingTransactionIds);
|
when(loan.findExistingTransactionIds()).thenReturn(existingTransactionIds);
|
||||||
when(loan.findExistingReversedTransactionIds()).thenReturn(existingReversedTransactionIds);
|
when(loan.findExistingReversedTransactionIds()).thenReturn(existingReversedTransactionIds);
|
||||||
|
|
||||||
|
when(loan.getLoanCharges()).thenReturn(new HashSet<>());
|
||||||
|
when(loan.getDisbursementDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
|
||||||
|
when(loan.getRepaymentScheduleInstallments()).thenReturn(new ArrayList<>());
|
||||||
|
when(loan.calculateAmountPercentageAppliedTo(any(LoanCharge.class))).thenReturn(BigDecimal.TEN);
|
||||||
|
when(loan.fetchNumberOfInstallmensAfterExceptions()).thenReturn(5);
|
||||||
|
when(loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(any(BigDecimal.class))).thenReturn(null);
|
||||||
|
when(loan.deriveSumTotalOfChargesDueAtDisbursement()).thenReturn(BigDecimal.ZERO);
|
||||||
|
when(loanCharge.getDueLocalDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
|
||||||
|
when(loanCharge.getEffectiveDueDate()).thenReturn(LocalDate.now(ZoneId.systemDefault()));
|
||||||
|
|
||||||
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||||
when(loan.isCashBasedAccountingEnabledOnLoanProduct()).thenReturn(false);
|
when(loan.isCashBasedAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||||
when(loan.isUpfrontAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
when(loan.isUpfrontAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||||
|
|
||||||
when(loanAccountingBridgeMapper.deriveAccountingBridgeData(anyString(), anyList(), anyList(), anyBoolean(), any(Loan.class))).thenReturn(new AccountingBridgeDataDTO());
|
when(loanAccountingBridgeMapper.deriveAccountingBridgeData(anyString(), anyList(), anyList(), anyBoolean(), any(Loan.class))).thenReturn(new AccountingBridgeDataDTO());
|
||||||
doNothing().when(journalEntryWritePlatformService).createJournalEntriesForLoan(any(AccountingBridgeDataDTO.class));
|
doNothing().when(journalEntryWritePlatformService).createJournalEntriesForLoan(any(AccountingBridgeDataDTO.class));
|
||||||
|
doNothing().when(loanChargeService).addLoanCharge(any(Loan.class), any(LoanCharge.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@@ -189,8 +225,13 @@ class LoanChargeWritePlatformServiceImplTest {
|
|||||||
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(true);
|
when(loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()).thenReturn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (MockedStatic<DateUtils> mockedDateUtils = mockStatic(DateUtils.class)) {
|
try (MockedStatic<DateUtils> mockedDateUtils = mockStatic(DateUtils.class);
|
||||||
|
MockedStatic<MoneyHelper> mockedMoneyHelper = mockStatic(MoneyHelper.class)) {
|
||||||
|
|
||||||
mockedDateUtils.when(DateUtils::getBusinessLocalDate).thenReturn(businessDate);
|
mockedDateUtils.when(DateUtils::getBusinessLocalDate).thenReturn(businessDate);
|
||||||
|
mockedDateUtils.when(() -> DateUtils.isBeforeBusinessDate(any(LocalDate.class))).thenReturn(false);
|
||||||
|
mockedMoneyHelper.when(MoneyHelper::getMathContext).thenReturn(java.math.MathContext.DECIMAL64);
|
||||||
|
mockedMoneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(java.math.RoundingMode.HALF_EVEN);
|
||||||
|
|
||||||
loanChargeWritePlatformService.addLoanCharge(LOAN_ID, jsonCommand);
|
loanChargeWritePlatformService.addLoanCharge(LOAN_ID, jsonCommand);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -45,6 +45,7 @@ import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
@@ -107,13 +108,16 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private LoanTransactionProcessingService loanTransactionProcessingService;
|
private LoanTransactionProcessingService loanTransactionProcessingService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||||
|
|
||||||
private LoanDownPaymentHandlerServiceImpl underTest;
|
private LoanDownPaymentHandlerServiceImpl underTest;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
||||||
reprocessLoanTransactionsService, loanTransactionProcessingService);
|
reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||||
tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);
|
tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user