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;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
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.service.BusinessEventNotifierService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
// TODO: introduce tests for the state machine
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
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 final BusinessEventNotifierService businessEventNotifierService;
|
||||
|
||||
@@ -46,7 +46,29 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
@Override
|
||||
public void transition(final LoanEvent loanEvent, final Loan loan) {
|
||||
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 newStatus = getNextStatus(loanEvent, loan);
|
||||
if (newStatus != null) {
|
||||
@@ -58,7 +80,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
}
|
||||
|
||||
// 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) {
|
||||
case SUBMITTED_AND_PENDING_APPROVAL -> {
|
||||
loan.setApprovedOnDate(null);
|
||||
@@ -276,4 +298,89 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
|
||||
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.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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.fund.domain.Fund;
|
||||
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.loanschedule.domain.LoanApplicationTerms;
|
||||
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.LoanProductRelatedDetail;
|
||||
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.repaymentwithpostdatedchecks.domain.PostDatedChecks;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
@@ -298,6 +294,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
@Column(name = "loan_product_counter")
|
||||
private Integer loanProductCounter;
|
||||
|
||||
@Setter
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||
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)
|
||||
private List<LoanTransaction> loanTransactions = new ArrayList<>();
|
||||
|
||||
@Setter
|
||||
@Embedded
|
||||
private LoanSummary summary;
|
||||
|
||||
@Transient
|
||||
private boolean accountNumberRequiresAutoGeneration;
|
||||
|
||||
@Transient
|
||||
private LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
|
||||
@Setter()
|
||||
@Column(name = "principal_amount_proposed", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal proposedPrincipal;
|
||||
@@ -638,40 +633,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
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
|
||||
* 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) {
|
||||
final LocalDate actualDisbursementDate = null;
|
||||
final LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, actualDisbursementDate,
|
||||
principal, this.netDisbursalAmount, false);
|
||||
final LoanDisbursementDetails disbursementDetails = new LoanDisbursementDetails(expectedDisbursementDate, null, principal,
|
||||
this.netDisbursalAmount, false);
|
||||
disbursementDetails.updateLoan(this);
|
||||
this.disbursementDetails.add(disbursementDetails);
|
||||
return disbursementDetails;
|
||||
@@ -992,11 +952,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLoanSummaryAndStatus() {
|
||||
updateLoanSummaryDerivedFields();
|
||||
doPostLoanTransactionChecks(getLastUserTransactionDate(), loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
private boolean isInterestRecalculationEnabledForProduct() {
|
||||
return this.loanProduct.isInterestRecalculationEnabled();
|
||||
}
|
||||
@@ -1111,35 +1066,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
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() {
|
||||
return getLoanTransactions().stream() //
|
||||
.filter(loanTransaction -> loanTransaction.getId() != null) //
|
||||
@@ -1161,18 +1087,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
.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() {
|
||||
Collection<LoanDisbursementDetails> disbursementDetails = new ArrayList<>();
|
||||
LocalDate date = null;
|
||||
@@ -1304,60 +1218,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
.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) {
|
||||
boolean isChronologicallyLatestRepaymentOrWaiver = true;
|
||||
|
||||
@@ -1852,10 +1712,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
return receivableInterest;
|
||||
}
|
||||
|
||||
public void setHelpers(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
this.loanLifecycleStateMachine = loanLifecycleStateMachine;
|
||||
}
|
||||
|
||||
public boolean isSyncDisbursementWithMeeting() {
|
||||
return this.syncDisbursementWithMeeting != null && this.syncDisbursementWithMeeting;
|
||||
}
|
||||
@@ -2486,10 +2342,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
Money[] receivables = retrieveIncomeOutstandingTillDate(closureDate);
|
||||
Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding());
|
||||
totalPrincipal = totalPrincipal.minus(receivables[3]);
|
||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
|
||||
final LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||
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) {
|
||||
|
||||
+18
@@ -18,9 +18,27 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface LoanLifecycleStateMachine {
|
||||
|
||||
LoanStatus dryTransition(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);
|
||||
|
||||
void setHelpers(Loan loanAccount);
|
||||
|
||||
void accountNumberGeneration(JsonCommand command, Loan loan);
|
||||
|
||||
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.LoanRepaymentScheduleProcessingWrapper;
|
||||
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.TransactionCtx;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
@@ -41,6 +42,7 @@ public class LoanChargeService {
|
||||
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
|
||||
public void recalculateAllCharges(final Loan loan) {
|
||||
Set<LoanCharge> charges = loan.getActiveCharges();
|
||||
@@ -76,9 +78,8 @@ public class LoanChargeService {
|
||||
}
|
||||
}
|
||||
|
||||
public void makeChargePayment(final Loan loan, final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||
public void makeChargePayment(final Loan loan, final Long chargeId, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||
loanChargeValidator.validateChargePaymentNotInFuture(paymentTransaction);
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
@@ -88,11 +89,11 @@ public class LoanChargeService {
|
||||
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,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) {
|
||||
final Integer installmentNumber) {
|
||||
chargesPayment.updateLoan(loan);
|
||||
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge,
|
||||
chargesPayment.getAmount(loan.getCurrency()).getAmount(), installmentNumber);
|
||||
@@ -118,7 +119,41 @@ public class LoanChargeService {
|
||||
new TransactionCtx(loan.getCurrency(), chargePaymentInstallments, loanCharges,
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
loan.doPostLoanTransactionChecks(chargesPayment.getTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, chargesPayment.getTransactionDate());
|
||||
}
|
||||
|
||||
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.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public interface LoanDownPaymentHandlerService {
|
||||
@@ -30,6 +29,5 @@ public interface LoanDownPaymentHandlerService {
|
||||
LoanTransaction disbursementTransaction, Loan loan);
|
||||
|
||||
void handleRepaymentOrRecoveryOrWaiverTransaction(Loan loan, LoanTransaction newTransactionDetail,
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanTransaction transactionForAdjustment,
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO);
|
||||
LoanTransaction transactionForAdjustment, ScheduleGeneratorDTO scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
+6
-7
@@ -61,6 +61,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
private final LoanRefundValidator loanRefundValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
|
||||
@Override
|
||||
public LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command,
|
||||
@@ -77,8 +78,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
|
||||
@Override
|
||||
public void handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final LoanTransaction adjustedTransaction, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
if (loanTransaction.isRecoveryRepayment()) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, loan);
|
||||
}
|
||||
@@ -158,13 +158,13 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
/**
|
||||
* FIXME: Vishwas, skipping post loan transaction checks for Loan recoveries
|
||||
**/
|
||||
if (loanTransaction.isNotRecoveryRepayment()) {
|
||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
|
||||
} else {
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
}
|
||||
|
||||
if (loan.getLoanProduct().isMultiDisburseLoan()) {
|
||||
@@ -223,8 +223,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler
|
||||
loanDownPaymentTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(downPaymentTransaction.getTransactionDate(),
|
||||
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
|
||||
handleRepaymentOrRecoveryOrWaiverTransaction(loan, downPaymentTransaction, loan.getLoanLifecycleStateMachine(), null,
|
||||
scheduleGeneratorDTO);
|
||||
handleRepaymentOrRecoveryOrWaiverTransaction(loan, downPaymentTransaction, null, scheduleGeneratorDTO);
|
||||
return downPaymentTransaction;
|
||||
} else {
|
||||
return null;
|
||||
|
||||
+9
-21
@@ -21,7 +21,6 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
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.LoanEvent;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
@@ -35,9 +34,9 @@ public class LoanRefundService {
|
||||
|
||||
private final LoanRefundValidator loanRefundValidator;
|
||||
private final LoanTransactionProcessingService loadTransactionProcessingService;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
|
||||
public void makeRefund(final Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
public void makeRefund(final Loan loan, final LoanTransaction loanTransaction, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
@@ -49,8 +48,7 @@ public class LoanRefundService {
|
||||
if (loanTransaction.isNotZero()) {
|
||||
loan.addLoanTransaction(loanTransaction);
|
||||
}
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
|
||||
}
|
||||
|
||||
public LocalDate extractTransactionDate(final Loan loan, final LoanTransaction loanTransaction) {
|
||||
@@ -59,18 +57,16 @@ public class LoanRefundService {
|
||||
return loanTransactionDate;
|
||||
}
|
||||
|
||||
public void makeRefundForActiveLoan(final Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
public void makeRefundForActiveLoan(final Loan loan, final LoanTransaction loanTransaction, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
handleRefundTransaction(loan, loanTransaction, loanLifecycleStateMachine);
|
||||
handleRefundTransaction(loan, loanTransaction);
|
||||
}
|
||||
|
||||
public void creditBalanceRefund(final Loan loan, final LoanTransaction newCreditBalanceRefundTransaction,
|
||||
final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
loanRefundValidator.validateCreditBalanceRefund(loan, newCreditBalanceRefundTransaction);
|
||||
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
@@ -78,17 +74,10 @@ public class LoanRefundService {
|
||||
|
||||
loan.getLoanTransactions().add(newCreditBalanceRefundTransaction);
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
if (MathUtil.isEmpty(loan.getTotalOverpaid())) {
|
||||
loan.setOverpaidOnDate(null);
|
||||
loan.setClosedOnDate(newCreditBalanceRefundTransaction.getTransactionDate());
|
||||
defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_CREDIT_BALANCE_REFUND, loan);
|
||||
}
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, newCreditBalanceRefundTransaction.getTransactionDate());
|
||||
}
|
||||
|
||||
private void handleRefundTransaction(final Loan loan, final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
private void handleRefundTransaction(final Loan loan, final LoanTransaction loanTransaction) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, loan);
|
||||
|
||||
loanTransaction.updateLoan(loan);
|
||||
@@ -110,7 +99,6 @@ public class LoanRefundService {
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
loan.doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loanTransaction.getTransactionDate());
|
||||
}
|
||||
}
|
||||
|
||||
+14
-24
@@ -211,12 +211,12 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
@Override
|
||||
@Transactional
|
||||
public void reverseTransfersWithFromAccountType(final Long accountNumber, final PortfolioAccountType accountTypeId) {
|
||||
List<AccountTransferTransaction> acccountTransfers = null;
|
||||
List<AccountTransferTransaction> accountTransfers = null;
|
||||
if (accountTypeId.isLoanAccount()) {
|
||||
acccountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber);
|
||||
accountTransfers = this.accountTransferRepository.findByFromLoanId(accountNumber);
|
||||
}
|
||||
if (acccountTransfers != null && acccountTransfers.size() > 0) {
|
||||
undoTransactions(acccountTransfers);
|
||||
if (accountTransfers != null && !accountTransfers.isEmpty()) {
|
||||
undoTransactions(accountTransfers);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -225,35 +225,31 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
@Transactional
|
||||
public void reverseTransfersWithFromAccountTransactions(final Collection<Long> fromTransactionIds,
|
||||
final PortfolioAccountType accountTypeId) {
|
||||
List<AccountTransferTransaction> acccountTransfers = new ArrayList<>();
|
||||
List<AccountTransferTransaction> accountTransfers = new ArrayList<>();
|
||||
if (accountTypeId.isLoanAccount()) {
|
||||
List<List<Long>> partitions = Lists.partition(fromTransactionIds.stream().toList(),
|
||||
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) {
|
||||
undoTransactions(acccountTransfers);
|
||||
if (!accountTransfers.isEmpty()) {
|
||||
undoTransactions(accountTransfers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void reverseAllTransactions(final Long accountId, final PortfolioAccountType accountTypeId) {
|
||||
List<AccountTransferTransaction> acccountTransfers = null;
|
||||
List<AccountTransferTransaction> accountTransfers = null;
|
||||
if (accountTypeId.isLoanAccount()) {
|
||||
acccountTransfers = this.accountTransferRepository.findAllByLoanId(accountId);
|
||||
accountTransfers = this.accountTransferRepository.findAllByLoanId(accountId);
|
||||
}
|
||||
if (acccountTransfers != null && acccountTransfers.size() > 0) {
|
||||
undoTransactions(acccountTransfers);
|
||||
if (accountTransfers != null && !accountTransfers.isEmpty()) {
|
||||
undoTransactions(accountTransfers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param acccountTransfers
|
||||
*/
|
||||
private void undoTransactions(final List<AccountTransferTransaction> acccountTransfers) {
|
||||
for (final AccountTransferTransaction accountTransfer : acccountTransfers) {
|
||||
private void undoTransactions(final List<AccountTransferTransaction> accountTransfers) {
|
||||
for (final AccountTransferTransaction accountTransfer : accountTransfers) {
|
||||
if (accountTransfer.getFromLoanTransaction() != null) {
|
||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
|
||||
}
|
||||
@@ -298,14 +294,12 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||
} else {
|
||||
toLoanAccount = accountTransferDTO.getLoan();
|
||||
this.loanAccountAssembler.setHelpers(toLoanAccount);
|
||||
}
|
||||
|
||||
} else {
|
||||
fromSavingsAccount = accountTransferDetails.fromSavingsAccount();
|
||||
this.savingsAccountAssembler.setHelpers(fromSavingsAccount);
|
||||
toLoanAccount = accountTransferDetails.toLoanAccount();
|
||||
this.loanAccountAssembler.setHelpers(toLoanAccount);
|
||||
}
|
||||
|
||||
final SavingsTransactionBooleanValues transactionBooleanValues = new SavingsTransactionBooleanValues(isAccountTransfer,
|
||||
@@ -414,12 +408,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
||||
} else {
|
||||
fromLoanAccount = accountTransferDTO.getLoan();
|
||||
this.loanAccountAssembler.setHelpers(fromLoanAccount);
|
||||
}
|
||||
toSavingsAccount = this.savingsAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill);
|
||||
} else {
|
||||
fromLoanAccount = accountTransferDetails.fromLoanAccount();
|
||||
this.loanAccountAssembler.setHelpers(fromLoanAccount);
|
||||
toSavingsAccount = accountTransferDetails.toSavingsAccount();
|
||||
this.savingsAccountAssembler.setHelpers(toSavingsAccount);
|
||||
}
|
||||
@@ -472,14 +464,12 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
|
||||
} else {
|
||||
fromLoanAccount = accountTransferDTO.getFromLoan();
|
||||
this.loanAccountAssembler.setHelpers(fromLoanAccount);
|
||||
}
|
||||
Loan toLoanAccount = null;
|
||||
if (accountTransferDTO.getToLoan() == null) {
|
||||
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||
} else {
|
||||
toLoanAccount = accountTransferDTO.getToLoan();
|
||||
this.loanAccountAssembler.setHelpers(toLoanAccount);
|
||||
}
|
||||
|
||||
ExternalId externalIdForDisbursement = accountTransferDTO.getTxnExternalId();
|
||||
|
||||
+15
-21
@@ -141,7 +141,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final PostDatedChecksRepository postDatedChecksRepository;
|
||||
private final LoanCollateralManagementRepository loanCollateralManagementRepository;
|
||||
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper;
|
||||
@@ -259,8 +259,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanDownPaymentTransactionValidator.validateRepaymentTypeAccountStatus(loan, newRepaymentTransaction, event);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, event,
|
||||
newRepaymentTransaction.getTransactionDate());
|
||||
makeRepayment(loan, newRepaymentTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
makeRepayment(loan, newRepaymentTransaction, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
@@ -421,8 +420,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
LoanEvent.LOAN_CHARGE_PAYMENT);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_CHARGE_PAYMENT,
|
||||
newPaymentTransaction.getTransactionDate());
|
||||
loanChargeService.makeChargePayment(loan, chargeId, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds, newPaymentTransaction, installmentNumber);
|
||||
loanChargeService.makeChargePayment(loan, chargeId, existingTransactionIds, existingReversedTransactionIds,
|
||||
newPaymentTransaction, installmentNumber);
|
||||
}
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -520,8 +519,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newRefundTransaction.getTransactionDate(), workingDays,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
|
||||
loanRefundService.makeRefund(loan, newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
loanRefundService.makeRefund(loan, newRefundTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction);
|
||||
this.loanRepositoryWrapper.saveAndFlush(loan);
|
||||
@@ -655,8 +653,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CREDIT_BALANCE_REFUND);
|
||||
loanTransactionValidator.validateRefundDateIsAfterLastRepayment(loan, newCreditBalanceRefundTransaction.getTransactionDate());
|
||||
|
||||
loanRefundService.creditBalanceRefund(loan, newCreditBalanceRefundTransaction, defaultLoanLifecycleStateMachine,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
loanRefundService.creditBalanceRefund(loan, newCreditBalanceRefundTransaction, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
|
||||
newCreditBalanceRefundTransaction = this.loanTransactionRepository.saveAndFlush(newCreditBalanceRefundTransaction);
|
||||
|
||||
@@ -709,8 +707,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REFUND,
|
||||
newRefundTransaction.getTransactionDate());
|
||||
loanRefundService.makeRefundForActiveLoan(loan, newRefundTransaction, defaultLoanLifecycleStateMachine, existingTransactionIds,
|
||||
existingReversedTransactionIds);
|
||||
loanRefundService.makeRefundForActiveLoan(loan, newRefundTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newRefundTransaction);
|
||||
|
||||
@@ -777,7 +774,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
loanForeclosureValidator.validateForForeclosure(loan, payment.getTransactionDate());
|
||||
}
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_FORECLOSURE);
|
||||
handleForeClosureTransactions(loan, payment, defaultLoanLifecycleStateMachine, scheduleGeneratorDTO);
|
||||
handleForeClosureTransactions(loan, payment, scheduleGeneratorDTO);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
@@ -918,8 +915,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
loan.doPostLoanTransactionChecks(transactionDate, defaultLoanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||
|
||||
switch (loanTransactionType) {
|
||||
case MERCHANT_ISSUED_REFUND -> businessEventNotifierService
|
||||
@@ -1062,21 +1058,19 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loan, repaymentTransaction, "created");
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
|
||||
null, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, null, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
private void handleForeClosureTransactions(final Loan loan, final LoanTransaction repaymentTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
loan.setLoanSubStatus(LoanSubStatus.FORECLOSED);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, loanLifecycleStateMachine,
|
||||
null, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, null, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -160,7 +160,7 @@ public class LoanScheduleAssembler {
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanDisbursementDetailsAssembler loanDisbursementDetailsAssembler;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanDisbursementService loanDisbursementService;
|
||||
private final LoanChargeService loanChargeService;
|
||||
@@ -1479,7 +1479,7 @@ public class LoanScheduleAssembler {
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
|
||||
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()));
|
||||
|
||||
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.domain.Loan;
|
||||
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.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
@@ -63,7 +62,6 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
private final CurrencyReadPlatformService currencyReadPlatformService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanRepositoryWrapper loanRepository;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanTermVariationsMapper loanTermVariationsMapper;
|
||||
|
||||
@Override
|
||||
@@ -204,16 +202,12 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
}
|
||||
|
||||
private LoanApplicationTerms constructLoanApplicationTerms(final Loan loan) {
|
||||
final LocalDate recalculateFrom = null;
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null);
|
||||
return loanTermVariationsMapper.constructLoanApplicationTerms(scheduleGeneratorDTO, loan);
|
||||
}
|
||||
|
||||
private Loan fetchLoan(final Long accountId) {
|
||||
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine);
|
||||
|
||||
return loanAccount;
|
||||
return loanRepository.findOneWithNotFoundDetection(accountId, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-12
@@ -30,6 +30,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
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.ScheduleGeneratorDTO;
|
||||
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.LoanRepaymentScheduleInstallmentRepository;
|
||||
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.schedule.LoanScheduleComponent;
|
||||
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.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
@@ -93,11 +91,11 @@ import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
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 final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||
@@ -258,17 +256,15 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
|
||||
if (rescheduleFromDate != null && endDate != null && emi != null) {
|
||||
LoanTermVariations parent = null;
|
||||
LocalDate rescheduleFromLocDate = rescheduleFromDate;
|
||||
LocalDate endDateLocDate = endDate;
|
||||
final Integer termType = LoanTermVariationType.EMI_AMOUNT.getValue();
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (!DateUtils.isBefore(installment.getDueDate(), rescheduleFromLocDate)
|
||||
&& !DateUtils.isAfter(installment.getDueDate(), endDateLocDate)) {
|
||||
if (!DateUtils.isBefore(installment.getDueDate(), rescheduleFromDate)
|
||||
&& !DateUtils.isAfter(installment.getDueDate(), endDate)) {
|
||||
createLoanTermVariations(loanRescheduleRequest, termType, loan, installment.getDueDate(), installment.getDueDate(),
|
||||
loanRescheduleRequestToTermVariationMappings, isActive, true, emi, parent);
|
||||
}
|
||||
if (DateUtils.isAfter(installment.getDueDate(), endDateLocDate)) {
|
||||
if (DateUtils.isAfter(installment.getDueDate(), endDate)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -430,8 +426,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),
|
||||
loanApplicationTerms.getInterestMethod());
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
|
||||
loan.setHelpers(loanLifecycleStateMachine);
|
||||
final LoanScheduleDTO loanScheduleDTO = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
|
||||
loan, loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
||||
|
||||
@@ -565,7 +559,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
*
|
||||
**/
|
||||
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",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
+7
-10
@@ -199,7 +199,7 @@ public final class LoanApplicationValidator {
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService;
|
||||
@@ -799,7 +799,6 @@ public final class LoanApplicationValidator {
|
||||
|
||||
private void fixedLengthValidations(final JsonElement element) {
|
||||
validateOrThrow("loan", baseDataValidator -> {
|
||||
boolean isInterestBearing = false;
|
||||
final String transactionProcessingStrategy = this.fromApiJsonHelper
|
||||
.extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName, element);
|
||||
final Integer numberOfRepayments = this.fromApiJsonHelper
|
||||
@@ -809,7 +808,7 @@ public final class LoanApplicationValidator {
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper
|
||||
.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,
|
||||
repaymentEvery, element, baseDataValidator);
|
||||
});
|
||||
@@ -1267,7 +1266,7 @@ public final class LoanApplicationValidator {
|
||||
final Set<String> supportedParameters = new HashSet<>(Arrays.asList(LoanApiConstants.idParameterName,
|
||||
LoanApiConstants.clientCollateralIdParameterName, LoanApiConstants.quantityParameterName));
|
||||
final JsonArray array = topLevelJsonElement.get(LoanApiConstants.collateralParameterName).getAsJsonArray();
|
||||
if (array.size() > 0) {
|
||||
if (!array.isEmpty()) {
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
for (int i = 1; i <= array.size(); i++) {
|
||||
final JsonObject collateralItemElement = array.get(i - 1).getAsJsonObject();
|
||||
@@ -1688,7 +1687,7 @@ public final class LoanApplicationValidator {
|
||||
final JsonArray variationArray = this.fromApiJsonHelper.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName,
|
||||
element);
|
||||
List<LocalDate> expectedDisbursementDates = new ArrayList<>();
|
||||
if (variationArray != null && variationArray.size() > 0) {
|
||||
if (variationArray != null && !variationArray.isEmpty()) {
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isEqualAmortizationParam, element)) {
|
||||
boolean isEqualAmortization = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isEqualAmortizationParam,
|
||||
element);
|
||||
@@ -1890,15 +1889,14 @@ public final class LoanApplicationValidator {
|
||||
? this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.expectedDisbursementDateParameterName, element)
|
||||
: originalExpectedDisbursementDate;
|
||||
|
||||
String defaultUserMessage = "";
|
||||
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,
|
||||
submittedOnDate.toString(), startDate.toString());
|
||||
}
|
||||
|
||||
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,
|
||||
submittedOnDate.toString(), closeDate.toString());
|
||||
}
|
||||
@@ -2024,7 +2022,6 @@ public final class LoanApplicationValidator {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
|
||||
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
loan.setHelpers(defaultLoanLifecycleStateMachine);
|
||||
|
||||
final Client client = loan.client();
|
||||
if (client != null && client.isNotActive()) {
|
||||
@@ -2116,7 +2113,7 @@ public final class LoanApplicationValidator {
|
||||
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())) {
|
||||
final String defaultUserMessage = "Loan is already approved.";
|
||||
final ApiParameterError error = ApiParameterError
|
||||
|
||||
+40
-25
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
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 com.google.gson.JsonArray;
|
||||
@@ -29,6 +31,7 @@ import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.LoanEvent;
|
||||
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.LoanRepositoryWrapper;
|
||||
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.loanproduct.LoanProductConstants;
|
||||
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.NoteRepository;
|
||||
import org.apache.fineract.portfolio.savings.data.GroupSavingsIndividualMonitoringAccountData;
|
||||
@@ -108,7 +111,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final NoteRepository noteRepository;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final CalendarRepository calendarRepository;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
||||
@@ -121,7 +123,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
private final GLIMAccountInfoRepository glimRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
private final GSIMReadPlatformService gsimReadPlatformService;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final LoanAccrualsProcessingService loanAccrualsProcessingService;
|
||||
private final LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator;
|
||||
private final LoanScheduleService loanScheduleService;
|
||||
@@ -505,9 +507,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult approveGLIMLoanAppication(final Long loanId, final JsonCommand command) {
|
||||
|
||||
final Long parentLoanId = loanId;
|
||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(parentLoanId).orElseThrow();
|
||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(loanId).orElseThrow();
|
||||
JsonArray approvalFormData = command.arrayOfParameterNamed("approvalFormData");
|
||||
|
||||
JsonObject jsonObject = null;
|
||||
@@ -579,11 +579,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult undoGLIMLoanApplicationApproval(final Long loanId, final JsonCommand command) {
|
||||
|
||||
// GroupLoanIndividualMonitoringAccount
|
||||
// glimAccount=glimRepository.findOne(loanId);
|
||||
final Long parentLoanId = loanId;
|
||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(parentLoanId).orElseThrow();
|
||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(loanId).orElseThrow();
|
||||
List<Loan> childLoans = this.loanRepository.findByGlimId(loanId);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
@@ -610,19 +606,16 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult undoApplicationApproval(final Long loanId, final JsonCommand command) {
|
||||
|
||||
this.loanApplicationValidator.validateForUndo(command.json());
|
||||
|
||||
Loan loan = retrieveLoanBy(loanId);
|
||||
loanApplicationTransitionValidator.checkClientOrGroupActive(loan);
|
||||
|
||||
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 loan approved amount is not same as loan amount demanded, then
|
||||
// during undo, restore the demand amount to principal amount.
|
||||
|
||||
if (changes.containsKey(LoanApiConstants.approvedLoanAmountParameterName)
|
||||
|| changes.containsKey(LoanApiConstants.disbursementPrincipalParameterName)) {
|
||||
LocalDate recalculateFrom = null;
|
||||
@@ -655,11 +648,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult rejectGLIMApplicationApproval(final Long glimId, final JsonCommand command) {
|
||||
|
||||
// GroupLoanIndividualMonitoringAccount
|
||||
// glimAccount=glimRepository.findOne(loanId);
|
||||
final Long parentLoanId = glimId;
|
||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(parentLoanId).orElseThrow();
|
||||
GroupLoanIndividualMonitoringAccount parentLoan = glimRepository.findById(glimId).orElseThrow();
|
||||
List<Loan> childLoans = this.loanRepository.findByGlimId(glimId);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
@@ -699,7 +688,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
// loan application rejection
|
||||
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);
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
@@ -737,7 +726,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
// loan application withdrawal
|
||||
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);
|
||||
// Release attached collaterals
|
||||
if (loan.getLoanType().isIndividualAccount()) {
|
||||
@@ -764,9 +753,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
}
|
||||
|
||||
private Loan retrieveLoanBy(final Long loanId) {
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
loan.setHelpers(defaultLoanLifecycleStateMachine);
|
||||
return loan;
|
||||
return loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
|
||||
}
|
||||
|
||||
private AppUser getAppUserIfPresent() {
|
||||
@@ -862,4 +849,32 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
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.LoanCreditAllocationRule;
|
||||
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.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
@@ -123,7 +122,6 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final RateAssembler rateAssembler;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository;
|
||||
private final GLIMAccountInfoRepository glimRepository;
|
||||
@@ -147,31 +145,17 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
||||
|
||||
@Override
|
||||
public Loan assembleFrom(final Long accountId, final boolean loadLazyCollections) {
|
||||
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(accountId, loadLazyCollections);
|
||||
setHelpers(loanAccount);
|
||||
|
||||
return loanAccount;
|
||||
return loanRepository.findOneWithNotFoundDetection(accountId, loadLazyCollections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan assembleFrom(final ExternalId externalId) {
|
||||
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(externalId, true);
|
||||
setHelpers(loanAccount);
|
||||
|
||||
return loanAccount;
|
||||
return loanRepository.findOneWithNotFoundDetection(externalId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loan assembleFrom(final ExternalId externalId, final boolean loadLazyCollections) {
|
||||
final Loan loanAccount = loanRepository.findOneWithNotFoundDetection(externalId, loadLazyCollections);
|
||||
setHelpers(loanAccount);
|
||||
|
||||
return loanAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelpers(final Loan loanAccount) {
|
||||
loanAccount.setHelpers(defaultLoanLifecycleStateMachine);
|
||||
return loanRepository.findOneWithNotFoundDetection(externalId, loadLazyCollections);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -298,7 +282,6 @@ public class LoanAssemblerImpl implements LoanAssembler {
|
||||
loanSchedule.updateLoanSchedule(loanApplication, loanScheduleModel);
|
||||
|
||||
copyAdvancedPaymentRulesIfApplicable(transactionProcessingStrategyCode, loanProduct, loanApplication);
|
||||
loanApplication.setHelpers(defaultLoanLifecycleStateMachine);
|
||||
// TODO: review
|
||||
loanChargeService.recalculateAllCharges(loanApplication);
|
||||
topUpLoanConfiguration(element, loanApplication);
|
||||
|
||||
+9
-13
@@ -162,7 +162,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final LoanChargeReadPlatformService loanChargeReadPlatformService;
|
||||
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
|
||||
private final LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
@@ -181,6 +181,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
private final LoanAccountService loanAccountService;
|
||||
private final LoanAdjustmentService loanAdjustmentService;
|
||||
private final LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
private final LoanChargeService loanChargeService;
|
||||
|
||||
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
|
||||
return DateUtils.isAfter(loanCharge.getDueDate(), e.getFromDate()) && !DateUtils.isAfter(loanCharge.getDueDate(), e.getDueDate());
|
||||
@@ -305,7 +306,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
} else {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
loan.doPostLoanTransactionChecks(transactionDate, loan.getLoanLifecycleStateMachine());
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, transactionDate);
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
}
|
||||
|
||||
@@ -571,15 +572,13 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
|
||||
loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge);
|
||||
final LoanTransaction waiveTransaction = waiveLoanCharge(loan, loanCharge, defaultLoanLifecycleStateMachine, changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge,
|
||||
externalId);
|
||||
final LoanTransaction waiveTransaction = waiveLoanCharge(loan, loanCharge, changes, existingTransactionIds,
|
||||
existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge, externalId);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()
|
||||
&& DateUtils.isBefore(loanCharge.getDueLocalDate(), DateUtils.getBusinessLocalDate())) {
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
|
||||
}
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(waiveTransaction);
|
||||
@@ -928,7 +927,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
}
|
||||
loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(loanChargeAdjustmentTransaction);
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||
|
||||
loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
@@ -1109,7 +1108,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
|
||||
loanChargeValidator.validateChargeAdditionForDisbursedLoan(loan, loanCharge);
|
||||
loanChargeValidator.validateChargeHasValidSpecifiedDateIfApplicable(loan, loanCharge, loan.getDisbursementDate());
|
||||
loan.addLoanCharge(loanCharge);
|
||||
loanChargeService.addLoanCharge(loan, 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
|
||||
@@ -1433,8 +1432,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
}
|
||||
}
|
||||
|
||||
public LoanTransaction waiveLoanCharge(final Loan loan, final LoanCharge loanCharge,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
||||
public LoanTransaction waiveLoanCharge(final Loan loan, final LoanCharge loanCharge, final Map<String, Object> changes,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final Integer loanInstallmentNumber,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge, final ExternalId externalId) {
|
||||
final Money amountWaived = loanCharge.waive(loan.getCurrency(), loanInstallmentNumber);
|
||||
@@ -1512,9 +1510,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
loan.getActiveCharges());
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
loan.doPostLoanTransactionChecks(waiveLoanChargeTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, waiveLoanChargeTransaction.getTransactionDate());
|
||||
|
||||
return waiveLoanChargeTransaction;
|
||||
}
|
||||
|
||||
+2
-1
@@ -62,6 +62,7 @@ public class LoanDisbursementService {
|
||||
private final LoanChargeValidator loanChargeValidator;
|
||||
private final LoanDisbursementValidator loanDisbursementValidator;
|
||||
private final ReprocessLoanTransactionsService reprocessLoanTransactionsService;
|
||||
private final LoanChargeService loanChargeService;
|
||||
|
||||
public void updateDisbursementDetails(final Loan loan, final JsonCommand jsonCommand, final Map<String, Object> actualChanges) {
|
||||
final List<Long> disbursementList = loan.fetchDisbursementIds();
|
||||
@@ -251,7 +252,7 @@ public class LoanDisbursementService {
|
||||
|
||||
loanChargeValidator.validateChargeAdditionForDisbursedLoan(loan, loanCharge);
|
||||
loanChargeValidator.validateChargeHasValidSpecifiedDateIfApplicable(loan, loanCharge, loan.getDisbursementDate());
|
||||
loan.addLoanCharge(loanCharge);
|
||||
loanChargeService.addLoanCharge(loan, loanCharge);
|
||||
}
|
||||
actualChanges.put(LoanApiConstants.disbursementDataParameterName, expectedDisbursementDate + "-" + principal);
|
||||
actualChanges.put(RECALCULATE_LOAN_SCHEDULE, true);
|
||||
|
||||
+34
-116
@@ -368,7 +368,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Locale locale = command.extractLocale();
|
||||
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
|
||||
|
||||
if (loan.canDisburse()) {
|
||||
if (canDisburse(loan)) {
|
||||
// Get netDisbursalAmount from disbursal screen field.
|
||||
final BigDecimal netDisbursalAmount = command
|
||||
.bigDecimalValueOfParameterNamed(LoanApiConstants.disbursementNetDisbursalAmountParameterName);
|
||||
@@ -756,8 +756,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
// disbursement and actual disbursement happens on same date
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_DISBURSED);
|
||||
updateLoanCounters(loan, actualDisbursementDate);
|
||||
boolean canDisburse = loan.canDisburse();
|
||||
if (canDisburse) {
|
||||
if (canDisburse(loan)) {
|
||||
Money amountBeforeAdjust = loan.getPrincipal();
|
||||
Money disburseAmount = loanDisbursementService.adjustDisburseAmount(loan, command, actualDisbursementDate);
|
||||
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincipal());
|
||||
@@ -960,76 +959,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
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) {
|
||||
LocalDate recalculateFrom = null;
|
||||
LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
|
||||
@@ -1078,9 +1007,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
reprocessChangedLoanTransactions(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
doPostLoanTransactionChecks(loan, newInterestPaymentWaiverTransaction.getTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
|
||||
}
|
||||
|
||||
private void reprocessChangedLoanTransactions(Loan loan, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
@@ -1411,7 +1338,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LoanTransactionRelationTypeEnum.CHARGEBACK);
|
||||
this.loanTransactionRelationRepository.save(loanTransactionRelation);
|
||||
|
||||
handleChargebackTransaction(loan, newTransaction, loanLifecycleStateMachine);
|
||||
handleChargebackTransaction(loan, newTransaction);
|
||||
|
||||
newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction);
|
||||
|
||||
@@ -1502,8 +1429,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||
waiveInterestTransaction.getTransactionDate());
|
||||
waiveInterest(loan, waiveInterestTransaction, loanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds,
|
||||
scheduleGeneratorDTO);
|
||||
waiveInterest(loan, waiveInterestTransaction, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
@@ -1598,8 +1524,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.withLoanId(loanId) //
|
||||
.with(changes);
|
||||
|
||||
final Optional<LoanTransaction> loanTransactionOptional = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||
final Optional<LoanTransaction> loanTransactionOptional = closeAsWrittenOff(loan, command, changes, existingTransactionIds,
|
||||
existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||
|
||||
if (loanTransactionOptional.isPresent()) {
|
||||
final LoanTransaction loanTransaction = loanTransactionOptional.get();
|
||||
@@ -1666,8 +1592,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CLOSED);
|
||||
loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.REPAID_IN_FULL, closureDate);
|
||||
final Optional<LoanTransaction> loanTransactionOptional = close(loan, command, loanLifecycleStateMachine, changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
final Optional<LoanTransaction> loanTransactionOptional = close(loan, command, changes, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
@@ -1747,7 +1673,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
changes.put("locale", command.locale());
|
||||
changes.put("dateFormat", command.dateFormat());
|
||||
|
||||
closeAsMarkedForReschedule(loan, command, loanLifecycleStateMachine, changes);
|
||||
closeAsMarkedForReschedule(loan, command, changes);
|
||||
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
@@ -1816,8 +1742,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Transactional
|
||||
@Override
|
||||
public LoanTransaction initiateLoanTransfer(final Loan loan, final LocalDate transferDate) {
|
||||
|
||||
this.loanAssembler.setHelpers(loan);
|
||||
checkClientOrGroupActive(loan);
|
||||
validateTransactionsForTransfer(loan, transferDate);
|
||||
|
||||
@@ -1828,7 +1752,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
ExternalId externalId = externalIdFactory.create();
|
||||
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId);
|
||||
loan.addLoanTransaction(newTransferTransaction);
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
|
||||
@@ -1843,7 +1766,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Override
|
||||
public LoanTransaction acceptLoanTransfer(final Loan loan, final LocalDate transferDate, final Office acceptedInOffice,
|
||||
final Staff loanOfficer) {
|
||||
this.loanAssembler.setHelpers(loan);
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanAcceptTransferBusinessEvent(loan));
|
||||
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
||||
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,
|
||||
externalId);
|
||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||
if (loan.getTotalOverpaid() != null) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
|
||||
} else {
|
||||
@@ -1873,7 +1794,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Transactional
|
||||
@Override
|
||||
public LoanTransaction withdrawLoanTransfer(final Loan loan, final LocalDate transferDate) {
|
||||
this.loanAssembler.setHelpers(loan);
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanWithdrawTransferBusinessEvent(loan));
|
||||
|
||||
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,
|
||||
externalId);
|
||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
|
||||
@@ -1899,9 +1818,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Transactional
|
||||
@Override
|
||||
public void rejectLoanTransfer(final Loan loan) {
|
||||
this.loanAssembler.setHelpers(loan);
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = this.loanLifecycleStateMachine;
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||
@@ -2281,7 +2198,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
|
||||
|
||||
loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
||||
undoWrittenOff(loan, loanLifecycleStateMachine, existingTransactionIds, existingReversedTransactionIds);
|
||||
undoWrittenOff(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
|
||||
@@ -3079,8 +2996,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.build();
|
||||
}
|
||||
|
||||
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
public void handleChargebackTransaction(final Loan loan, LoanTransaction chargebackTransaction) {
|
||||
loanTransactionValidator.validateIfTransactionIsChargeback(chargebackTransaction);
|
||||
|
||||
loan.addLoanTransaction(chargebackTransaction);
|
||||
@@ -3093,10 +3009,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
new TransactionCtx(loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges(),
|
||||
new MoneyHolder(loan.getTotalOverpaidAsMoney()), null));
|
||||
}
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
if (!loan.doPostLoanTransactionChecks(chargebackTransaction.getTransactionDate(), loanLifecycleStateMachine)) {
|
||||
loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGEBACK, loan);
|
||||
}
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, chargebackTransaction.getTransactionDate());
|
||||
}
|
||||
|
||||
private void validateIsMultiDisbursalLoanAndDisbursedMoreThanOneTranche(Loan loan) {
|
||||
@@ -3232,8 +3145,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.getLoanTransactions().retainAll(retainTransactions);
|
||||
}
|
||||
|
||||
private Optional<LoanTransaction> closeAsWrittenOff(final Loan loan, final JsonCommand command,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
||||
private Optional<LoanTransaction> closeAsWrittenOff(final Loan loan, final JsonCommand command, final Map<String, Object> changes,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final AppUser currentUser,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
closeDisbursements(loan, scheduleGeneratorDTO);
|
||||
@@ -3308,12 +3220,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
LocalDate lastLoanTransactionDate = loan.getLatestTransactionDate();
|
||||
loan.doPostLoanTransactionChecks(lastLoanTransactionDate, loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, lastLoanTransactionDate);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<LoanTransaction> close(final Loan loan, final JsonCommand command,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes,
|
||||
private Optional<LoanTransaction> close(final Loan loan, final JsonCommand command, final Map<String, Object> changes,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
@@ -3458,25 +3369,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loanScheduleService.recalculateSchedule(loan, scheduleGeneratorDTO);
|
||||
actualChanges.put("undolastdisbursal", "true");
|
||||
actualChanges.put("disbursedAmount", loan.getDisbursedAmount());
|
||||
loan.updateLoanSummaryDerivedFields();
|
||||
|
||||
loan.doPostLoanTransactionChecks(loan.getLastUserTransactionDate(), loanLifecycleStateMachine);
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction,
|
||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction, null,
|
||||
scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
private void undoWrittenOff(final Loan loan, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
private void undoWrittenOff(final Loan loan, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
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
|
||||
* platform.
|
||||
*/
|
||||
public void closeAsMarkedForReschedule(final Loan loan, final JsonCommand command,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map<String, Object> changes) {
|
||||
public void closeAsMarkedForReschedule(final Loan loan, final JsonCommand command, final Map<String, Object> changes) {
|
||||
final LocalDate rescheduledOn = command.localDateValueOfParameterNamed(TRANSACTION_DATE);
|
||||
|
||||
loan.setClosedOnDate(rescheduledOn);
|
||||
@@ -3545,4 +3451,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
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(),
|
||||
holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
|
||||
adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionToAdjust, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO, reversalTxnExternalId);
|
||||
adjustExistingTransaction(loan, newTransactionDetail, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||
scheduleGeneratorDTO, reversalTxnExternalId);
|
||||
|
||||
loanAccrualsProcessingService.reprocessExistingAccruals(loan);
|
||||
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
@@ -185,7 +185,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
this.accountTransfersWritePlatformService.reverseTransfersWithFromAccountTransactions(transactionIds,
|
||||
PortfolioAccountType.LOAN);
|
||||
}
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate());
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
@@ -221,9 +221,9 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
}
|
||||
|
||||
public void adjustExistingTransaction(final Loan loan, final LoanTransaction newTransactionDetail,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final ExternalId reversalExternalId) {
|
||||
final LoanTransaction transactionForAdjustment, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO,
|
||||
final ExternalId reversalExternalId) {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
|
||||
@@ -268,8 +268,8 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
}
|
||||
|
||||
if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail,
|
||||
loanLifecycleStateMachine, transactionForAdjustment, scheduleGeneratorDTO);
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail, transactionForAdjustment,
|
||||
scheduleGeneratorDTO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-33
@@ -201,21 +201,19 @@ public class LoanAccountConfiguration {
|
||||
public LoanApplicationWritePlatformService loanApplicationWritePlatformService(PlatformSecurityContext context,
|
||||
LoanApplicationTransitionValidator loanApplicationTransitionValidator, LoanApplicationValidator loanApplicationValidator,
|
||||
LoanRepositoryWrapper loanRepositoryWrapper, NoteRepository noteRepository, LoanAssembler loanAssembler,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
CalendarRepository calendarRepository, CalendarInstanceRepository calendarInstanceRepository,
|
||||
SavingsAccountRepositoryWrapper savingsAccountRepository, AccountAssociationsRepository accountAssociationsRepository,
|
||||
BusinessEventNotifierService businessEventNotifierService, LoanScheduleAssembler loanScheduleAssembler,
|
||||
LoanUtilService loanUtilService, CalendarReadPlatformService calendarReadPlatformService,
|
||||
EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, GLIMAccountInfoRepository glimRepository,
|
||||
LoanRepository loanRepository, GSIMReadPlatformService gsimReadPlatformService,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService) {
|
||||
return new LoanApplicationWritePlatformServiceJpaRepositoryImpl(context, loanApplicationTransitionValidator,
|
||||
loanApplicationValidator, loanRepositoryWrapper, noteRepository, loanAssembler,
|
||||
loanRepaymentScheduleTransactionProcessorFactory, calendarRepository, calendarInstanceRepository, savingsAccountRepository,
|
||||
accountAssociationsRepository, businessEventNotifierService, loanScheduleAssembler, loanUtilService,
|
||||
calendarReadPlatformService, entityDatatableChecksWritePlatformService, glimRepository, loanRepository,
|
||||
gsimReadPlatformService, defaultLoanLifecycleStateMachine, loanAccrualsProcessingService,
|
||||
loanApplicationValidator, loanRepositoryWrapper, noteRepository, loanAssembler, calendarRepository,
|
||||
calendarInstanceRepository, savingsAccountRepository, accountAssociationsRepository, businessEventNotifierService,
|
||||
loanScheduleAssembler, loanUtilService, calendarReadPlatformService, entityDatatableChecksWritePlatformService,
|
||||
glimRepository, loanRepository, gsimReadPlatformService, loanLifecycleStateMachine, loanAccrualsProcessingService,
|
||||
loanDownPaymentTransactionValidator, loanScheduleService);
|
||||
}
|
||||
|
||||
@@ -235,8 +233,7 @@ public class LoanAccountConfiguration {
|
||||
LoanCollateralAssembler collateralAssembler,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
HolidayRepository holidayRepository, ConfigurationDomainService configurationDomainService,
|
||||
WorkingDaysRepositoryWrapper workingDaysRepository, RateAssembler rateAssembler,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, ExternalIdFactory externalIdFactory,
|
||||
WorkingDaysRepositoryWrapper workingDaysRepository, RateAssembler rateAssembler, ExternalIdFactory externalIdFactory,
|
||||
AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, GLIMAccountInfoRepository glimRepository,
|
||||
AccountNumberGenerator accountNumberGenerator, GLIMAccountInfoWritePlatformService glimAccountInfoWritePlatformService,
|
||||
LoanCollateralAssembler loanCollateralAssembler, LoanScheduleCalculationPlatformService calculationPlatformService,
|
||||
@@ -247,10 +244,10 @@ public class LoanAccountConfiguration {
|
||||
return new LoanAssemblerImpl(fromApiJsonHelper, loanRepository, loanProductRepository, clientRepository, groupRepository,
|
||||
fundRepository, staffRepository, codeValueRepository, loanScheduleAssembler, loanChargeAssembler, collateralAssembler,
|
||||
loanRepaymentScheduleTransactionProcessorFactory, holidayRepository, configurationDomainService, workingDaysRepository,
|
||||
rateAssembler, defaultLoanLifecycleStateMachine, externalIdFactory, accountNumberFormatRepository, glimRepository,
|
||||
accountNumberGenerator, glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService,
|
||||
loanDisbursementDetailsAssembler, loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService,
|
||||
loanDisbursementService, loanChargeService, loanOfficerService, loanSchedule);
|
||||
rateAssembler, externalIdFactory, accountNumberFormatRepository, glimRepository, accountNumberGenerator,
|
||||
glimAccountInfoWritePlatformService, loanCollateralAssembler, calculationPlatformService, loanDisbursementDetailsAssembler,
|
||||
loanChargeMapper, loanCollateralManagementMapper, loanAccrualsProcessingService, loanDisbursementService, loanChargeService,
|
||||
loanOfficerService, loanSchedule);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -293,7 +290,7 @@ public class LoanAccountConfiguration {
|
||||
AccountTransfersWritePlatformService accountTransfersWritePlatformService, LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
JournalEntryWritePlatformService journalEntryWritePlatformService, LoanAccountDomainService loanAccountDomainService,
|
||||
LoanChargeRepository loanChargeRepository, LoanWritePlatformService loanWritePlatformService, LoanUtilService loanUtilService,
|
||||
LoanChargeReadPlatformService loanChargeReadPlatformService, LoanLifecycleStateMachine defaultLoanLifecycleStateMachine,
|
||||
LoanChargeReadPlatformService loanChargeReadPlatformService, LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, FromJsonHelper fromApiJsonHelper,
|
||||
ConfigurationDomainService configurationDomainService,
|
||||
LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
@@ -304,16 +301,16 @@ public class LoanAccountConfiguration {
|
||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanChargeValidator loanChargeValidator,
|
||||
LoanScheduleService loanScheduleService, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService,
|
||||
LoanAccountingBridgeMapper loanAccountingBridgeMapper) {
|
||||
LoanAccountingBridgeMapper loanAccountingBridgeMapper, LoanChargeService loanChargeService) {
|
||||
return new LoanChargeWritePlatformServiceImpl(loanChargeApiJsonValidator, loanAssembler, chargeRepository,
|
||||
businessEventNotifierService, loanTransactionRepository, accountTransfersWritePlatformService, loanRepositoryWrapper,
|
||||
journalEntryWritePlatformService, loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService,
|
||||
loanChargeReadPlatformService, defaultLoanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper,
|
||||
loanChargeReadPlatformService, loanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper,
|
||||
configurationDomainService, loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory,
|
||||
accountTransferDetailRepository, loanChargeAssembler, paymentDetailWritePlatformService, noteRepository,
|
||||
loanAccrualTransactionBusinessEventService, loanAccrualsProcessingService, loanDownPaymentTransactionValidator,
|
||||
loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService, loanAdjustmentService,
|
||||
loanAccountingBridgeMapper);
|
||||
loanAccountingBridgeMapper, loanChargeService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -395,7 +392,7 @@ public class LoanAccountConfiguration {
|
||||
RepaymentWithPostDatedChecksAssembler repaymentWithPostDatedChecksAssembler,
|
||||
PostDatedChecksRepository postDatedChecksRepository,
|
||||
LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository,
|
||||
LoanLifecycleStateMachine defaultLoanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine, LoanAccountLockService loanAccountLockService,
|
||||
ExternalIdFactory externalIdFactory, LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService,
|
||||
ErrorHandler errorHandler, LoanDownPaymentHandlerService loanDownPaymentHandlerService,
|
||||
LoanTransactionAssembler loanTransactionAssembler, LoanAccrualsProcessingService loanAccrualsProcessingService,
|
||||
@@ -415,12 +412,12 @@ public class LoanAccountConfiguration {
|
||||
loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
|
||||
guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, codeValueRepository,
|
||||
cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
|
||||
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
|
||||
loanAccountLockService, externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler,
|
||||
loanDownPaymentHandlerService, loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator,
|
||||
loanDownPaymentTransactionValidator, loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService,
|
||||
reprocessLoanTransactionsService, loanAccountService, journalEntryPoster, loanAdjustmentService, loanAccountingBridgeMapper,
|
||||
loanMapper, loanTransactionProcessingService);
|
||||
postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, loanLifecycleStateMachine, loanAccountLockService,
|
||||
externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService,
|
||||
loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator,
|
||||
loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService, reprocessLoanTransactionsService,
|
||||
loanAccountService, journalEntryPoster, loanAdjustmentService, loanAccountingBridgeMapper, loanMapper,
|
||||
loanTransactionProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -437,10 +434,10 @@ public class LoanAccountConfiguration {
|
||||
LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanScheduleService loanScheduleService,
|
||||
LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator,
|
||||
ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
||||
LoanTransactionProcessingService loanTransactionProcessingService, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
||||
reprocessLoanTransactionsService, loanTransactionProcessingService);
|
||||
reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -452,15 +449,17 @@ public class LoanAccountConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanDisbursementService.class)
|
||||
public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator,
|
||||
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService) {
|
||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService);
|
||||
LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService,
|
||||
LoanChargeService loanChargeService) {
|
||||
return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService,
|
||||
loanChargeService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanChargeService.class)
|
||||
public LoanChargeService loanChargeService(LoanChargeValidator loanChargeValidator,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
||||
return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService);
|
||||
LoanTransactionProcessingService loanTransactionProcessingService, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -480,9 +479,10 @@ public class LoanAccountConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(LoanRefundService.class)
|
||||
public LoanRefundService loanRefundService(LoanRefundValidator loanRefundValidator,
|
||||
LoanTransactionProcessingService loanTransactionProcessingService) {
|
||||
return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService);
|
||||
public LoanRefundService loanRefundService(final LoanRefundValidator loanRefundValidator,
|
||||
final LoanTransactionProcessingService loanTransactionProcessingService,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+4
-4
@@ -241,8 +241,8 @@ class LoanAdjustmentServiceImplTest {
|
||||
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
|
||||
|
||||
// Act
|
||||
underTest.adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment,
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
||||
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
||||
|
||||
// Assert
|
||||
// Verify that related transaction is reversed and event is triggered
|
||||
@@ -286,8 +286,8 @@ class LoanAdjustmentServiceImplTest {
|
||||
when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
|
||||
|
||||
// Act
|
||||
underTest.adjustExistingTransaction(loan, newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment,
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
||||
underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId);
|
||||
|
||||
// Assert
|
||||
// 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.when;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
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.service.BusinessEventNotifierService;
|
||||
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.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
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.LoanCharge;
|
||||
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.LoanTransaction;
|
||||
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.serialization.LoanChargeApiJsonValidator;
|
||||
import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
|
||||
@@ -150,6 +158,18 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
@Mock
|
||||
private LoanAccountingBridgeMapper loanAccountingBridgeMapper;
|
||||
|
||||
@Mock
|
||||
private LoanChargeService loanChargeService;
|
||||
|
||||
@Mock
|
||||
private ChargeCalculationType chargeCalculationType;
|
||||
|
||||
@Mock
|
||||
private SingleLoanChargeRepaymentScheduleProcessingWrapper wrapper;
|
||||
|
||||
@Mock
|
||||
private LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
|
||||
@@ -158,8 +178,13 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
when(chargeDefinition.getCurrencyCode()).thenReturn(CURRENCY_CODE);
|
||||
when(loanChargeAssembler.createNewFromJson(loan, chargeDefinition, jsonCommand)).thenReturn(loanCharge);
|
||||
when(loan.repaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
|
||||
when(loanRepaymentScheduleDetail.getLoanScheduleType()).thenReturn(LoanScheduleType.CUMULATIVE);
|
||||
when(loan.getLoanRepaymentScheduleDetail()).thenReturn(loanRepaymentScheduleDetail);
|
||||
when(loan.hasCurrencyCodeOf(CURRENCY_CODE)).thenReturn(true);
|
||||
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(loanChargeRepository.saveAndFlush(any(LoanCharge.class))).thenReturn(loanCharge);
|
||||
when(loan.getCurrency()).thenReturn(monetaryCurrency);
|
||||
@@ -170,12 +195,23 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
when(loan.findExistingTransactionIds()).thenReturn(existingTransactionIds);
|
||||
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.isCashBasedAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||
when(loan.isUpfrontAccrualAccountingEnabledOnLoanProduct()).thenReturn(false);
|
||||
|
||||
when(loanAccountingBridgeMapper.deriveAccountingBridgeData(anyString(), anyList(), anyList(), anyBoolean(), any(Loan.class))).thenReturn(new AccountingBridgeDataDTO());
|
||||
doNothing().when(journalEntryWritePlatformService).createJournalEntriesForLoan(any(AccountingBridgeDataDTO.class));
|
||||
doNothing().when(loanChargeService).addLoanCharge(any(Loan.class), any(LoanCharge.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@@ -189,8 +225,13 @@ class LoanChargeWritePlatformServiceImplTest {
|
||||
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.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);
|
||||
}
|
||||
|
||||
+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.domain.ChangedTransactionDetail;
|
||||
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.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
@@ -107,13 +108,16 @@ public class LoanDownPaymentHandlerServiceImplTest {
|
||||
@Mock
|
||||
private LoanTransactionProcessingService loanTransactionProcessingService;
|
||||
|
||||
@Mock
|
||||
private LoanLifecycleStateMachine loanLifecycleStateMachine;
|
||||
|
||||
private LoanDownPaymentHandlerServiceImpl underTest;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService,
|
||||
loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator,
|
||||
reprocessLoanTransactionsService, loanTransactionProcessingService);
|
||||
reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine);
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
|
||||
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
|
||||
tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);
|
||||
|
||||
Reference in New Issue
Block a user