wip loan permissions

This commit is contained in:
John Woodlock
2012-07-20 18:11:40 +10:00
parent 7aeff1f0ac
commit b1a41f63b7
5 changed files with 298 additions and 262 deletions
@@ -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";
@@ -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
@@ -53,7 +45,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne @ManyToOne
@JoinColumn(name = "product_id") @JoinColumn(name = "product_id")
private final LoanProduct loanProduct; private final LoanProduct loanProduct;
@ManyToOne @ManyToOne
@JoinColumn(name = "fund_id", nullable = true) @JoinColumn(name = "fund_id", nullable = true)
private Fund fund; private Fund fund;
@@ -92,7 +84,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "expected_firstrepaymenton_date") @Column(name = "expected_firstrepaymenton_date")
private Date expectedFirstRepaymentOnDate; private Date expectedFirstRepaymentOnDate;
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "interest_calculated_from_date") @Column(name = "interest_calculated_from_date")
private Date interestChargedFromDate; private Date interestChargedFromDate;
@@ -140,18 +132,20 @@ 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() {
this.client = null; this.client = null;
this.loanProduct = null; this.loanProduct = null;
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,11 +176,15 @@ 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,
this.loanStatus = lifecycleStateMachine.transition(LoanEvent.LOAN_CREATED, this.loanStatus); LocalDate interestChargedFromDate,
LoanLifecycleStateMachine lifecycleStateMachine) {
this.loanStatus = lifecycleStateMachine.transition(
LoanEvent.LOAN_CREATED, this.loanStatus);
this.submittedOnDate = submittedOn.toDateTimeAtCurrentTime().toDate(); this.submittedOnDate = submittedOn.toDateTimeAtCurrentTime().toDate();
this.expectedMaturityDate = this.repaymentScheduleInstallments this.expectedMaturityDate = this.repaymentScheduleInstallments
@@ -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)
@@ -292,106 +320,141 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
this.loanRepaymentScheduleDetail.getPrincipal(), disbursedOn); this.loanRepaymentScheduleDetail.getPrincipal(), disbursedOn);
loanTransaction.updateLoan(this); loanTransaction.updateLoan(this);
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,
if (disbursedOn.isAfter(new LocalDate())) { disbursedOn, getApprovedOnDate());
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(); if (disbursedOn.isAfter(new LocalDate())) {
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();
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();
if (isInterestRebateAllowed()) { if (isInterestRebateAllowed()) {
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);
deriveLoanRepaymentScheduleCompletedData(); deriveLoanRepaymentScheduleCompletedData();
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();
if (isInterestRebateAllowed()) { if (isInterestRebateAllowed()) {
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();
} }
} }
@@ -399,40 +462,47 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
private void deriveLoanRepaymentScheduleCompletedData() { private void deriveLoanRepaymentScheduleCompletedData() {
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++;
} }
} }
} }
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());
} }
} }
return totalRepaidOrWaived; return totalRepaidOrWaived;
} }
public LocalDate possibleNextRepaymentDate() { public LocalDate possibleNextRepaymentDate() {
LocalDate earliestUnpaidInstallmentDate = new LocalDate(); LocalDate earliestUnpaidInstallmentDate = new LocalDate();
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
@@ -441,49 +511,55 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
break; break;
} }
} }
LocalDate lastTransactionDate = null; LocalDate lastTransactionDate = null;
for (LoanTransaction transaction : this.loanTransactions) { for (LoanTransaction transaction : this.loanTransactions) {
if (transaction.isRepayment() && transaction.isNonZero()) { if (transaction.isRepayment() && transaction.isNonZero()) {
lastTransactionDate = transaction.getTransactionDate(); lastTransactionDate = transaction.getTransactionDate();
} }
} }
LocalDate possibleNextRepaymentDate = earliestUnpaidInstallmentDate; LocalDate possibleNextRepaymentDate = earliestUnpaidInstallmentDate;
if (lastTransactionDate != null && lastTransactionDate.isAfter(earliestUnpaidInstallmentDate)) { if (lastTransactionDate != null
&& lastTransactionDate.isAfter(earliestUnpaidInstallmentDate)) {
possibleNextRepaymentDate = lastTransactionDate; possibleNextRepaymentDate = lastTransactionDate;
} }
return possibleNextRepaymentDate; return possibleNextRepaymentDate;
} }
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) {
if (installment.unpaid()) { if (installment.unpaid()) {
possibleNextRepaymentAmount = installment.getTotalDue(currency); possibleNextRepaymentAmount = installment.getTotalDue(currency);
break; break;
} }
} }
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();
if (newTransactionDetail.isRepayment()) { if (newTransactionDetail.isRepayment()) {
makeRepayment(newTransactionDetail, loanLifecycleStateMachine); makeRepayment(newTransactionDetail, loanLifecycleStateMachine);
} }
if (newTransactionDetail.isWaiver()) { if (newTransactionDetail.isWaiver()) {
waive(newTransactionDetail, loanLifecycleStateMachine); waive(newTransactionDetail, loanLifecycleStateMachine);
} }
@@ -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,42 +605,56 @@ 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);
} }
} }
public boolean isNotSubmittedAndPendingApproval() { public boolean isNotSubmittedAndPendingApproval() {
return !isSubmittedAndPendingApproval(); return !isSubmittedAndPendingApproval();
} }
public boolean isSubmittedAndPendingApproval() { public boolean isSubmittedAndPendingApproval() {
return this.loanStatus.isSubmittedAndPendingApproval(); return this.loanStatus.isSubmittedAndPendingApproval();
} }
@@ -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;
@@ -731,11 +802,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public Date getExpectedDisbursedOnDate() { public Date getExpectedDisbursedOnDate() {
return this.expectedDisbursedOnDate; return this.expectedDisbursedOnDate;
} }
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;
} }
@@ -755,7 +828,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
return disbursementDate; return disbursementDate;
} }
public LocalDate getExpectedMaturityDate() { public LocalDate getExpectedMaturityDate() {
LocalDate possibleMaturityDate = null; LocalDate possibleMaturityDate = null;
if (this.expectedMaturityDate != null) { if (this.expectedMaturityDate != null) {
@@ -763,7 +836,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
return possibleMaturityDate; return possibleMaturityDate;
} }
public LocalDate getActualMaturityDate() { public LocalDate getActualMaturityDate() {
LocalDate possibleMaturityDate = null; LocalDate possibleMaturityDate = null;
if (this.maturedOnDate != null) { if (this.maturedOnDate != null) {
@@ -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() {
@@ -946,25 +1022,26 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
public LocalDate getLoanStatusSinceDate() { public LocalDate getLoanStatusSinceDate() {
LocalDate statusSinceDate = getSubmittedOnDate(); LocalDate statusSinceDate = getSubmittedOnDate();
if (isApproved()) { if (isApproved()) {
statusSinceDate = new LocalDate(this.approvedOnDate); statusSinceDate = new LocalDate(this.approvedOnDate);
} }
if (isDisbursed()) { if (isDisbursed()) {
statusSinceDate = new LocalDate(this.disbursedOnDate); statusSinceDate = new LocalDate(this.disbursedOnDate);
} }
if (isClosed()) { if (isClosed()) {
statusSinceDate = new LocalDate(this.closedOnDate); statusSinceDate = new LocalDate(this.closedOnDate);
} }
return statusSinceDate; return statusSinceDate;
} }
public String getCurrencyCode() { public String getCurrencyCode() {
return this.loanRepaymentScheduleDetail.getPrincipal().getCurrencyCode(); return this.loanRepaymentScheduleDetail.getPrincipal()
.getCurrencyCode();
} }
} }
@@ -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
} }
@@ -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(),
@@ -158,13 +160,13 @@ 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());
*/
boolean undoDisbursalAllowed = isDisbursed()
&& isOpenWithNoRepaymentMade();
boolean makeRepaymentAllowed = isDisbursed() && isNotClosed();
// return new LoanAccountData(loanBasic, null, repaymentSchedule, null, boolean rejectAllowed = isNotApproved() && isNotDisbursed()
// null); && isNotClosed();
return null; boolean withdrawnByApplicantAllowed = isNotDisbursed() && isNotClosed();
} boolean undoApprovalAllowed = isApproved() && isNotClosed();
boolean disbursalAllowed = isApproved() && isNotDisbursed()
&& isNotClosed();
/* return new LoanPermissionData(waiveAllowed,
* private DerivedLoanData deriveLoanData(Long loanId, CurrencyData makeRepaymentAllowed, rejectAllowed,
* currencyData, BigDecimal arrearsTolerance) { withdrawnByApplicantAllowed, undoApprovalAllowed,
* undoDisbursalAllowed, disbursalAllowed,
* List<LoanTransaction> repaymentTransactions = new isSubmittedAndPendingApproval(), isWaitingForDisbursal());
* 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( return new LoanPermissionData(true,
LoanBasicDetailsData basicDetails, LoanAccountSummaryData summary, true, true,
LoanRepaymentScheduleData repaymentSchedule, true, true,
List<LoanTransactionData> loanRepayments) { true, true,
true, true);
// 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,