wip loan permissions
This commit is contained in:
@@ -26,6 +26,7 @@ import org.mifosng.platform.api.data.EntityIdentifier;
|
|||||||
import org.mifosng.platform.api.data.LoanAccountData;
|
import org.mifosng.platform.api.data.LoanAccountData;
|
||||||
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
||||||
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
||||||
|
import org.mifosng.platform.api.data.LoanPermissionData;
|
||||||
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
||||||
import org.mifosng.platform.api.data.LoanSchedule;
|
import org.mifosng.platform.api.data.LoanSchedule;
|
||||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||||
@@ -95,17 +96,14 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService
|
LoanBasicDetailsData loanBasicDetails = this.loanReadPlatformService
|
||||||
.retrieveLoanAccountDetails(loanId);
|
.retrieveLoanAccountDetails(loanId);
|
||||||
//LoanAccountData loanAccount = this.loanReadPlatformService.convertToData(loanBasicDetails);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Collection<LoanTransactionDatajpw> loanRepayments = this.loanReadPlatformService.retrieveLoanPayments(loanId);
|
Collection<LoanTransactionDatajpw> loanRepayments = this.loanReadPlatformService.retrieveLoanPayments(loanId);
|
||||||
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId);
|
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId);
|
||||||
|
|
||||||
LoanAccountSummaryData summary = this.loanReadPlatformService.retrieveSummary(loanBasicDetails.getPrincipal(), repaymentSchedule, loanRepayments);
|
LoanAccountSummaryData summary = this.loanReadPlatformService.retrieveSummary(loanBasicDetails.getPrincipal(), repaymentSchedule, loanRepayments);
|
||||||
|
LoanPermissionData permissions = this.loanReadPlatformService.retrieveLoanPermissions(loanBasicDetails, summary.isWaiveAllowed(loanBasicDetails.getInArrearsTolerance()));
|
||||||
|
|
||||||
|
LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, summary, repaymentSchedule, loanRepayments, permissions);
|
||||||
LoanAccountData loanAccount = new LoanAccountData(loanBasicDetails, summary, repaymentSchedule, loanRepayments, null );
|
|
||||||
|
|
||||||
String selectedFields = "";
|
String selectedFields = "";
|
||||||
String associatedFields = "summary,repaymentSchedule,loanRepayments,permissions,convenienceData";
|
String associatedFields = "summary,repaymentSchedule,loanRepayments,permissions,convenienceData";
|
||||||
|
|||||||
+1
@@ -21,6 +21,7 @@ import org.mifosng.platform.loanproduct.service.LoanEnumerations;
|
|||||||
// FIXME - The derived details of the loan schedule are now updated on the fly when someone does a transaction against a loan. So this is not fully needed.
|
// FIXME - The derived details of the loan schedule are now updated on the fly when someone does a transaction against a loan. So this is not fully needed.
|
||||||
// - we still use this to figure out how much of a given transactions amount was principal versus interest but might not need this and we should probably
|
// - we still use this to figure out how much of a given transactions amount was principal versus interest but might not need this and we should probably
|
||||||
// - derive and update that on the database on the fly
|
// - derive and update that on the database on the fly
|
||||||
|
// FIXME - JPW - the above fixme was done and I can't see anything else using this method or whole class
|
||||||
public class DerivedLoanDataProcessor {
|
public class DerivedLoanDataProcessor {
|
||||||
|
|
||||||
public DerivedLoanData process(List<LoanRepaymentScheduleInstallment> repaymentSchedulePeriods, List<LoanTransaction> loanTransactions,
|
public DerivedLoanData process(List<LoanRepaymentScheduleInstallment> repaymentSchedulePeriods, List<LoanTransaction> loanTransactions,
|
||||||
|
|||||||
@@ -25,14 +25,6 @@ import org.hibernate.annotations.LazyCollection;
|
|||||||
import org.hibernate.annotations.LazyCollectionOption;
|
import org.hibernate.annotations.LazyCollectionOption;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosng.platform.api.data.CurrencyData;
|
|
||||||
import org.mifosng.platform.api.data.DerivedLoanData;
|
|
||||||
import org.mifosng.platform.api.data.LoanAccountData;
|
|
||||||
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
|
||||||
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
|
||||||
import org.mifosng.platform.api.data.LoanPermissionData;
|
|
||||||
import org.mifosng.platform.api.data.LoanRepaymentScheduleData;
|
|
||||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
|
||||||
import org.mifosng.platform.client.domain.Client;
|
import org.mifosng.platform.client.domain.Client;
|
||||||
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||||
import org.mifosng.platform.currency.domain.Money;
|
import org.mifosng.platform.currency.domain.Money;
|
||||||
@@ -43,7 +35,7 @@ import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
|
|||||||
import org.mifosng.platform.user.domain.AppUser;
|
import org.mifosng.platform.user.domain.AppUser;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "portfolio_loan", uniqueConstraints = @UniqueConstraint(columnNames = {"external_id" }))
|
@Table(name = "portfolio_loan", uniqueConstraints = @UniqueConstraint(columnNames = { "external_id" }))
|
||||||
public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@@ -140,8 +132,11 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
@Transient
|
@Transient
|
||||||
private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory();
|
private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory();
|
||||||
|
|
||||||
public static Loan createNew(final Fund fund, final LoanProduct loanProduct, final Client client, final LoanProductRelatedDetail loanRepaymentScheduleDetail) {
|
public static Loan createNew(final Fund fund,
|
||||||
return new Loan(client, fund, loanProduct, loanRepaymentScheduleDetail, null);
|
final LoanProduct loanProduct, final Client client,
|
||||||
|
final LoanProductRelatedDetail loanRepaymentScheduleDetail) {
|
||||||
|
return new Loan(client, fund, loanProduct, loanRepaymentScheduleDetail,
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Loan() {
|
public Loan() {
|
||||||
@@ -150,8 +145,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.loanRepaymentScheduleDetail = null;
|
this.loanRepaymentScheduleDetail = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Loan(final Client client,
|
public Loan(final Client client, Fund fund, final LoanProduct loanProduct,
|
||||||
Fund fund, final LoanProduct loanProduct,
|
|
||||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||||
final LoanStatus loanStatus) {
|
final LoanStatus loanStatus) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
@@ -182,10 +176,14 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return this.loanRepaymentScheduleDetail;
|
return this.loanRepaymentScheduleDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submitApplication(final LocalDate submittedOn, final LocalDate expectedDisbursementDate,
|
public void submitApplication(final LocalDate submittedOn,
|
||||||
LocalDate repaymentsStartingFromDate, LocalDate interestChargedFromDate, LoanLifecycleStateMachine lifecycleStateMachine) {
|
final LocalDate expectedDisbursementDate,
|
||||||
|
LocalDate repaymentsStartingFromDate,
|
||||||
|
LocalDate interestChargedFromDate,
|
||||||
|
LoanLifecycleStateMachine lifecycleStateMachine) {
|
||||||
|
|
||||||
this.loanStatus = lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, this.loanStatus);
|
this.loanStatus = lifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_CREATED, this.loanStatus);
|
||||||
|
|
||||||
this.submittedOnDate = submittedOn.toDateTimeAtCurrentTime().toDate();
|
this.submittedOnDate = submittedOn.toDateTimeAtCurrentTime().toDate();
|
||||||
|
|
||||||
@@ -194,85 +192,113 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
.getDueDate().toDateMidnight().toDate();
|
.getDueDate().toDateMidnight().toDate();
|
||||||
if (expectedDisbursementDate != null) {
|
if (expectedDisbursementDate != null) {
|
||||||
// can be null during bulk upload of loans
|
// can be null during bulk upload of loans
|
||||||
this.expectedDisbursedOnDate = expectedDisbursementDate.toDateMidnight().toDate();
|
this.expectedDisbursedOnDate = expectedDisbursementDate
|
||||||
|
.toDateMidnight().toDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repaymentsStartingFromDate != null) {
|
if (repaymentsStartingFromDate != null) {
|
||||||
this.expectedFirstRepaymentOnDate = repaymentsStartingFromDate.toDateMidnight().toDate();
|
this.expectedFirstRepaymentOnDate = repaymentsStartingFromDate
|
||||||
|
.toDateMidnight().toDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interestChargedFromDate != null) {
|
if (interestChargedFromDate != null) {
|
||||||
this.interestChargedFromDate = interestChargedFromDate.toDateMidnight().toDate();
|
this.interestChargedFromDate = interestChargedFromDate
|
||||||
|
.toDateMidnight().toDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submittedOn.isAfter(new LocalDate())) {
|
if (submittedOn.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is submitted cannot be in the future.";
|
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);
|
throw new InvalidLoanStateTransitionException("submittal",
|
||||||
|
"cannot.be.a.future.date", errorMessage, submittedOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submittedOn.isAfter(getExpectedDisbursedOnLocalDate())) {
|
if (submittedOn.isAfter(getExpectedDisbursedOnLocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is submitted cannot be after its expected disbursement date: " + getExpectedDisbursedOnLocalDate().toString();
|
final String errorMessage = "The date on which a loan is submitted cannot be after its expected disbursement date: "
|
||||||
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.after.expected.disbursement.date", errorMessage, submittedOn, getExpectedDisbursedOnLocalDate());
|
+ getExpectedDisbursedOnLocalDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("submittal",
|
||||||
|
"cannot.be.after.expected.disbursement.date", errorMessage,
|
||||||
|
submittedOn, getExpectedDisbursedOnLocalDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reject(final LocalDate rejectedOn, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void reject(final LocalDate rejectedOn,
|
||||||
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REJECTED, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_REJECTED, this.loanStatus);
|
||||||
|
|
||||||
this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
this.rejectedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
||||||
this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
this.closedOnDate = rejectedOn.toDateTimeAtCurrentTime().toDate();
|
||||||
|
|
||||||
if (rejectedOn.isBefore(getSubmittedOnDate())) {
|
if (rejectedOn.isBefore(getSubmittedOnDate())) {
|
||||||
final String errorMessage = "The date on which a loan is rejected cannot be before its submittal date: " + getSubmittedOnDate().toString();
|
final String errorMessage = "The date on which a loan is rejected cannot be before its submittal date: "
|
||||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, rejectedOn, getSubmittedOnDate());
|
+ getSubmittedOnDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("reject",
|
||||||
|
"cannot.be.before.submittal.date", errorMessage,
|
||||||
|
rejectedOn, getSubmittedOnDate());
|
||||||
}
|
}
|
||||||
if (rejectedOn.isAfter(new LocalDate())) {
|
if (rejectedOn.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is rejected cannot be in the future.";
|
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);
|
throw new InvalidLoanStateTransitionException("reject",
|
||||||
|
"cannot.be.a.future.date", errorMessage, rejectedOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void withdraw(final LocalDate withdrawnOn, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void withdraw(final LocalDate withdrawnOn,
|
||||||
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAWN, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_WITHDRAWN, this.loanStatus);
|
||||||
|
|
||||||
this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
this.withdrawnOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
||||||
this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
this.closedOnDate = withdrawnOn.toDateTimeAtCurrentTime().toDate();
|
||||||
|
|
||||||
if (withdrawnOn.isBefore(getSubmittedOnDate())) {
|
if (withdrawnOn.isBefore(getSubmittedOnDate())) {
|
||||||
final String errorMessage = "The date on which a loan is withdrawn cannot be before its submittal date: " + getSubmittedOnDate().toString();
|
final String errorMessage = "The date on which a loan is withdrawn cannot be before its submittal date: "
|
||||||
throw new InvalidLoanStateTransitionException("reject", "cannot.be.before.submittal.date", errorMessage, withdrawnOn, getSubmittedOnDate());
|
+ getSubmittedOnDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("reject",
|
||||||
|
"cannot.be.before.submittal.date", errorMessage,
|
||||||
|
withdrawnOn, getSubmittedOnDate());
|
||||||
}
|
}
|
||||||
if (withdrawnOn.isAfter(new LocalDate())) {
|
if (withdrawnOn.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is withdrawn cannot be in the future.";
|
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);
|
throw new InvalidLoanStateTransitionException("reject",
|
||||||
|
"cannot.be.a.future.date", errorMessage, withdrawnOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void approve(final LocalDate approvedOn, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void approve(final LocalDate approvedOn,
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVED, this.loanStatus);
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_APPROVED, this.loanStatus);
|
||||||
this.approvedOnDate = approvedOn.toDateTimeAtCurrentTime().toDate();
|
this.approvedOnDate = approvedOn.toDateTimeAtCurrentTime().toDate();
|
||||||
|
|
||||||
LocalDate submittalDate = new LocalDate(this.submittedOnDate);
|
LocalDate submittalDate = new LocalDate(this.submittedOnDate);
|
||||||
if (approvedOn.isBefore(submittalDate)) {
|
if (approvedOn.isBefore(submittalDate)) {
|
||||||
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: " + submittalDate.toString();
|
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: "
|
||||||
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, getApprovedOnDate(), submittalDate);
|
+ submittalDate.toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("approval",
|
||||||
|
"cannot.be.before.submittal.date", errorMessage,
|
||||||
|
getApprovedOnDate(), submittalDate);
|
||||||
}
|
}
|
||||||
if (approvedOn.isAfter(new LocalDate())) {
|
if (approvedOn.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is approved cannot be in the future.";
|
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());
|
throw new InvalidLoanStateTransitionException("approval",
|
||||||
|
"cannot.be.a.future.date", errorMessage,
|
||||||
|
getApprovedOnDate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void undoApproval(LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void undoApproval(LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_APPROVAL_UNDO, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_APPROVAL_UNDO, this.loanStatus);
|
||||||
this.approvedOnDate = null;
|
this.approvedOnDate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disburseWithModifiedRepaymentSchedule(
|
public void disburseWithModifiedRepaymentSchedule(
|
||||||
final LocalDate disbursedOn,
|
final LocalDate disbursedOn,
|
||||||
final List<LoanRepaymentScheduleInstallment> modifiedLoanRepaymentSchedule, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
final List<LoanRepaymentScheduleInstallment> modifiedLoanRepaymentSchedule,
|
||||||
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
this.repaymentScheduleInstallments.clear();
|
this.repaymentScheduleInstallments.clear();
|
||||||
for (LoanRepaymentScheduleInstallment modifiedInstallment : modifiedLoanRepaymentSchedule) {
|
for (LoanRepaymentScheduleInstallment modifiedInstallment : modifiedLoanRepaymentSchedule) {
|
||||||
modifiedInstallment.updateLoan(this);
|
modifiedInstallment.updateLoan(this);
|
||||||
@@ -281,8 +307,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
disburse(disbursedOn, loanLifecycleStateMachine);
|
disburse(disbursedOn, loanLifecycleStateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disburse(final LocalDate disbursedOn, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void disburse(final LocalDate disbursedOn,
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, this.loanStatus);
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_DISBURSED, this.loanStatus);
|
||||||
this.disbursedOnDate = disbursedOn.toDateTimeAtCurrentTime().toDate();
|
this.disbursedOnDate = disbursedOn.toDateTimeAtCurrentTime().toDate();
|
||||||
this.expectedMaturityDate = this.repaymentScheduleInstallments
|
this.expectedMaturityDate = this.repaymentScheduleInstallments
|
||||||
.get(this.repaymentScheduleInstallments.size() - 1)
|
.get(this.repaymentScheduleInstallments.size() - 1)
|
||||||
@@ -294,57 +322,82 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
this.loanTransactions.add(loanTransaction);
|
this.loanTransactions.add(loanTransaction);
|
||||||
|
|
||||||
if (disbursedOn.isBefore(getApprovedOnDate())) {
|
if (disbursedOn.isBefore(getApprovedOnDate())) {
|
||||||
final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: " + getApprovedOnDate().toString();
|
final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: "
|
||||||
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.before.approval.date", errorMessage, disbursedOn, getApprovedOnDate());
|
+ getApprovedOnDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("disbursal",
|
||||||
|
"cannot.be.before.approval.date", errorMessage,
|
||||||
|
disbursedOn, getApprovedOnDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disbursedOn.isAfter(new LocalDate())) {
|
if (disbursedOn.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is disbursed cannot be in the 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);
|
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)) {
|
if (disbursedOn.isAfter(firstRepaymentDueDate)) {
|
||||||
final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: " + firstRepaymentDueDate.toString();
|
final String errorMessage = "The date on which a loan is disbursed cannot be after the first expected repayment date: "
|
||||||
throw new InvalidLoanStateTransitionException("disbursal", "cannot.be.after.first.repayment.due.date", errorMessage, disbursedOn, firstRepaymentDueDate);
|
+ firstRepaymentDueDate.toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("disbursal",
|
||||||
|
"cannot.be.after.first.repayment.due.date", errorMessage,
|
||||||
|
disbursedOn, firstRepaymentDueDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void undoDisbursal(LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void undoDisbursal(
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, this.loanStatus);
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_DISBURSAL_UNDO, this.loanStatus);
|
||||||
this.loanTransactions.clear();
|
this.loanTransactions.clear();
|
||||||
this.disbursedOnDate = null;
|
this.disbursedOnDate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waive(LoanTransaction loanTransaction, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void waive(LoanTransaction loanTransaction,
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT, this.loanStatus);
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_REPAYMENT, this.loanStatus);
|
||||||
loanTransaction.updateLoan(this);
|
loanTransaction.updateLoan(this);
|
||||||
this.loanTransactions.add(loanTransaction);
|
this.loanTransactions.add(loanTransaction);
|
||||||
|
|
||||||
LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
||||||
if (loanTransactionDate.isBefore(this.getDisbursementDate())) {
|
if (loanTransactionDate.isBefore(this.getDisbursementDate())) {
|
||||||
final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString();
|
final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
|
||||||
throw new InvalidLoanStateTransitionException("waive", "cannot.be.before.disbursement.date", errorMessage, loanTransactionDate, this.getDisbursementDate());
|
+ getApprovedOnDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("waive",
|
||||||
|
"cannot.be.before.disbursement.date", errorMessage,
|
||||||
|
loanTransactionDate, this.getDisbursementDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loanTransactionDate.isAfter(new LocalDate())) {
|
if (loanTransactionDate.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The transaction date cannot be in the future.";
|
final String errorMessage = "The transaction date cannot be in the future.";
|
||||||
throw new InvalidLoanStateTransitionException("waive", "cannot.be.a.futre.date", errorMessage, loanTransactionDate);
|
throw new InvalidLoanStateTransitionException("waive",
|
||||||
|
"cannot.be.a.futre.date", errorMessage, loanTransactionDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getTotalOutstanding().isGreaterThan(this.getInArrearsTolerance())) {
|
if (getTotalOutstanding().isGreaterThan(this.getInArrearsTolerance())) {
|
||||||
final String errorMessage = "Waiver is only allowed when the total outstanding amount left on loan (" + getTotalOutstanding() +") is less than the in arrears tolerance setting of " + getInArrearsTolerance().getAmount();
|
final String errorMessage = "Waiver is only allowed when the total outstanding amount left on loan ("
|
||||||
throw new InvalidLoanStateTransitionException("waive", "cannot.exceed.in.arrears.tolerance.setting", errorMessage, getTotalOutstanding(), getInArrearsTolerance());
|
+ getTotalOutstanding()
|
||||||
|
+ ") is less than the in arrears tolerance setting of "
|
||||||
|
+ getInArrearsTolerance().getAmount();
|
||||||
|
throw new InvalidLoanStateTransitionException("waive",
|
||||||
|
"cannot.exceed.in.arrears.tolerance.setting", errorMessage,
|
||||||
|
getTotalOutstanding(), getInArrearsTolerance());
|
||||||
}
|
}
|
||||||
|
|
||||||
Money waived = Money.of(getCurrency(), loanTransaction.getAmount());
|
Money waived = Money.of(getCurrency(), loanTransaction.getAmount());
|
||||||
if (waived.isGreaterThan(this.getInArrearsTolerance())) {
|
if (waived.isGreaterThan(this.getInArrearsTolerance())) {
|
||||||
final String errorMessage = "The amount being waived cannot exceed the in arrears tolerance setting of " + getInArrearsTolerance().getAmount();
|
final String errorMessage = "The amount being waived cannot exceed the in arrears tolerance setting of "
|
||||||
throw new InvalidLoanStateTransitionException("waive", "cannot.exceed.in.arrears.tolerance.setting", errorMessage, waived, getInArrearsTolerance());
|
+ getInArrearsTolerance().getAmount();
|
||||||
|
throw new InvalidLoanStateTransitionException("waive",
|
||||||
|
"cannot.exceed.in.arrears.tolerance.setting", errorMessage,
|
||||||
|
waived, getInArrearsTolerance());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isRepaidInFull()) {
|
if (this.isRepaidInFull()) {
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.REPAID_IN_FULL, this.loanStatus);
|
||||||
this.closedOnDate = loanTransaction.getTransactionDate().toDate();
|
this.closedOnDate = loanTransaction.getTransactionDate().toDate();
|
||||||
this.maturedOnDate = loanTransaction.getTransactionDate().toDate();
|
this.maturedOnDate = loanTransaction.getTransactionDate().toDate();
|
||||||
|
|
||||||
@@ -352,15 +405,18 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
Money rebateDue = calculateRebateWhenPaidInFullOn(loanTransaction
|
Money rebateDue = calculateRebateWhenPaidInFullOn(loanTransaction
|
||||||
.getTransactionDate());
|
.getTransactionDate());
|
||||||
if (rebateDue.isGreaterThanZero()) {
|
if (rebateDue.isGreaterThanZero()) {
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.INTERST_REBATE_OWED, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.INTERST_REBATE_OWED, this.loanStatus);
|
||||||
this.interestRebateOwed = rebateDue.getAmount();
|
this.interestRebateOwed = rebateDue.getAmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeRepayment(final LoanTransaction loanTransaction, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void makeRepayment(final LoanTransaction loanTransaction,
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT, this.loanStatus);
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_REPAYMENT, this.loanStatus);
|
||||||
loanTransaction.updateLoan(this);
|
loanTransaction.updateLoan(this);
|
||||||
this.loanTransactions.add(loanTransaction);
|
this.loanTransactions.add(loanTransaction);
|
||||||
|
|
||||||
@@ -368,22 +424,28 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
|
|
||||||
if (loanTransaction.isNotRepayment()) {
|
if (loanTransaction.isNotRepayment()) {
|
||||||
final String errorMessage = "A transaction of type repayment was expected but not received.";
|
final String errorMessage = "A transaction of type repayment was expected but not received.";
|
||||||
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.transaction", errorMessage);
|
throw new InvalidLoanTransactionTypeException("transaction",
|
||||||
|
"is.not.a.repayment.transaction", errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
||||||
if (loanTransactionDate.isBefore(this.getDisbursementDate())) {
|
if (loanTransactionDate.isBefore(this.getDisbursementDate())) {
|
||||||
final String errorMessage = "The transaction date cannot be before the loan disbursement date: " + getApprovedOnDate().toString();
|
final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
|
||||||
throw new InvalidLoanStateTransitionException("repayment", "cannot.be.before.disbursement.date", errorMessage, loanTransactionDate, this.getDisbursementDate());
|
+ getApprovedOnDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("repayment",
|
||||||
|
"cannot.be.before.disbursement.date", errorMessage,
|
||||||
|
loanTransactionDate, this.getDisbursementDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loanTransactionDate.isAfter(new LocalDate())) {
|
if (loanTransactionDate.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The transaction date cannot be in the future.";
|
final String errorMessage = "The transaction date cannot be in the future.";
|
||||||
throw new InvalidLoanStateTransitionException("repayment", "cannot.be.a.futre.date", errorMessage, loanTransactionDate);
|
throw new InvalidLoanStateTransitionException("repayment",
|
||||||
|
"cannot.be.a.futre.date", errorMessage, loanTransactionDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isRepaidInFull()) {
|
if (this.isRepaidInFull()) {
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.REPAID_IN_FULL, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.REPAID_IN_FULL, this.loanStatus);
|
||||||
this.closedOnDate = loanTransaction.getTransactionDate().toDate();
|
this.closedOnDate = loanTransaction.getTransactionDate().toDate();
|
||||||
this.maturedOnDate = loanTransaction.getTransactionDate().toDate();
|
this.maturedOnDate = loanTransaction.getTransactionDate().toDate();
|
||||||
|
|
||||||
@@ -391,7 +453,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
Money rebateDue = calculateRebateWhenPaidInFullOn(loanTransaction
|
Money rebateDue = calculateRebateWhenPaidInFullOn(loanTransaction
|
||||||
.getTransactionDate());
|
.getTransactionDate());
|
||||||
if (rebateDue.isGreaterThanZero()) {
|
if (rebateDue.isGreaterThanZero()) {
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.INTERST_REBATE_OWED, this.loanStatus);
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.INTERST_REBATE_OWED, this.loanStatus);
|
||||||
this.interestRebateOwed = rebateDue.getAmount();
|
this.interestRebateOwed = rebateDue.getAmount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,18 +466,23 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
Money totalRepaidOrWaivedAgainstLoan = calculateTotalPaidOrWaived();
|
Money totalRepaidOrWaivedAgainstLoan = calculateTotalPaidOrWaived();
|
||||||
|
|
||||||
Money remainingToPayoffAgainstLoanSchedule = totalRepaidOrWaivedAgainstLoan;
|
Money remainingToPayoffAgainstLoanSchedule = totalRepaidOrWaivedAgainstLoan;
|
||||||
Money totalOverpaid = Money.zero(totalRepaidOrWaivedAgainstLoan.getCurrency());
|
Money totalOverpaid = Money.zero(totalRepaidOrWaivedAgainstLoan
|
||||||
|
.getCurrency());
|
||||||
int repaymentInstallmentIndex = 0;
|
int repaymentInstallmentIndex = 0;
|
||||||
while (remainingToPayoffAgainstLoanSchedule.isGreaterThanZero()) {
|
while (remainingToPayoffAgainstLoanSchedule.isGreaterThanZero()) {
|
||||||
|
|
||||||
if (repaymentInstallmentIndex == this.repaymentScheduleInstallments.size()) {
|
if (repaymentInstallmentIndex == this.repaymentScheduleInstallments
|
||||||
|
.size()) {
|
||||||
totalOverpaid = remainingToPayoffAgainstLoanSchedule;
|
totalOverpaid = remainingToPayoffAgainstLoanSchedule;
|
||||||
|
|
||||||
// to exit while loop
|
// to exit while loop
|
||||||
remainingToPayoffAgainstLoanSchedule = remainingToPayoffAgainstLoanSchedule.minus(totalOverpaid);
|
remainingToPayoffAgainstLoanSchedule = remainingToPayoffAgainstLoanSchedule
|
||||||
|
.minus(totalOverpaid);
|
||||||
} else {
|
} else {
|
||||||
LoanRepaymentScheduleInstallment scheduledRepaymentInstallment = this.repaymentScheduleInstallments.get(repaymentInstallmentIndex);
|
LoanRepaymentScheduleInstallment scheduledRepaymentInstallment = this.repaymentScheduleInstallments
|
||||||
remainingToPayoffAgainstLoanSchedule = scheduledRepaymentInstallment.updateDerivedComponents(remainingToPayoffAgainstLoanSchedule);
|
.get(repaymentInstallmentIndex);
|
||||||
|
remainingToPayoffAgainstLoanSchedule = scheduledRepaymentInstallment
|
||||||
|
.updateDerivedComponents(remainingToPayoffAgainstLoanSchedule);
|
||||||
repaymentInstallmentIndex++;
|
repaymentInstallmentIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,11 +490,13 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
|
|
||||||
private Money calculateTotalPaidOrWaived() {
|
private Money calculateTotalPaidOrWaived() {
|
||||||
|
|
||||||
Money totalRepaidOrWaived = Money.zero(this.loanRepaymentScheduleDetail.getPrincipal().getCurrency());
|
Money totalRepaidOrWaived = Money.zero(this.loanRepaymentScheduleDetail
|
||||||
|
.getPrincipal().getCurrency());
|
||||||
|
|
||||||
for (LoanTransaction transaction : this.loanTransactions) {
|
for (LoanTransaction transaction : this.loanTransactions) {
|
||||||
if (transaction.isRepayment() || transaction.isWaiver()) {
|
if (transaction.isRepayment() || transaction.isWaiver()) {
|
||||||
totalRepaidOrWaived = totalRepaidOrWaived.plus(transaction.getAmount());
|
totalRepaidOrWaived = totalRepaidOrWaived.plus(transaction
|
||||||
|
.getAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +520,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LocalDate possibleNextRepaymentDate = earliestUnpaidInstallmentDate;
|
LocalDate possibleNextRepaymentDate = earliestUnpaidInstallmentDate;
|
||||||
if (lastTransactionDate != null && lastTransactionDate.isAfter(earliestUnpaidInstallmentDate)) {
|
if (lastTransactionDate != null
|
||||||
|
&& lastTransactionDate.isAfter(earliestUnpaidInstallmentDate)) {
|
||||||
possibleNextRepaymentDate = lastTransactionDate;
|
possibleNextRepaymentDate = lastTransactionDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +529,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Money possibleNextRepaymentAmount() {
|
public Money possibleNextRepaymentAmount() {
|
||||||
MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getPrincipal().getCurrency();
|
MonetaryCurrency currency = this.loanRepaymentScheduleDetail
|
||||||
|
.getPrincipal().getCurrency();
|
||||||
Money possibleNextRepaymentAmount = Money.zero(currency);
|
Money possibleNextRepaymentAmount = Money.zero(currency);
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
@@ -471,12 +543,16 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return possibleNextRepaymentAmount;
|
return possibleNextRepaymentAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void adjustExistingTransaction(LoanTransaction transactionForAdjustment,
|
public void adjustExistingTransaction(
|
||||||
LoanTransaction newTransactionDetail, LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
LoanTransaction transactionForAdjustment,
|
||||||
|
LoanTransaction newTransactionDetail,
|
||||||
|
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
|
||||||
if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) {
|
if (transactionForAdjustment.isNotRepayment()
|
||||||
|
&& transactionForAdjustment.isNotWaiver()) {
|
||||||
final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
|
final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
|
||||||
throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.or.waiver.transaction", errorMessage);
|
throw new InvalidLoanTransactionTypeException("transaction",
|
||||||
|
"is.not.a.repayment.or.waiver.transaction", errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionForAdjustment.contra();
|
transactionForAdjustment.contra();
|
||||||
@@ -503,7 +579,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
.getPrincipal().getCurrency());
|
.getPrincipal().getCurrency());
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||||
cumulativePrincipal = cumulativePrincipal.plus(scheduledRepayment.getPrincipal(loanCurrency()));
|
cumulativePrincipal = cumulativePrincipal.plus(scheduledRepayment
|
||||||
|
.getPrincipal(loanCurrency()));
|
||||||
cumulativeInterest = cumulativeInterest.plus(scheduledRepayment
|
cumulativeInterest = cumulativeInterest.plus(scheduledRepayment
|
||||||
.getInterest(loanCurrency()));
|
.getInterest(loanCurrency()));
|
||||||
cumulativeTotal = cumulativeTotal.plus(scheduledRepayment
|
cumulativeTotal = cumulativeTotal.plus(scheduledRepayment
|
||||||
@@ -528,35 +605,49 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return this.loanRepaymentScheduleDetail.getCurrency();
|
return this.loanRepaymentScheduleDetail.getCurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeOff(final DateTime writtenOffOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void writeOff(final DateTime writtenOffOn,
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_WRITE_OFF, this.loanStatus);
|
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_WRITE_OFF, this.loanStatus);
|
||||||
this.closedOnDate = writtenOffOn.toDate();
|
this.closedOnDate = writtenOffOn.toDate();
|
||||||
this.writtenOffOnDate = writtenOffOn.toDate();
|
this.writtenOffOnDate = writtenOffOn.toDate();
|
||||||
|
|
||||||
LocalDate writtenOffOnLocalDate = new LocalDate(writtenOffOnDate);
|
LocalDate writtenOffOnLocalDate = new LocalDate(writtenOffOnDate);
|
||||||
if (writtenOffOnLocalDate.isBefore(this.getDisbursementDate())) {
|
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();
|
final String errorMessage = "The date on which a loan is withdrawn cannot be before the loan disbursement date: "
|
||||||
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage, writtenOffOnLocalDate, getDisbursementDate());
|
+ getDisbursementDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("writeoff",
|
||||||
|
"cannot.be.before.submittal.date", errorMessage,
|
||||||
|
writtenOffOnLocalDate, getDisbursementDate());
|
||||||
}
|
}
|
||||||
if (writtenOffOnLocalDate.isAfter(new LocalDate())) {
|
if (writtenOffOnLocalDate.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is written off cannot be in the future.";
|
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);
|
throw new InvalidLoanStateTransitionException("writeoff",
|
||||||
|
"cannot.be.a.future.date", errorMessage,
|
||||||
|
writtenOffOnLocalDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reschedule(final DateTime rescheduledOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
public void reschedule(final DateTime rescheduledOn,
|
||||||
this.loanStatus = loanLifecycleStateMachine.transition(LoanEvent.LOAN_RESCHEDULE, this.loanStatus);
|
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
this.loanStatus = loanLifecycleStateMachine.transition(
|
||||||
|
LoanEvent.LOAN_RESCHEDULE, this.loanStatus);
|
||||||
this.closedOnDate = rescheduledOn.toDate();
|
this.closedOnDate = rescheduledOn.toDate();
|
||||||
this.rescheduledOnDate = rescheduledOn.toDate();
|
this.rescheduledOnDate = rescheduledOn.toDate();
|
||||||
|
|
||||||
LocalDate rescheduledOnLocalDate = new LocalDate(rescheduledOnDate);
|
LocalDate rescheduledOnLocalDate = new LocalDate(rescheduledOnDate);
|
||||||
if (rescheduledOnLocalDate.isBefore(this.getDisbursementDate())) {
|
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();
|
final String errorMessage = "The date on which a loan is rescheduled cannot be before the loan disbursement date: "
|
||||||
throw new InvalidLoanStateTransitionException("writeoff", "cannot.be.before.submittal.date", errorMessage, rescheduledOnLocalDate, getDisbursementDate());
|
+ getDisbursementDate().toString();
|
||||||
|
throw new InvalidLoanStateTransitionException("writeoff",
|
||||||
|
"cannot.be.before.submittal.date", errorMessage,
|
||||||
|
rescheduledOnLocalDate, getDisbursementDate());
|
||||||
}
|
}
|
||||||
if (rescheduledOnLocalDate.isAfter(new LocalDate())) {
|
if (rescheduledOnLocalDate.isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan is rescheduled cannot be in the future.";
|
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);
|
throw new InvalidLoanStateTransitionException("writeoff",
|
||||||
|
"cannot.be.a.future.date", errorMessage,
|
||||||
|
rescheduledOnLocalDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,26 +744,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
public List<LoanRepaymentScheduleInstallment> getRepaymentScheduleInstallments() {
|
public List<LoanRepaymentScheduleInstallment> getRepaymentScheduleInstallments() {
|
||||||
return this.repaymentScheduleInstallments;
|
return this.repaymentScheduleInstallments;
|
||||||
}
|
}
|
||||||
//delete after jpw
|
|
||||||
/*
|
|
||||||
public DerivedLoanData deriveLoanDataxxx(CurrencyData currencyData) {
|
|
||||||
|
|
||||||
List<LoanTransaction> repaymentTransactions = new ArrayList<LoanTransaction>();
|
|
||||||
for (LoanTransaction loanTransaction : this.loanTransactions) {
|
|
||||||
if (loanTransaction.isRepayment() || loanTransaction.isWaiver()) {
|
|
||||||
repaymentTransactions.add(loanTransaction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Money arrearsTolerance = this.loanRepaymentScheduleDetail
|
|
||||||
.getInArrearsTolerance();
|
|
||||||
|
|
||||||
return new DerivedLoanDataProcessor().process(
|
|
||||||
new ArrayList<LoanRepaymentScheduleInstallment>(
|
|
||||||
this.repaymentScheduleInstallments),
|
|
||||||
repaymentTransactions, currencyData, arrearsTolerance);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public String getExternalId() {
|
public String getExternalId() {
|
||||||
return this.externalId;
|
return this.externalId;
|
||||||
@@ -735,7 +806,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
public LocalDate getExpectedDisbursedOnLocalDate() {
|
public LocalDate getExpectedDisbursedOnLocalDate() {
|
||||||
LocalDate expectedDisbursementDate = null;
|
LocalDate expectedDisbursementDate = null;
|
||||||
if (this.expectedDisbursedOnDate != null) {
|
if (this.expectedDisbursedOnDate != null) {
|
||||||
expectedDisbursementDate = new LocalDate(this.expectedDisbursedOnDate);
|
expectedDisbursementDate = new LocalDate(
|
||||||
|
this.expectedDisbursedOnDate);
|
||||||
}
|
}
|
||||||
return expectedDisbursementDate;
|
return expectedDisbursementDate;
|
||||||
}
|
}
|
||||||
@@ -743,7 +815,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
public LocalDate getExpectedFirstRepaymentOnDate() {
|
public LocalDate getExpectedFirstRepaymentOnDate() {
|
||||||
LocalDate firstRepaymentDate = null;
|
LocalDate firstRepaymentDate = null;
|
||||||
if (this.expectedFirstRepaymentOnDate != null) {
|
if (this.expectedFirstRepaymentOnDate != null) {
|
||||||
firstRepaymentDate = new LocalDate(this.expectedFirstRepaymentOnDate);
|
firstRepaymentDate = new LocalDate(
|
||||||
|
this.expectedFirstRepaymentOnDate);
|
||||||
}
|
}
|
||||||
return firstRepaymentDate;
|
return firstRepaymentDate;
|
||||||
}
|
}
|
||||||
@@ -784,7 +857,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
return possibleMaturityDate;
|
return possibleMaturityDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRepaymentScheduleInstallment(final LoanRepaymentScheduleInstallment installment) {
|
public void addRepaymentScheduleInstallment(
|
||||||
|
final LoanRepaymentScheduleInstallment installment) {
|
||||||
installment.updateLoan(this);
|
installment.updateLoan(this);
|
||||||
this.repaymentScheduleInstallments.add(installment);
|
this.repaymentScheduleInstallments.add(installment);
|
||||||
}
|
}
|
||||||
@@ -898,7 +972,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
.getPrincipal().getCurrency());
|
.getPrincipal().getCurrency());
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||||
cumulativeInterest = cumulativeInterest.plus(scheduledRepayment.getInterest(loanCurrency()));
|
cumulativeInterest = cumulativeInterest.plus(scheduledRepayment
|
||||||
|
.getInterest(loanCurrency()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return cumulativeInterest;
|
return cumulativeInterest;
|
||||||
@@ -917,7 +992,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Money getInterestRebateOwed() {
|
public Money getInterestRebateOwed() {
|
||||||
return Money.of(this.loanRepaymentScheduleDetail.getCurrency(), this.interestRebateOwed);
|
return Money.of(this.loanRepaymentScheduleDetail.getCurrency(),
|
||||||
|
this.interestRebateOwed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money getInArrearsTolerance() {
|
public Money getInArrearsTolerance() {
|
||||||
@@ -964,7 +1040,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getCurrencyCode() {
|
public String getCurrencyCode() {
|
||||||
return this.loanRepaymentScheduleDetail.getPrincipal().getCurrencyCode();
|
return this.loanRepaymentScheduleDetail.getPrincipal()
|
||||||
|
.getCurrencyCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+4
-3
@@ -2,9 +2,9 @@ package org.mifosng.platform.loan.service;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.mifosng.platform.api.data.LoanAccountData;
|
|
||||||
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
||||||
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
||||||
|
import org.mifosng.platform.api.data.LoanPermissionData;
|
||||||
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
||||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||||
import org.mifosng.platform.api.data.LoanTransactionDatajpw;
|
import org.mifosng.platform.api.data.LoanTransactionDatajpw;
|
||||||
@@ -23,6 +23,9 @@ public interface LoanReadPlatformService {
|
|||||||
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule,
|
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule,
|
||||||
Collection<LoanTransactionDatajpw> loanRepayments);
|
Collection<LoanTransactionDatajpw> loanRepayments);
|
||||||
|
|
||||||
|
LoanPermissionData retrieveLoanPermissions(
|
||||||
|
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed);
|
||||||
|
|
||||||
NewLoanData retrieveClientAndProductDetails(Long clientId, Long productId);
|
NewLoanData retrieveClientAndProductDetails(Long clientId, Long productId);
|
||||||
|
|
||||||
LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId);
|
LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId);
|
||||||
@@ -32,6 +35,4 @@ public interface LoanReadPlatformService {
|
|||||||
LoanTransactionData retrieveLoanTransactionDetails(Long loanId,
|
LoanTransactionData retrieveLoanTransactionDetails(Long loanId,
|
||||||
Long transactionId);
|
Long transactionId);
|
||||||
|
|
||||||
LoanAccountData convertToData(LoanBasicDetailsData loanBasic);// del jpw
|
|
||||||
// after
|
|
||||||
}
|
}
|
||||||
+36
-77
@@ -14,6 +14,7 @@ import org.mifosng.platform.api.data.EnumOptionData;
|
|||||||
import org.mifosng.platform.api.data.LoanAccountData;
|
import org.mifosng.platform.api.data.LoanAccountData;
|
||||||
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
import org.mifosng.platform.api.data.LoanAccountSummaryData;
|
||||||
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
||||||
|
import org.mifosng.platform.api.data.LoanPermissionData;
|
||||||
import org.mifosng.platform.api.data.LoanProductData;
|
import org.mifosng.platform.api.data.LoanProductData;
|
||||||
import org.mifosng.platform.api.data.LoanProductLookup;
|
import org.mifosng.platform.api.data.LoanProductLookup;
|
||||||
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
import org.mifosng.platform.api.data.LoanRepaymentPeriodDatajpw;
|
||||||
@@ -115,7 +116,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanAccountSummaryData retrieveSummary(MoneyData principal,
|
public LoanAccountSummaryData retrieveSummary(MoneyData principal,
|
||||||
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule, Collection<LoanTransactionDatajpw> loanRepayments) {
|
Collection<LoanRepaymentPeriodDatajpw> repaymentSchedule,
|
||||||
|
Collection<LoanTransactionDatajpw> loanRepayments) {
|
||||||
|
|
||||||
CurrencyData currencyData = new CurrencyData(
|
CurrencyData currencyData = new CurrencyData(
|
||||||
principal.getCurrencyCode(), principal.getDefaultName(),
|
principal.getCurrencyCode(), principal.getDefaultName(),
|
||||||
@@ -159,12 +161,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
totalInArrears = BigDecimal.TEN;
|
totalInArrears = BigDecimal.TEN;
|
||||||
|
|
||||||
|
|
||||||
Long waiverType = (long) 4;
|
Long waiverType = (long) 4;
|
||||||
for (LoanTransactionDatajpw loanRepayment : loanRepayments) {
|
for (LoanTransactionDatajpw loanRepayment : loanRepayments) {
|
||||||
Long transactionType = loanRepayment.getTransactionType().getId();
|
Long transactionType = loanRepayment.getTransactionType().getId();
|
||||||
if (transactionType.equals(waiverType)) {
|
if (transactionType.equals(waiverType)) {
|
||||||
totalWaived = totalWaived.add(loanRepayment.getTotal().getAmount());
|
totalWaived = totalWaived.add(loanRepayment.getTotal()
|
||||||
|
.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -200,84 +202,41 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanAccountData convertToData(LoanBasicDetailsData loanBasic) {
|
@Override
|
||||||
|
public LoanPermissionData retrieveLoanPermissions(
|
||||||
MoneyData princ = loanBasic.getPrincipal();
|
LoanBasicDetailsData loanBasicDetails, boolean isWaiverAllowed) {
|
||||||
|
|
||||||
CurrencyData currencyData = new CurrencyData(princ.getCurrencyCode(),
|
|
||||||
princ.getDefaultName(), princ.getDigitsAfterDecimal(),
|
|
||||||
princ.getDisplaySymbol(), princ.getNameCode());
|
|
||||||
|
|
||||||
// DerivedLoanData loanData = deriveLoanData(loanBasic.getId(),
|
|
||||||
// currencyData, loanBasic.getInArrearsTolerance().getAmount());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* return toLoanAccountData(loanBasic, loanData.getSummary(),
|
boolean waiveAllowed = isWaiverAllowed && (loanBasicDetails.getClosedOnDate() == null);
|
||||||
* loanData.getRepaymentSchedule(), loanData.getLoanRepayments());
|
|
||||||
*/
|
|
||||||
|
|
||||||
// return new LoanAccountData(loanBasic, null, repaymentSchedule, null,
|
|
||||||
// null);
|
|
||||||
return null;
|
|
||||||
|
boolean undoDisbursalAllowed = isDisbursed()
|
||||||
|
&& isOpenWithNoRepaymentMade();
|
||||||
|
boolean makeRepaymentAllowed = isDisbursed() && isNotClosed();
|
||||||
|
|
||||||
|
boolean rejectAllowed = isNotApproved() && isNotDisbursed()
|
||||||
|
&& isNotClosed();
|
||||||
|
boolean withdrawnByApplicantAllowed = isNotDisbursed() && isNotClosed();
|
||||||
|
boolean undoApprovalAllowed = isApproved() && isNotClosed();
|
||||||
|
boolean disbursalAllowed = isApproved() && isNotDisbursed()
|
||||||
|
&& isNotClosed();
|
||||||
|
|
||||||
|
return new LoanPermissionData(waiveAllowed,
|
||||||
|
makeRepaymentAllowed, rejectAllowed,
|
||||||
|
withdrawnByApplicantAllowed, undoApprovalAllowed,
|
||||||
|
undoDisbursalAllowed, disbursalAllowed,
|
||||||
|
isSubmittedAndPendingApproval(), isWaitingForDisbursal());
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
return new LoanPermissionData(true,
|
||||||
|
true, true,
|
||||||
|
true, true,
|
||||||
|
true, true,
|
||||||
|
true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* private DerivedLoanData deriveLoanData(Long loanId, CurrencyData
|
|
||||||
* currencyData, BigDecimal arrearsTolerance) {
|
|
||||||
*
|
|
||||||
* List<LoanTransaction> repaymentTransactions = new
|
|
||||||
* ArrayList<LoanTransaction>(); //for (LoanTransaction loanTransaction :
|
|
||||||
* this.loanTransactions) { // if (loanTransaction.isRepayment() ||
|
|
||||||
* loanTransaction.isWaiver()) { //
|
|
||||||
* repaymentTransactions.add(loanTransaction); // } //}
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* LoanRepaymentPeriodDatajpw selectedLoan = null;
|
|
||||||
*
|
|
||||||
* Collection<LoanRepaymentPeriodDatajpw> periods; try {
|
|
||||||
* context.authenticatedUser();
|
|
||||||
*
|
|
||||||
* LoanScheduleMapper rm = new LoanScheduleMapper(); String sql = "select "
|
|
||||||
* + rm.loanScheduleSchema() + " where l.id = ?"; periods =
|
|
||||||
* this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
|
|
||||||
*
|
|
||||||
* } catch (EmptyResultDataAccessException e) { throw new
|
|
||||||
* LoanNotFoundException(loanId); }
|
|
||||||
*
|
|
||||||
* return new DerivedLoanDataProcessor().process( new
|
|
||||||
* ArrayList<LoanRepaymentScheduleInstallment>( periods),
|
|
||||||
* repaymentTransactions, currencyData, arrearsTolerance); }
|
|
||||||
*/
|
|
||||||
|
|
||||||
private LoanAccountData toLoanAccountData(
|
|
||||||
LoanBasicDetailsData basicDetails, LoanAccountSummaryData summary,
|
|
||||||
LoanRepaymentScheduleData repaymentSchedule,
|
|
||||||
List<LoanTransactionData> loanRepayments) {
|
|
||||||
|
|
||||||
// permissions
|
|
||||||
/*
|
|
||||||
* boolean waiveAllowed =
|
|
||||||
* summary.isWaiveAllowed(basicDetails.getInArrearsTolerance()) &&
|
|
||||||
* isNotClosed(); boolean undoDisbursalAllowed = isDisbursed() &&
|
|
||||||
* isOpenWithNoRepaymentMade(); boolean makeRepaymentAllowed =
|
|
||||||
* isDisbursed() && isNotClosed();
|
|
||||||
*
|
|
||||||
* boolean rejectAllowed = isNotApproved() && isNotDisbursed() &&
|
|
||||||
* isNotClosed(); boolean withdrawnByApplicantAllowed = isNotDisbursed()
|
|
||||||
* && isNotClosed(); boolean undoApprovalAllowed = isApproved() &&
|
|
||||||
* isNotClosed(); boolean disbursalAllowed = isApproved() &&
|
|
||||||
* isNotDisbursed() && isNotClosed();
|
|
||||||
*
|
|
||||||
* LoanPermissionData permissions = new LoanPermissionData(waiveAllowed,
|
|
||||||
* makeRepaymentAllowed, rejectAllowed, withdrawnByApplicantAllowed,
|
|
||||||
* undoApprovalAllowed, undoDisbursalAllowed, disbursalAllowed,
|
|
||||||
* isSubmittedAndPendingApproval(), isWaitingForDisbursal());
|
|
||||||
*
|
|
||||||
* return new LoanAccountData(basicDetails, summary, repaymentSchedule,
|
|
||||||
* loanRepayments, permissions);
|
|
||||||
*/
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NewLoanData retrieveClientAndProductDetails(final Long clientId,
|
public NewLoanData retrieveClientAndProductDetails(final Long clientId,
|
||||||
|
|||||||
Reference in New Issue
Block a user