diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidLoanStateTransitionException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidLoanStateTransitionException.java new file mode 100644 index 000000000..a70926258 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidLoanStateTransitionException.java @@ -0,0 +1,11 @@ +package org.mifosng.platform.exceptions; + +/** + * {@link AbstractPlatformDomainRuleException} thrown an action to transition a loan from one state to another violates a domain rule. + */ +public class InvalidLoanStateTransitionException extends AbstractPlatformDomainRuleException { + + public InvalidLoanStateTransitionException(final String action, String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) { + super("error.msg.loan." + action + postFix, defaultUserMessage, defaultUserMessageArgs); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidSignupException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidSignupException.java deleted file mode 100644 index 13b1bb5d8..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/InvalidSignupException.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.mifosng.platform.exceptions; - -public class InvalidSignupException extends RuntimeException { - - public InvalidSignupException(final Throwable e) { - super(e); - } - -} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/InvalidLoanTimelineDate.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/InvalidLoanTimelineDate.java deleted file mode 100644 index 6caca5886..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/InvalidLoanTimelineDate.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.mifosng.platform.loan.domain; - -public class InvalidLoanTimelineDate extends RuntimeException { - - private final String errorCode; - - public InvalidLoanTimelineDate(final String message, final String errorCode) { - super(message); - this.errorCode = errorCode; - } - - public String getErrorCode() { - return this.errorCode; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index fc73d63dd..d8d5ba846 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -33,6 +33,7 @@ import org.mifosng.platform.api.data.MoneyData; import org.mifosng.platform.client.domain.Client; import org.mifosng.platform.currency.domain.MonetaryCurrency; import org.mifosng.platform.currency.domain.Money; +import org.mifosng.platform.exceptions.InvalidLoanStateTransitionException; import org.mifosng.platform.infrastructure.AbstractAuditableCustom; import org.mifosng.platform.loanproduct.service.LoanEnumerations; import org.mifosng.platform.organisation.domain.Organisation; @@ -72,6 +73,7 @@ public class Loan extends AbstractAuditableCustom { @Column(name = "rejectedon_date") private Date rejectedOnDate; + @SuppressWarnings("unused") @Temporal(TemporalType.TIMESTAMP) @Column(name = "withdrawnon_date") private Date withdrawnOnDate; @@ -199,16 +201,14 @@ public class Loan extends AbstractAuditableCustom { this.interestChargedFromDate = interestChargedFromDate.toDateMidnight().toDate(); } - if (new LocalDate(this.submittedOnDate).isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The loan application submit date cannot be in the future.", - "invalid.submit.date.as.date.is.in.future"); + if (submittedOn.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a loan is submitted cannot be in the future."; + throw new InvalidLoanStateTransitionException("submittal", "cannot.be.a.future.date", errorMessage, submittedOn); } - if (new LocalDate(this.submittedOnDate).isAfter(new LocalDate( - this.expectedDisbursedOnDate))) { - throw new InvalidLoanTimelineDate( - "The loan application submit date cannot be after its expected disbursement date.", - "invalid.submit.date.as.date.is.after.expected.disbursement.date"); + + if (submittedOn.isAfter(getExpectedDisbursedOnLocalDate())) { + final String errorMessage = "The date on which a loan is submitted cannot be after its expected disbursement date: " + getExpectedDisbursedOnLocalDate().toString(); + throw new InvalidLoanStateTransitionException("submittal", "cannot.be.after.expected.disbursement.date", errorMessage, submittedOn, getExpectedDisbursedOnLocalDate()); } } @@ -218,16 +218,14 @@ public class Loan extends AbstractAuditableCustom { this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate(); this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate(); - if (new LocalDate(this.rejectedOnDate).isBefore(new LocalDate( - this.submittedOnDate))) { - throw new InvalidLoanTimelineDate( - "The loan rejection date cannot be before its submittal date.", - "invalid.rejection.date.as.date.is.before.submittal.date"); + + if (rejectedOn.isBefore(getSubmittedOnDate())) { + final String errorMessage = "The date on which a loan is rejected cannot be before its submittal date: " + getSubmittedOnDate().toString(); + throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, rejectedOn, getSubmittedOnDate()); } - if (new LocalDate(this.rejectedOnDate).isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The loan application rejection date cannot be in the future.", - "invalid.rejection.date.as.date.is.in.future"); + if (rejectedOn.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a loan is rejected cannot be in the future."; + throw new InvalidLoanStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, rejectedOn); } } @@ -237,32 +235,29 @@ public class Loan extends AbstractAuditableCustom { this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate(); this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate(); - if (new LocalDate(this.withdrawnOnDate).isBefore(new LocalDate( - this.submittedOnDate))) { - throw new InvalidLoanTimelineDate( - "The date of when loan is withdrawn cannot be before its submittal date.", - "invalid.withdrawal.date.as.date.is.before.submittal.date"); + + if (withdrawnOn.isBefore(getSubmittedOnDate())) { + final String errorMessage = "The date on which a loan is withdrawn cannot be before its submittal date: " + getSubmittedOnDate().toString(); + throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, withdrawnOn, getSubmittedOnDate()); } - if (new LocalDate(this.withdrawnOnDate).isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The loan application withdraw date cannot be in the future.", - "invalid.withdrawal.date.as.date.is.in.future"); + if (withdrawnOn.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a loan is withdrawn cannot be in the future."; + throw new InvalidLoanStateTransitionException("reject", "cannot.be.a.future.date", errorMessage, withdrawnOn); } } public void approve(final LocalDate approvedOn, LoanLifecycleStateMachine loanLifecycleStateMachine) { this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, this.loanStatus); this.approvedOnDate = approvedOn.toDateTimeAtCurrentTime().toDate(); - if (new LocalDate(this.approvedOnDate).isBefore(new LocalDate( - this.submittedOnDate))) { - throw new InvalidLoanTimelineDate( - "The date of when loan is approved cannot be before its submittal date.", - "invalid.approval.date.as.date.is.before.submittal.date"); + + LocalDate submittalDate = new LocalDate(this.submittedOnDate); + if (approvedOn.isBefore(submittalDate)) { + final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: " + submittalDate.toString(); + throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getApprovedOnDate(), submittalDate); } - if (new LocalDate(this.approvedOnDate).isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The date of when loan is approved cannot be in the future.", - "invalid.approval.date.as.date.is.in.future"); + if (approvedOn.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a loan is approved cannot be in the future."; + throw new InvalidLoanStateTransitionException("approval", "cannot.be.a.future.date", errorMessage, getApprovedOnDate()); } } @@ -273,7 +268,6 @@ public class Loan extends AbstractAuditableCustom { public void disburseWithModifiedRepaymentSchedule( final LocalDate disbursedOn, - final String comment, final List modifiedLoanRepaymentSchedule, LoanLifecycleStateMachine loanLifecycleStateMachine) { this.repaymentScheduleInstallments.clear(); for (LoanRepaymentScheduleInstallment modifiedInstallment : modifiedLoanRepaymentSchedule) { @@ -296,23 +290,21 @@ public class Loan extends AbstractAuditableCustom { loanTransaction.updateLoan(this); loanTransaction.updateOrganisation(organisation); this.loanTransactions.add(loanTransaction); - - if (disbursedOn.isBefore(new LocalDate(this.approvedOnDate))) { - throw new InvalidLoanTimelineDate( - "The date of when loan is disbursed cannot be before its approval date.", - "invalid.disbursal.as.disbursement.date.is.before.approved.date"); + + if (disbursedOn.isBefore(getApprovedOnDate())) { + final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: " + getApprovedOnDate().toString(); + throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.before.approval.date", errorMessage, disbursedOn, getApprovedOnDate()); } + if (disbursedOn.isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The date of when loan is disbursed cannot be in the future.", - "invalid.disbursal.as.disbursement.date.is.in.future"); + final String errorMessage = "The date on which a loan is disbursed cannot be in the future."; + throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.a.future.date", errorMessage, disbursedOn); } - LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments - .get(0).getDueDate(); + + LocalDate firstRepaymentDueDate = this.repaymentScheduleInstallments.get(0).getDueDate(); if (disbursedOn.isAfter(firstRepaymentDueDate)) { - throw new InvalidLoanTimelineDate( - "The date of when loan is disbursed cannot be after the first expected repayment.", - "invalid.disbursal.as.disbursement.date.is.after.first.repayment.due.date"); + final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: " + firstRepaymentDueDate.toString(); + throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.first.repayment.due.date", errorMessage, disbursedOn, firstRepaymentDueDate); } } @@ -328,18 +320,17 @@ public class Loan extends AbstractAuditableCustom { loanTransaction.updateOrganisation(organisation); this.loanTransactions.add(loanTransaction); - if (loanTransaction.getTransactionDate().isBefore( - this.getDisbursementDate())) { - throw new InvalidLoanTimelineDate( - "The repayment date cannot be before the loan disbursement date.", - "invalid.repayment.date.as.date.is.before.disbursement.date"); + LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); + if (loanTransactionDate.isBefore(this.getDisbursementDate())) { + final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString(); + throw new InvalidLoanStateTransitionException("waive", "cannot.be.before.disbursement.date", errorMessage, loanTransactionDate, this.getDisbursementDate()); } - if (loanTransaction.getTransactionDate().isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The repayment date cannot be in the future.", - "invalid.repayment.date.as.date.is.in.future"); + + if (loanTransactionDate.isAfter(new LocalDate())) { + final String errorMessage = "The transaction date cannot be in the future."; + throw new InvalidLoanStateTransitionException("waive", "cannot.be.a.futre.date", errorMessage, loanTransactionDate); } - + if (this.isRepaidInFull()) { this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this.loanStatus); this.closedOnDate = loanTransaction.getTransactionDate().toDate(); @@ -368,18 +359,18 @@ public class Loan extends AbstractAuditableCustom { throw new IllegalArgumentException( "Only repayment transactions can be passed to makeRepayment."); } + + LocalDate loanTransactionDate = loanTransaction.getTransactionDate(); + if (loanTransactionDate.isBefore(this.getDisbursementDate())) { + final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString(); + throw new InvalidLoanStateTransitionException("repayment", "cannot.be.before.disbursement.date", errorMessage, loanTransactionDate, this.getDisbursementDate()); + } + + if (loanTransactionDate.isAfter(new LocalDate())) { + final String errorMessage = "The transaction date cannot be in the future."; + throw new InvalidLoanStateTransitionException("repayment", "cannot.be.a.futre.date", errorMessage, loanTransactionDate); + } - if (loanTransaction.getTransactionDate().isBefore( - this.getDisbursementDate())) { - throw new InvalidLoanTimelineDate( - "The repayment date cannot be before the loan disbursement date.", - "invalid.repayment.date.as.date.is.before.disbursement.date"); - } - if (loanTransaction.getTransactionDate().isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The repayment date cannot be in the future.", - "invalid.repayment.date.as.date.is.in.future"); - } if (this.isRepaidInFull()) { this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this.loanStatus); this.closedOnDate = loanTransaction.getTransactionDate().toDate(); @@ -529,16 +520,15 @@ public class Loan extends AbstractAuditableCustom { this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WRITE_OFF, this.loanStatus); this.closedOnDate = writtenOffOn.toDate(); this.writtenOffOnDate = writtenOffOn.toDate(); - if (new LocalDate(this.writtenOffOnDate).isBefore(this - .getDisbursementDate())) { - throw new InvalidLoanTimelineDate( - "The date the loan is written off cannot be before the loan disbursement date.", - "invalid.writeoff.date.as.date.is.before.disbursement.date"); + + LocalDate writtenOffOnLocalDate = new LocalDate(writtenOffOnDate); + if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) { + final String errorMessage = "The date on which a loan is withdrawn cannot be before the loan disbursement date: " + getDisbursementDate().toString(); + throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage, writtenOffOnLocalDate, getDisbursementDate()); } - if (new LocalDate(this.writtenOffOnDate).isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The date the loan is written off cannot be in the future.", - "invalid.writeoff.date.as.date.is.in.future"); + if (writtenOffOnLocalDate.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a loan is written off cannot be in the future."; + throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, writtenOffOnLocalDate); } } @@ -546,16 +536,15 @@ public class Loan extends AbstractAuditableCustom { this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RESCHEDULE, this.loanStatus); this.closedOnDate = rescheduledOn.toDate(); this.rescheduledOnDate = rescheduledOn.toDate(); - if (new LocalDate(this.rescheduledOnDate).isBefore(this - .getDisbursementDate())) { - throw new InvalidLoanTimelineDate( - "The date the loan is rescheduled cannot be before the loan disbursement date.", - "invalid.reschedule.date.as.date.is.before.disbursement.date"); + + LocalDate rescheduledOnLocalDate = new LocalDate(rescheduledOnDate); + if (rescheduledOnLocalDate.isBefore(this.getDisbursementDate())) { + final String errorMessage = "The date on which a loan is rescheduled cannot be before the loan disbursement date: " + getDisbursementDate().toString(); + throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage, rescheduledOnLocalDate, getDisbursementDate()); } - if (new LocalDate(this.rescheduledOnDate).isAfter(new LocalDate())) { - throw new InvalidLoanTimelineDate( - "The date the loan is rescheduled cannot be in the future.", - "invalid.reschedule.date.as.date.is.in.future"); + if (rescheduledOnLocalDate.isAfter(new LocalDate())) { + final String errorMessage = "The date on which a loan is rescheduled cannot be in the future."; + throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.a.future.date", errorMessage, rescheduledOnLocalDate); } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java index 02aa0849f..46288adca 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanTransactionCommandValidator.java @@ -24,7 +24,7 @@ public class LoanTransactionCommandValidator { baseDataValidator.reset().parameter("loanid").value(command.getLoanId()).notNull().greaterThanZero(); baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionLocalDate()).notNull(); - baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmount()).notNull().positiveAmount(); + baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmountValue()).notNull().positiveAmount(); baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000); if (!dataValidationErrors.isEmpty()) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java index e15703702..89b41fd1e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -312,7 +312,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf interest.getAmount()); modifiedLoanRepaymentSchedule.add(installment); } - loan.disburseWithModifiedRepaymentSchedule(actualDisbursementDate, noteText, modifiedLoanRepaymentSchedule, defaultLoanLifecycleStateMachine()); + loan.disburseWithModifiedRepaymentSchedule(actualDisbursementDate, modifiedLoanRepaymentSchedule, defaultLoanLifecycleStateMachine()); } else { loan.disburse(actualDisbursementDate, defaultLoanLifecycleStateMachine()); }