diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java index c2a5868ff..fa37e4813 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/DefaultLoanLifecycleStateMachine.java @@ -89,6 +89,12 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi newState = stateOf(LoanStatus.OVERPAID, this.allowedLoanStatuses); } break; + case LOAN_CLOSED: + break; + case WRITE_OFF_OUTSTANDING_UNDO: + break; + default: + break; } return newState; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index bca35d424..76bed1d11 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -44,6 +44,8 @@ import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator; import org.mifosplatform.organisation.holiday.domain.Holiday; @@ -1314,7 +1316,9 @@ public class Loan extends AbstractPersistable { public Map loanApplicationRejection(final AppUser currentUser, final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) { - + + validateAccountStatus(LoanEvent.LOAN_REJECTED); + final Map actualChanges = new LinkedHashMap(); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, LoanStatus.fromInt(this.loanStatus)); @@ -1410,6 +1414,8 @@ public class Loan extends AbstractPersistable { public Map loanApplicationApproval(final AppUser currentUser, final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine) { + validateAccountStatus(LoanEvent.LOAN_APPROVED); + final Map actualChanges = new LinkedHashMap(); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, LoanStatus.fromInt(this.loanStatus)); @@ -1458,6 +1464,7 @@ public class Loan extends AbstractPersistable { public Map undoApproval(final LoanLifecycleStateMachine loanLifecycleStateMachine) { + validateAccountStatus(LoanEvent.LOAN_APPROVAL_UNDO); final Map actualChanges = new LinkedHashMap(); final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus); @@ -1506,6 +1513,7 @@ public class Loan extends AbstractPersistable { final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay) { + validateAccountStatus(LoanEvent.LOAN_DISBURSED); updateLoanToPreDisbursalState(); final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, @@ -1674,8 +1682,9 @@ public class Loan extends AbstractPersistable { final List existingReversedTransactionIds, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { + validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO); + final Map actualChanges = new LinkedHashMap(); - final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus); final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO, getDisbursementDate()); @@ -1736,6 +1745,8 @@ public class Loan extends AbstractPersistable { public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, final List existingReversedTransactionIds) { + + validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, waiveInterestTransaction.getTransactionDate()); @@ -1754,6 +1765,7 @@ public class Loan extends AbstractPersistable { final List existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List holidays, final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) { + validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, repaymentTransaction.getTransactionDate()); validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays); @@ -1773,6 +1785,7 @@ public class Loan extends AbstractPersistable { final boolean allowTransactionsOnHoliday, final List holidays, final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction, final Integer installmentNumber) { + validateAccountStatus(LoanEvent.LOAN_CHARGE_PAYMENT); validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate()); validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays); validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay); @@ -2113,6 +2126,9 @@ public class Loan extends AbstractPersistable { } public ChangedTransactionDetail undoWrittenOff(final List existingTransactionIds, final List existingReversedTransactionIds){ + + validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO); + existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); final LoanTransaction writeOffTransaction = findWriteOffTransaction(); @@ -2179,6 +2195,8 @@ public class Loan extends AbstractPersistable { final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds, final AppUser currentUser) { + validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING); + final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.WRITE_OFF_OUTSTANDING, LoanStatus.fromInt(this.loanStatus)); @@ -2235,7 +2253,9 @@ public class Loan extends AbstractPersistable { public LoanTransaction close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Map changes, final List existingTransactionIds, final List existingReversedTransactionIds) { - + + validateAccountStatus(LoanEvent.LOAN_CLOSED); + existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); @@ -2363,6 +2383,10 @@ public class Loan extends AbstractPersistable { return status().isSubmittedAndPendingApproval(); } + private boolean isApproved() { + return status().isApproved(); + } + private boolean isNotDisbursed() { return !isDisbursed(); } @@ -3043,4 +3067,108 @@ public class Loan extends AbstractPersistable { return loanChargePerInstallments; } + private void validateAccountStatus(final LoanEvent event) { + + final List dataValidationErrors = new ArrayList(); + + switch (event) { + case LOAN_CREATED: + break; + case LOAN_APPROVED: + if (!isSubmittedAndPendingApproval()) { + final String defaultUserMessage = "Loan Account Approval is not allowed. Loan Account is not in submitted and pending approval state."; + final ApiParameterError error = ApiParameterError.generalError( + "error.msg.loan.approve.account.is.not.submitted.and.pending.state", defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_APPROVAL_UNDO: + if (!isApproved()) { + final String defaultUserMessage = "Loan Account Undo Approval is not allowed. Loan Account is not in approved state."; + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.undo.approval.account.is.not.approved", + defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_DISBURSED: + if (!(isApproved() && isNotDisbursed())) { + final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state."; + final ApiParameterError error = ApiParameterError.generalError( + "error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_DISBURSAL_UNDO: + if (!isOpen()) { + final String defaultUserMessage = "Loan Undo disbursal is not allowed. Loan Account is not active."; + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.undo.disbursal.account.is.not.active", + defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_REPAYMENT_OR_WAIVER: + if (!isOpen()) { + final String defaultUserMessage = "Loan Repayment or Waiver is not allowed. Loan Account is not active."; + final ApiParameterError error = ApiParameterError.generalError( + "error.msg.loan.repayment.or.waiver.account.is.not.active", defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_REJECTED: + if (!isClosedWrittenOff()) { + final String defaultUserMessage = "Loan application cannot be rejected. Loan Account is not in Submitted and Pending approval state."; + final ApiParameterError error = ApiParameterError.generalError( + "error.msg.loan.reject.account.is.not.submitted.pending.approval.state", defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_WITHDRAWN: + if (!isSubmittedAndPendingApproval()) { + final String defaultUserMessage = "Loan application cannot be withdrawn. Loan Account is not in Submitted and Pending approval state."; + final ApiParameterError error = ApiParameterError.generalError( + "error.msg.loan.withdrawn.account.is.not.submitted.pending.approval.state", defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case WRITE_OFF_OUTSTANDING: + if (!isOpen()) { + final String defaultUserMessage = "Loan Written off is not allowed. Loan Account is not active."; + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.writtenoff.account.is.not.active", + defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case WRITE_OFF_OUTSTANDING_UNDO: + if (!isClosedWrittenOff()) { + final String defaultUserMessage = "Loan Undo Written off is not allowed. Loan Account is not Written off."; + final ApiParameterError error = ApiParameterError.generalError( + "error.msg.loan.undo.writtenoff.account.is.not.written.off", defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case REPAID_IN_FULL: + break; + case LOAN_CHARGE_PAYMENT: + if (!isClosedWrittenOff()) { + final String defaultUserMessage = "Charge payment is not allowed. Loan Account is not Active."; + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.charge.payment.account.is.not.active", + defaultUserMessage); + dataValidationErrors.add(error); + } + break; + case LOAN_CLOSED: + if (!isClosedWrittenOff()) { + final String defaultUserMessage = "Closing Loan Account is not allowed. Loan Account is not Active."; + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.close.account.is.not.active", + defaultUserMessage); + dataValidationErrors.add(error); + } + break; + default: + break; + } + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanEvent.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanEvent.java index cb3eda664..dc25aea65 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanEvent.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanEvent.java @@ -20,7 +20,9 @@ public enum LoanEvent { LOAN_REPAYMENT_OR_WAIVER, // REPAID_IN_FULL, // WRITE_OFF_OUTSTANDING, // + WRITE_OFF_OUTSTANDING_UNDO, // LOAN_RESCHEDULE, // INTERST_REBATE_OWED, // - LOAN_OVERPAYMENT, LOAN_CHARGE_PAYMENT; + LOAN_OVERPAYMENT, LOAN_CHARGE_PAYMENT, + LOAN_CLOSED; }