FINERACT-1734: Loan status can and only be set using the State Machine

This commit is contained in:
Arnold Galovics
2022-09-09 16:44:18 +02:00
committed by Arnold Gálovics
parent d003326e48
commit d06c3655af
4 changed files with 62 additions and 82 deletions
@@ -27,16 +27,26 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
private final List<LoanStatus> allowedLoanStatuses; private final List<LoanStatus> allowedLoanStatuses;
@Override @Override
public LoanStatus transition(final LoanEvent loanEvent, final LoanStatus from) { public LoanStatus dryTransition(final LoanEvent loanEvent, final Loan loan) {
return getNextState(loanEvent, loan);
}
@Override
public void transition(final LoanEvent loanEvent, final Loan loan) {
LoanStatus newState = getNextState(loanEvent, loan);
loan.setLoanStatus(newState.getValue());
}
private LoanStatus getNextState(LoanEvent loanEvent, Loan loan) {
Integer plainFrom = loan.getPlainStatus();
if (loanEvent.equals(LoanEvent.LOAN_CREATED) && plainFrom == null) {
return submittedTransition();
}
LoanStatus from = loan.status();
LoanStatus newState = from; LoanStatus newState = from;
switch (loanEvent) { switch (loanEvent) {
case LOAN_CREATED:
if (from == null) {
newState = submittedTransition();
}
break;
case LOAN_REJECTED: case LOAN_REJECTED:
if (from.hasStateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) { if (from.hasStateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) {
newState = rejectedTransition(); newState = rejectedTransition();
@@ -126,7 +136,6 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
default: default:
break; break;
} }
return newState; return newState;
} }
@@ -700,9 +700,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
chargesPayment.getAmount(getCurrency()).getAmount(), installmentNumber); chargesPayment.getAmount(getCurrency()).getAmount(), installmentNumber);
chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy);
addLoanTransaction(chargesPayment); addLoanTransaction(chargesPayment);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, this);
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
@@ -2025,13 +2023,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
updateLoanSchedule(loanSchedule); updateLoanSchedule(loanSchedule);
LoanStatus from = null; lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, this);
if (this.loanStatus != null) {
from = LoanStatus.fromInt(this.loanStatus);
}
final LoanStatus statusEnum = lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, from);
this.loanStatus = statusEnum.getValue();
this.externalId = externalId; this.externalId = externalId;
this.termFrequency = loanApplicationTerms.getLoanTermFrequency(); this.termFrequency = loanApplicationTerms.getLoanTermFrequency();
@@ -2114,9 +2106,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final Map<String, Object> actualChanges = new LinkedHashMap<>(); final Map<String, Object> actualChanges = new LinkedHashMap<>();
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_REJECTED, this);
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, this);
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
final LocalDate rejectedOn = command.localDateValueOfParameterNamed("rejectedOnDate"); final LocalDate rejectedOn = command.localDateValueOfParameterNamed("rejectedOnDate");
@@ -2160,9 +2152,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final Map<String, Object> actualChanges = new LinkedHashMap<>(); final Map<String, Object> actualChanges = new LinkedHashMap<>();
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_WITHDRAWN, this);
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, this);
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
LocalDate withdrawnOn = command.localDateValueOfParameterNamed("withdrawnOnDate"); LocalDate withdrawnOn = command.localDateValueOfParameterNamed("withdrawnOnDate");
@@ -2215,7 +2207,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
* statusEnum is holding the possible new status derived from loanLifecycleStateMachine.transition. * statusEnum is holding the possible new status derived from loanLifecycleStateMachine.transition.
*/ */
final LoanStatus newStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus)); final LoanStatus newStatusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVED, this);
/* /*
* FIXME: There is no need to check below condition, if loanLifecycleStateMachine.transition is doing it's * FIXME: There is no need to check below condition, if loanLifecycleStateMachine.transition is doing it's
@@ -2225,7 +2217,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
*/ */
if (!newStatusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!newStatusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = newStatusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, this);
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
// only do below if status has changed in the 'approval' case // only do below if status has changed in the 'approval' case
@@ -2363,9 +2355,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final Map<String, Object> actualChanges = new LinkedHashMap<>(); final Map<String, Object> actualChanges = new LinkedHashMap<>();
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus); final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, currentStatus); final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_APPROVAL_UNDO, this);
if (!statusEnum.hasStateOf(currentStatus)) { if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, this);
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.approvedOnDate = null; this.approvedOnDate = null;
@@ -2414,12 +2406,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges, public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) { final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) {
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, this);
LoanStatus.fromInt(this.loanStatus));
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
this.loanStatus = statusEnum.getValue();
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
this.disbursedBy = currentUser; this.disbursedBy = currentUser;
@@ -2526,8 +2516,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public boolean canDisburse(final LocalDate actualDisbursementDate) { public boolean canDisburse(final LocalDate actualDisbursementDate) {
LocalDate lastDisburseDate = this.actualDisbursementDate; LocalDate lastDisburseDate = this.actualDisbursementDate;
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSED, this);
LoanStatus.fromInt(this.loanStatus));
boolean isMultiTrancheDisburse = false; boolean isMultiTrancheDisburse = false;
LoanStatus actualLoanStatus = LoanStatus.fromInt(this.loanStatus); LoanStatus actualLoanStatus = LoanStatus.fromInt(this.loanStatus);
@@ -2921,12 +2910,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final Map<String, Object> actualChanges = new LinkedHashMap<>(); final Map<String, Object> actualChanges = new LinkedHashMap<>();
final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus); final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus); final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSAL_UNDO, this);
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO, getDisbursementDate()); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO, getDisbursementDate());
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
if (!statusEnum.hasStateOf(currentStatus)) { if (!statusEnum.hasStateOf(currentStatus)) {
this.loanStatus = statusEnum.getValue(); this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, this);
actualChanges.put("status", LoanEnumerations.status(this.loanStatus)); actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
final LocalDate actualDisbursementDate = getDisbursementDate(); final LocalDate actualDisbursementDate = getDisbursementDate();
@@ -3162,9 +3151,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
if (loanTransaction.isRecoveryRepayment()) { if (loanTransaction.isRecoveryRepayment()) {
statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, LoanStatus.fromInt(this.loanStatus)); loanLifecycleStateMachine.transition(LoanEvent.LOAN_RECOVERY_PAYMENT, this);
} else { } else {
statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, LoanStatus.fromInt(this.loanStatus)); loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, this);
} }
if (loanTransaction.isRecoveryRepayment() if (loanTransaction.isRecoveryRepayment()
@@ -3173,8 +3162,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage); throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage);
} }
this.loanStatus = statusEnum.getValue();
loanTransaction.updateLoan(this); loanTransaction.updateLoan(this);
final boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, getLoanTransactions()); final boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, getLoanTransactions());
@@ -3392,16 +3379,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
} }
if (isAllChargesPaid) { if (isAllChargesPaid) {
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this);
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
this.closedOnDate = transactionDate; this.closedOnDate = transactionDate;
this.actualMaturityDate = transactionDate; this.actualMaturityDate = transactionDate;
} else if (LoanStatus.fromInt(this.loanStatus).isOverpaid()) { } else if (LoanStatus.fromInt(this.loanStatus).isOverpaid()) {
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, this);
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
} }
processIncomeAccrualTransactionOnLoanClosure(); processIncomeAccrualTransactionOnLoanClosure();
} }
@@ -3473,8 +3456,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) { private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, LoanStatus.fromInt(this.loanStatus)); loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, this);
this.loanStatus = statusEnum.getValue();
this.closedOnDate = null; this.closedOnDate = null;
this.actualMaturityDate = null; this.actualMaturityDate = null;
@@ -3635,9 +3617,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
} }
if (isClosedObligationsMet() || isClosedWrittenOff() || isClosedWithOutsandingAmountMarkedForReschedule()) { if (isClosedObligationsMet() || isClosedWrittenOff() || isClosedWithOutsandingAmountMarkedForReschedule()) {
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION, loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION, this);
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = loanStatusEnum.getValue();
} }
if (newTransactionDetail.isRepaymentType() || newTransactionDetail.isInterestWaiver()) { if (newTransactionDetail.isRepaymentType() || newTransactionDetail.isInterestWaiver()) {
@@ -3657,9 +3637,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LoanTransaction writeOffTransaction = findWriteOffTransaction(); final LoanTransaction writeOffTransaction = findWriteOffTransaction();
writeOffTransaction.reverse(); writeOffTransaction.reverse();
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO, this);
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = loanStatusEnum.getValue();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy); .determineProcessor(this.transactionProcessingStrategy);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
@@ -3740,12 +3718,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING); validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING);
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.WRITE_OFF_OUTSTANDING, this);
LoanStatus.fromInt(this.loanStatus));
LoanTransaction loanTransaction = null; LoanTransaction loanTransaction = null;
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, this);
changes.put("status", LoanEnumerations.status(this.loanStatus)); changes.put("status", LoanEnumerations.status(this.loanStatus));
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
@@ -3866,10 +3843,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final Money totalOutstanding = this.summary.getTotalOutstanding(loanCurrency()); final Money totalOutstanding = this.summary.getTotalOutstanding(loanCurrency());
if (totalOutstanding.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(totalOutstanding)) { if (totalOutstanding.isGreaterThanZero() && getInArrearsTolerance().isGreaterThanOrEqualTo(totalOutstanding)) {
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.REPAID_IN_FULL, this);
LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this);
changes.put("status", LoanEnumerations.status(this.loanStatus)); changes.put("status", LoanEnumerations.status(this.loanStatus));
} }
this.closedOnDate = closureDate; this.closedOnDate = closureDate;
@@ -3899,10 +3875,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
// TODO - KW - technically should set somewhere that this loan // TODO - KW - technically should set somewhere that this loan
// has // has
// 'overpaid' amount // 'overpaid' amount
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.REPAID_IN_FULL, this);
LoanStatus.fromInt(this.loanStatus));
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this);
changes.put("status", LoanEnumerations.status(this.loanStatus)); changes.put("status", LoanEnumerations.status(this.loanStatus));
} }
this.closedOnDate = closureDate; this.closedOnDate = closureDate;
@@ -3928,9 +3903,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final LocalDate rescheduledOn = command.localDateValueOfParameterNamed("transactionDate"); final LocalDate rescheduledOn = command.localDateValueOfParameterNamed("transactionDate");
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RESCHEDULE, LoanStatus.fromInt(this.loanStatus)); final LoanStatus statusEnum = loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_RESCHEDULE, this);
if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) { if (!statusEnum.hasStateOf(LoanStatus.fromInt(this.loanStatus))) {
this.loanStatus = statusEnum.getValue(); loanLifecycleStateMachine.transition(LoanEvent.LOAN_RESCHEDULE, this);
changes.put("status", LoanEnumerations.status(this.loanStatus)); changes.put("status", LoanEnumerations.status(this.loanStatus));
} }
@@ -3961,6 +3936,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return LoanStatus.fromInt(this.loanStatus); return LoanStatus.fromInt(this.loanStatus);
} }
public Integer getPlainStatus() {
return this.loanStatus;
}
public boolean isSubmittedAndPendingApproval() { public boolean isSubmittedAndPendingApproval() {
return status().isSubmittedAndPendingApproval(); return status().isSubmittedAndPendingApproval();
} }
@@ -4793,7 +4772,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
this.loanTransactions.remove(loanTransaction); this.loanTransactions.remove(loanTransaction);
} }
public void setLoanStatus(final Integer loanStatus) { // Intentionally kept as package-private. Nobody should set the status directly but use the
// DefaultLoanLifecycleStateMachine to transition
void setLoanStatus(final Integer loanStatus) {
this.loanStatus = loanStatus; this.loanStatus = loanStatus;
} }
@@ -6073,9 +6054,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
updateLoanSummaryDerivedFields(); updateLoanSummaryDerivedFields();
if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) { if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) {
LoanStatus statusEnum = defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_CREDIT_BALANCE_REFUND, defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_CREDIT_BALANCE_REFUND, this);
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
} }
} }
@@ -6116,15 +6095,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
ChangedTransactionDetail changedTransactionDetail = null; ChangedTransactionDetail changedTransactionDetail = null;
final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, LoanStatus.fromInt(this.loanStatus)); loanLifecycleStateMachine.transition(LoanEvent.LOAN_REFUND, this);
this.loanStatus = statusEnum.getValue();
loanTransaction.updateLoan(this); loanTransaction.updateLoan(this);
// final boolean isTransactionChronologicallyLatest =
// isChronologicallyLatestRefund(loanTransaction,
// this.loanTransactions);
if (status().isOverpaid() || status().isClosed()) { if (status().isOverpaid() || status().isClosed()) {
final String errorMessage = "This refund option is only for active loans "; final String errorMessage = "This refund option is only for active loans ";
@@ -20,5 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.domain;
public interface LoanLifecycleStateMachine { public interface LoanLifecycleStateMachine {
LoanStatus transition(LoanEvent loanEvent, LoanStatus from); LoanStatus dryTransition(LoanEvent loanEvent, Loan loan);
void transition(LoanEvent loanEvent, Loan loan);
} }
@@ -2261,8 +2261,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate); final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate);
loan.addLoanTransaction(newTransferTransaction); loan.addLoanTransaction(newTransferTransaction);
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine(); LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan.status()); loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan);
loan.setLoanStatus(loanStatusEnum.getValue());
this.loanTransactionRepository.saveAndFlush(newTransferTransaction); this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
saveLoanWithDataIntegrityViolationChecks(loan); saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2284,13 +2283,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate); final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate);
loan.addLoanTransaction(newTransferAcceptanceTransaction); loan.addLoanTransaction(newTransferAcceptanceTransaction);
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine(); LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
LoanStatus loanStatusEnum;
if (loan.getTotalOverpaid() != null) { if (loan.getTotalOverpaid() != null) {
loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan.status()); loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan);
} else { } else {
loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan.status()); loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan);
} }
loan.setLoanStatus(loanStatusEnum.getValue());
if (loanOfficer != null) { if (loanOfficer != null) {
loan.reassignLoanOfficer(loanOfficer, transferDate); loan.reassignLoanOfficer(loanOfficer, transferDate);
} }
@@ -2316,8 +2313,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate); final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate);
loan.addLoanTransaction(newTransferAcceptanceTransaction); loan.addLoanTransaction(newTransferAcceptanceTransaction);
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine(); LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan.status()); loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan);
loan.setLoanStatus(loanStatusEnum.getValue());
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction); this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
saveLoanWithDataIntegrityViolationChecks(loan); saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2334,8 +2330,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanAssembler.setHelpers(loan); this.loanAssembler.setHelpers(loan);
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan)); businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine(); LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan.status()); loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan);
loan.setLoanStatus(loanStatusEnum.getValue());
saveLoanWithDataIntegrityViolationChecks(loan); saveLoanWithDataIntegrityViolationChecks(loan);
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
} }