FINERACT-1734: Refactoring around Loan status changes as a preparation to emit Loan Status change business events
This commit is contained in:
committed by
Arnold Gálovics
parent
8bb2e9230c
commit
d003326e48
+78
-19
@@ -19,15 +19,13 @@
|
||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachine {
|
||||
|
||||
private final List<LoanStatus> allowedLoanStatuses;
|
||||
|
||||
public DefaultLoanLifecycleStateMachine(final List<LoanStatus> allowedLoanStatuses) {
|
||||
this.allowedLoanStatuses = allowedLoanStatuses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanStatus transition(final LoanEvent loanEvent, final LoanStatus from) {
|
||||
|
||||
@@ -36,77 +34,94 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
switch (loanEvent) {
|
||||
case LOAN_CREATED:
|
||||
if (from == null) {
|
||||
newState = stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, this.allowedLoanStatuses);
|
||||
newState = submittedTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_REJECTED:
|
||||
if (from.hasStateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) {
|
||||
newState = stateOf(LoanStatus.REJECTED, this.allowedLoanStatuses);
|
||||
newState = rejectedTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_APPROVED:
|
||||
if (from.hasStateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) {
|
||||
newState = stateOf(LoanStatus.APPROVED, this.allowedLoanStatuses);
|
||||
newState = approvedTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_WITHDRAWN:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL)) {
|
||||
newState = stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, this.allowedLoanStatuses);
|
||||
newState = withdrawnByClientTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_DISBURSED:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.APPROVED, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
|
||||
newState = stateOf(LoanStatus.ACTIVE, this.allowedLoanStatuses);
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_APPROVAL_UNDO:
|
||||
if (from.hasStateOf(LoanStatus.APPROVED)) {
|
||||
newState = stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, this.allowedLoanStatuses);
|
||||
newState = submittedTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_DISBURSAL_UNDO:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
|
||||
newState = stateOf(LoanStatus.APPROVED, this.allowedLoanStatuses);
|
||||
newState = approvedTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_CHARGE_PAYMENT:
|
||||
case LOAN_REPAYMENT_OR_WAIVER:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.OVERPAID)) {
|
||||
newState = stateOf(LoanStatus.ACTIVE, this.allowedLoanStatuses);
|
||||
} else {
|
||||
newState = from;
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
case REPAID_IN_FULL:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.OVERPAID)) {
|
||||
newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, this.allowedLoanStatuses);
|
||||
newState = closeObligationsMetTransition();
|
||||
}
|
||||
break;
|
||||
case WRITE_OFF_OUTSTANDING:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
|
||||
newState = stateOf(LoanStatus.CLOSED_WRITTEN_OFF, this.allowedLoanStatuses);
|
||||
newState = closedWrittenOffTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_RESCHEDULE:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE)) {
|
||||
newState = stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, this.allowedLoanStatuses);
|
||||
newState = closedRescheduleOutstandingAmountTransition();
|
||||
}
|
||||
break;
|
||||
case INTERST_REBATE_OWED:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET)) {
|
||||
newState = stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, this.allowedLoanStatuses);
|
||||
newState = closeObligationsMetTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_OVERPAYMENT:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.ACTIVE)) {
|
||||
newState = stateOf(LoanStatus.OVERPAID, this.allowedLoanStatuses);
|
||||
newState = overpaidTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_ADJUST_TRANSACTION:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_OBLIGATIONS_MET, LoanStatus.CLOSED_WRITTEN_OFF,
|
||||
LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT)) {
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_INITIATE_TRANSFER:
|
||||
newState = transferInProgress();
|
||||
break;
|
||||
case LOAN_REJECT_TRANSFER:
|
||||
newState = transferOnHold();
|
||||
break;
|
||||
case LOAN_WITHDRAW_TRANSFER:
|
||||
newState = activeTransition();
|
||||
break;
|
||||
case LOAN_CLOSED:
|
||||
break;
|
||||
case WRITE_OFF_OUTSTANDING_UNDO:
|
||||
if (anyOfAllowedWhenComingFrom(from, LoanStatus.CLOSED_WRITTEN_OFF)) {
|
||||
newState = activeTransition();
|
||||
}
|
||||
break;
|
||||
case LOAN_CREDIT_BALANCE_REFUND:
|
||||
newState = closeObligationsMetTransition();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -115,6 +130,50 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
return newState;
|
||||
}
|
||||
|
||||
private LoanStatus transferOnHold() {
|
||||
return stateOf(LoanStatus.TRANSFER_ON_HOLD, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus transferInProgress() {
|
||||
return stateOf(LoanStatus.TRANSFER_IN_PROGRESS, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus overpaidTransition() {
|
||||
return stateOf(LoanStatus.OVERPAID, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus closedRescheduleOutstandingAmountTransition() {
|
||||
return stateOf(LoanStatus.CLOSED_RESCHEDULE_OUTSTANDING_AMOUNT, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus closedWrittenOffTransition() {
|
||||
return stateOf(LoanStatus.CLOSED_WRITTEN_OFF, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus closeObligationsMetTransition() {
|
||||
return stateOf(LoanStatus.CLOSED_OBLIGATIONS_MET, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus activeTransition() {
|
||||
return stateOf(LoanStatus.ACTIVE, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus withdrawnByClientTransition() {
|
||||
return stateOf(LoanStatus.WITHDRAWN_BY_CLIENT, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus approvedTransition() {
|
||||
return stateOf(LoanStatus.APPROVED, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus rejectedTransition() {
|
||||
return stateOf(LoanStatus.REJECTED, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus submittedTransition() {
|
||||
return stateOf(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL, this.allowedLoanStatuses);
|
||||
}
|
||||
|
||||
private LoanStatus stateOf(final LoanStatus state, final List<LoanStatus> allowedLoanStatuses) {
|
||||
LoanStatus match = null;
|
||||
for (final LoanStatus loanStatus : allowedLoanStatuses) {
|
||||
|
||||
+11
-4
@@ -3635,7 +3635,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
if (isClosedObligationsMet() || isClosedWrittenOff() || isClosedWithOutsandingAmountMarkedForReschedule()) {
|
||||
this.loanStatus = LoanStatus.ACTIVE.getValue();
|
||||
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = loanStatusEnum.getValue();
|
||||
}
|
||||
|
||||
if (newTransactionDetail.isRepaymentType() || newTransactionDetail.isInterestWaiver()) {
|
||||
@@ -3646,7 +3648,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
public ChangedTransactionDetail undoWrittenOff(LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
||||
@@ -3654,7 +3657,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
final LoanTransaction writeOffTransaction = findWriteOffTransaction();
|
||||
writeOffTransaction.reverse();
|
||||
this.loanStatus = LoanStatus.ACTIVE.getValue();
|
||||
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = loanStatusEnum.getValue();
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
@@ -6068,7 +6073,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) {
|
||||
this.loanStatus = LoanStatus.CLOSED_OBLIGATIONS_MET.getValue();
|
||||
LoanStatus statusEnum = defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_CREDIT_BALANCE_REFUND,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
@@ -44,5 +44,9 @@ public enum LoanEvent {
|
||||
LOAN_EDIT_MULTI_DISBURSE_DATE, //
|
||||
LOAN_REFUND, //
|
||||
LOAN_FORECLOSURE, //
|
||||
LOAN_ADJUST_TRANSACTION, //
|
||||
LOAN_INITIATE_TRANSFER, //
|
||||
LOAN_REJECT_TRANSFER, //
|
||||
LOAN_WITHDRAW_TRANSFER, //
|
||||
LOAN_CREDIT_BALANCE_REFUND;
|
||||
}
|
||||
|
||||
+16
-7
@@ -2260,7 +2260,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate);
|
||||
loan.addLoanTransaction(newTransferTransaction);
|
||||
loan.setLoanStatus(LoanStatus.TRANSFER_IN_PROGRESS.getValue());
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
|
||||
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan.status());
|
||||
loan.setLoanStatus(loanStatusEnum.getValue());
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newTransferTransaction);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -2281,11 +2283,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate);
|
||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
|
||||
LoanStatus loanStatusEnum;
|
||||
if (loan.getTotalOverpaid() != null) {
|
||||
loan.setLoanStatus(LoanStatus.OVERPAID.getValue());
|
||||
loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, loan.status());
|
||||
} else {
|
||||
loan.setLoanStatus(LoanStatus.ACTIVE.getValue());
|
||||
loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, loan.status());
|
||||
}
|
||||
loan.setLoanStatus(loanStatusEnum.getValue());
|
||||
if (loanOfficer != null) {
|
||||
loan.reassignLoanOfficer(loanOfficer, transferDate);
|
||||
}
|
||||
@@ -2310,7 +2315,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate);
|
||||
loan.addLoanTransaction(newTransferAcceptanceTransaction);
|
||||
loan.setLoanStatus(LoanStatus.ACTIVE.getValue());
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
|
||||
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan.status());
|
||||
loan.setLoanStatus(loanStatusEnum.getValue());
|
||||
|
||||
this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -2326,7 +2333,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
public void rejectLoanTransfer(final Loan loan) {
|
||||
this.loanAssembler.setHelpers(loan);
|
||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||
loan.setLoanStatus(LoanStatus.TRANSFER_ON_HOLD.getValue());
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine = defaultLoanLifecycleStateMachine();
|
||||
LoanStatus loanStatusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECT_TRANSFER, loan.status());
|
||||
loan.setLoanStatus(loanStatusEnum.getValue());
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanRejectTransferBusinessEvent(loan));
|
||||
}
|
||||
@@ -2857,8 +2866,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(existingTransactionIds, existingReversedTransactionIds,
|
||||
scheduleGeneratorDTO);
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(defaultLoanLifecycleStateMachine(), existingTransactionIds,
|
||||
existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
|
||||
Reference in New Issue
Block a user