support submit new loan application and delete loan capabilities through api.

This commit is contained in:
Keith Woodlock
2012-05-16 18:21:36 +01:00
parent bf7783fc94
commit a006e60681
32 changed files with 1040 additions and 1118 deletions
@@ -2,9 +2,10 @@ package org.mifosng.data;
import javax.xml.bind.annotation.XmlRootElement;
/*
* TODO - Arguments is still not correctly supported
/**
* Being removed in favour of {@link ApiGlobalErrorResponse} and {@link ApiParameterError}
*/
@Deprecated
@XmlRootElement(name="errorResponse")
public class ErrorResponse {
@@ -9,13 +9,28 @@ import org.joda.time.LocalDate;
@XmlRootElement
public class CalculateLoanScheduleCommand {
private CommonLoanProperties commonLoanProperties;
private String currencyCode;
private Integer digitsAfterDecimal;
private BigDecimal principal;
private BigDecimal inArrearsToleranceAmount;
private Integer repaymentEvery;
private Integer repaymentFrequency;
private Integer numberOfRepayments;
private BigDecimal interestRatePerPeriod;
private Integer interestRateFrequencyMethod;
private Integer amortizationMethod;
private Integer interestMethod;
private Integer interestCalculationPeriodMethod;
private boolean flexibleRepaymentSchedule = false;
private boolean interestRebateAllowed = false;
private LocalDate expectedDisbursementDate;
private LocalDate repaymentsStartingFromDate;
private LocalDate interestCalculatedFromDate;
public CalculateLoanScheduleCommand() {
protected CalculateLoanScheduleCommand() {
//
}
@@ -23,132 +38,139 @@ public class CalculateLoanScheduleCommand {
final Integer digitsAfterDecimal, final Number principal,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed,
final LocalDate expectedDisbursementDate,
final LocalDate repaymentsStartingFromDate, final LocalDate interestCalculatedFromDate) {
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, Integer.valueOf(0),
flexibleRepaymentSchedule, interestRebateAllowed);
this.currencyCode = currencyCode;
this.digitsAfterDecimal = digitsAfterDecimal;
this.principal = BigDecimal.valueOf(principal.doubleValue());
this.interestRatePerPeriod = BigDecimal.valueOf(interestRatePerPeriod.doubleValue());
this.interestRateFrequencyMethod = interestRateFrequencyMethod;
this.interestMethod = interestMethod;
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
this.repaymentEvery = repaymentEvery;
this.repaymentFrequency = repaymentFrequency;
this.numberOfRepayments = numberOfRepayments;
this.amortizationMethod = amortizationMethod;
this.flexibleRepaymentSchedule = false;
this.interestRebateAllowed = false;
this.expectedDisbursementDate = expectedDisbursementDate;
this.repaymentsStartingFromDate = repaymentsStartingFromDate;
this.interestCalculatedFromDate = interestCalculatedFromDate;
}
public String getCurrencyCode() {
return this.commonLoanProperties.getCurrencyCode();
return currencyCode;
}
public void setCurrencyCode(final String currencyCode) {
this.commonLoanProperties.setCurrencyCode(currencyCode);
public void setCurrencyCode(String currencyCode) {
this.currencyCode = currencyCode;
}
public Integer getDigitsAfterDecimal() {
return this.commonLoanProperties.getDigitsAfterDecimal();
return digitsAfterDecimal;
}
public void setDigitsAfterDecimal(final Integer digitsAfterDecimal) {
this.commonLoanProperties.setDigitsAfterDecimal(digitsAfterDecimal);
}
public Integer getRepaymentEvery() {
return this.commonLoanProperties.getRepaymentEvery();
}
public void setRepaymentEvery(final Integer repaymentEvery) {
this.commonLoanProperties.setRepaymentEvery(repaymentEvery);
}
public Integer getRepaymentFrequency() {
return this.commonLoanProperties.getRepaymentFrequency();
}
public void setRepaymentFrequency(final Integer repaymentFrequency) {
this.commonLoanProperties.setRepaymentFrequency(repaymentFrequency);
}
public Boolean isFlexibleRepaymentSchedule() {
return this.commonLoanProperties.isFlexibleRepaymentSchedule();
}
public void setFlexibleRepaymentSchedule(
final Boolean flexibleRepaymentSchedule) {
this.commonLoanProperties.setFlexibleRepaymentSchedule(flexibleRepaymentSchedule);
}
public Boolean isInterestRebateAllowed() {
return this.commonLoanProperties.isInterestRebateAllowed();
}
public void setInterestRebateAllowed(final Boolean interestRebateAllowed) {
this.commonLoanProperties.setInterestRebateAllowed(interestRebateAllowed);
public void setDigitsAfterDecimal(Integer digitsAfterDecimal) {
this.digitsAfterDecimal = digitsAfterDecimal;
}
public BigDecimal getPrincipal() {
return this.commonLoanProperties.getPrincipal();
return principal;
}
public void setPrincipal(final BigDecimal principal) {
this.commonLoanProperties.setPrincipal(principal);
public void setPrincipal(BigDecimal principal) {
this.principal = principal;
}
// public Boolean getFlexibleRepaymentSchedule() {
// return this.commonLoanProperties.getFlexibleRepaymentSchedule();
// }
//
// public Boolean getInterestRebateAllowed() {
// return this.commonLoanProperties.getInterestRebateAllowed();
// }
public Integer getInterestRateFrequencyMethod() {
return commonLoanProperties.getInterestRateFrequencyMethod();
public BigDecimal getInArrearsToleranceAmount() {
return inArrearsToleranceAmount;
}
public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) {
this.commonLoanProperties.setInterestRateFrequencyMethod(interestRateFrequencyMethod);
public void setInArrearsToleranceAmount(BigDecimal inArrearsToleranceAmount) {
this.inArrearsToleranceAmount = inArrearsToleranceAmount;
}
public Integer getInterestMethod() {
return commonLoanProperties.getInterestMethod();
public Integer getRepaymentEvery() {
return repaymentEvery;
}
public void setInterestMethod(Integer interestMethod) {
this.commonLoanProperties.setInterestMethod(interestMethod);
public void setRepaymentEvery(Integer repaymentEvery) {
this.repaymentEvery = repaymentEvery;
}
public Integer getAmortizationMethod() {
return commonLoanProperties.getAmortizationMethod();
public Integer getRepaymentFrequency() {
return repaymentFrequency;
}
public void setAmortizationMethod(Integer amortizationMethod) {
this.commonLoanProperties.setAmortizationMethod(amortizationMethod);
public void setRepaymentFrequency(Integer repaymentFrequency) {
this.repaymentFrequency = repaymentFrequency;
}
public Integer getNumberOfRepayments() {
return commonLoanProperties.getNumberOfRepayments();
return numberOfRepayments;
}
public void setNumberOfRepayments(Integer numberOfRepayments) {
this.commonLoanProperties.setNumberOfRepayments(numberOfRepayments);
this.numberOfRepayments = numberOfRepayments;
}
public BigDecimal getInterestRatePerPeriod() {
return commonLoanProperties.getInterestRatePerPeriod();
return interestRatePerPeriod;
}
public void setInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
this.commonLoanProperties.setInterestRatePerPeriod(interestRatePerPeriod);
this.interestRatePerPeriod = interestRatePerPeriod;
}
public CommonLoanProperties getCommonLoanProperties() {
return commonLoanProperties;
public Integer getInterestRateFrequencyMethod() {
return interestRateFrequencyMethod;
}
public void setCommonLoanProperties(CommonLoanProperties commonLoanProperties) {
this.commonLoanProperties = commonLoanProperties;
public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) {
this.interestRateFrequencyMethod = interestRateFrequencyMethod;
}
public Integer getAmortizationMethod() {
return amortizationMethod;
}
public void setAmortizationMethod(Integer amortizationMethod) {
this.amortizationMethod = amortizationMethod;
}
public Integer getInterestMethod() {
return interestMethod;
}
public void setInterestMethod(Integer interestMethod) {
this.interestMethod = interestMethod;
}
public Integer getInterestCalculationPeriodMethod() {
return interestCalculationPeriodMethod;
}
public void setInterestCalculationPeriodMethod(
Integer interestCalculationPeriodMethod) {
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
}
public boolean isFlexibleRepaymentSchedule() {
return flexibleRepaymentSchedule;
}
public void setFlexibleRepaymentSchedule(boolean flexibleRepaymentSchedule) {
this.flexibleRepaymentSchedule = flexibleRepaymentSchedule;
}
public boolean isInterestRebateAllowed() {
return interestRebateAllowed;
}
public void setInterestRebateAllowed(boolean interestRebateAllowed) {
this.interestRebateAllowed = interestRebateAllowed;
}
public LocalDate getExpectedDisbursementDate() {
@@ -174,12 +196,4 @@ public class CalculateLoanScheduleCommand {
public void setInterestCalculatedFromDate(LocalDate interestCalculatedFromDate) {
this.interestCalculatedFromDate = interestCalculatedFromDate;
}
public Integer getInterestCalculationPeriodMethod() {
return this.commonLoanProperties.getInterestCalculationPeriodMethod();
}
public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) {
this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
}
}
@@ -1,44 +0,0 @@
package org.mifosng.data.command;
import java.math.BigDecimal;
public interface LoanProductCommandData {
String getName();
String getDescription();
String getCurrencyCode();
Integer getDigitsAfterDecimal();
Integer getRepaymentEvery();
Integer getRepaymentFrequency();
Boolean isFlexibleRepaymentSchedule();
Boolean isInterestRebateAllowed();
BigDecimal getPrincipal();
// Boolean getFlexibleRepaymentSchedule();
// Boolean getInterestRebateAllowed();
Integer getInterestRateFrequencyMethod();
Integer getInterestMethod();
Integer getAmortizationMethod();
Integer getNumberOfRepayments();
BigDecimal getInterestRatePerPeriod();
CommonLoanProperties getCommonLoanProperties();
BigDecimal getInArrearsToleranceAmount();
Integer getInterestCalculationPeriodMethod();
}
@@ -9,20 +9,45 @@ import org.mifosng.data.LoanSchedule;
@XmlRootElement
public class SubmitLoanApplicationCommand {
private Long applicantId;
private Long productId;
// product
private String currencyCode;
private Integer digitsAfterDecimal;
private String principalFormatted;
private BigDecimal principal;
private String inArrearsToleranceAmountFormatted;
private BigDecimal inArrearsToleranceAmount;
private Integer repaymentEvery;
private Integer repaymentFrequency;
private Integer numberOfRepayments;
private String interestRatePerPeriodFormatted;
private BigDecimal interestRatePerPeriod;
private Integer interestRateFrequencyMethod;
private Integer amortizationMethod;
private Integer interestMethod;
private Integer interestCalculationPeriodMethod;
private boolean flexibleRepaymentSchedule = false;
private boolean interestRebateAllowed = false;
//
private String expectedDisbursementDateFormatted;
private String repaymentsStartingFromDateFormatted;
private String interestCalculatedFromDateFormatted;
private LocalDate expectedDisbursementDate;
private LocalDate repaymentsStartingFromDate;
private LocalDate interestCalculatedFromDate;
private String dateFormat;
private String submittedOnDateFormatted;
private LocalDate submittedOnDate;
private String submittedOnNote;
private String externalId;
private CommonLoanProperties commonLoanProperties;
private LoanSchedule loanSchedule;
protected SubmitLoanApplicationCommand() {
@@ -38,22 +63,34 @@ public class SubmitLoanApplicationCommand {
final LoanSchedule loanSchedule, final String currencyCode,
final Integer digitsAfterDecimal, final Number principal,
final Number interestRatePerPeriod, Integer interestRateFrequencyMethod, final Integer interestMethod, final Integer interestCalculationPeriodMethod,
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount,
final boolean flexibleRepaymentSchedule,
final boolean interestRebateAllowed) {
final Integer repaymentEvery, final Integer repaymentFrequency, final Integer numberOfRepayments, Integer amortizationMethod, final Number toleranceAmount) {
this.applicantId = applicantId;
this.productId = productId;
this.submittedOnDate = submittedOnDate;
this.submittedOnNote = submittedOnNote;
commonLoanProperties = new CommonLoanProperties(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
this.expectedDisbursementDate = expectedDisbursementDate;
this.repaymentsStartingFromDate = repaymentsStartingFromDate;
this.interestCalculatedFromDate = interestCalculatedFromDate;
this.loanSchedule = loanSchedule;
this.inArrearsToleranceAmount = BigDecimal.valueOf(toleranceAmount.doubleValue());
this.currencyCode = currencyCode;
this.digitsAfterDecimal = digitsAfterDecimal;
this.principal = BigDecimal.valueOf(principal.doubleValue());
this.interestRatePerPeriod = BigDecimal.valueOf(interestRatePerPeriod.doubleValue());
this.interestRateFrequencyMethod = interestRateFrequencyMethod;
this.interestMethod = interestMethod;
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
this.repaymentEvery = repaymentEvery;
this.repaymentFrequency = repaymentFrequency;
this.numberOfRepayments = numberOfRepayments;
this.amortizationMethod = amortizationMethod;
this.flexibleRepaymentSchedule = false;
this.interestRebateAllowed = false;
}
public CalculateLoanScheduleCommand toCalculateLoanScheduleCommand() {
return new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
}
public Long getApplicantId() {
@@ -122,125 +159,117 @@ public class SubmitLoanApplicationCommand {
this.externalId = externalId;
}
public CommonLoanProperties getCommonLoanProperties() {
return commonLoanProperties;
}
public void setCommonLoanProperties(CommonLoanProperties commonLoanProperties) {
this.commonLoanProperties = commonLoanProperties;
}
public String getCurrencyCode() {
return this.commonLoanProperties.getCurrencyCode();
return currencyCode;
}
public void setCurrencyCode(final String currencyCode) {
this.commonLoanProperties.setCurrencyCode(currencyCode);
public void setCurrencyCode(String currencyCode) {
this.currencyCode = currencyCode;
}
public Integer getDigitsAfterDecimal() {
return this.commonLoanProperties.getDigitsAfterDecimal();
return digitsAfterDecimal;
}
public void setDigitsAfterDecimal(final Integer digitsAfterDecimal) {
this.commonLoanProperties.setDigitsAfterDecimal(digitsAfterDecimal);
}
public Integer getRepaymentEvery() {
return this.commonLoanProperties.getRepaymentEvery();
}
public void setRepaymentEvery(final Integer repaymentEvery) {
this.commonLoanProperties.setRepaymentEvery(repaymentEvery);
}
public Integer getRepaymentFrequency() {
return this.commonLoanProperties.getRepaymentFrequency();
}
public void setRepaymentFrequency(final Integer repaymentFrequency) {
this.commonLoanProperties.setRepaymentFrequency(repaymentFrequency);
}
public Boolean isFlexibleRepaymentSchedule() {
return this.commonLoanProperties.isFlexibleRepaymentSchedule();
}
public void setFlexibleRepaymentSchedule(
final Boolean flexibleRepaymentSchedule) {
this.commonLoanProperties.setFlexibleRepaymentSchedule(flexibleRepaymentSchedule);
}
public Boolean isInterestRebateAllowed() {
return this.commonLoanProperties.isInterestRebateAllowed();
}
public void setInterestRebateAllowed(final Boolean interestRebateAllowed) {
this.commonLoanProperties.setInterestRebateAllowed(interestRebateAllowed);
public void setDigitsAfterDecimal(Integer digitsAfterDecimal) {
this.digitsAfterDecimal = digitsAfterDecimal;
}
public BigDecimal getPrincipal() {
return this.commonLoanProperties.getPrincipal();
return principal;
}
public void setPrincipal(final BigDecimal principal) {
this.commonLoanProperties.setPrincipal(principal);
public void setPrincipal(BigDecimal principal) {
this.principal = principal;
}
// public Boolean getFlexibleRepaymentSchedule() {
// return this.commonLoanProperties.getFlexibleRepaymentSchedule();
// }
//
// public Boolean getInterestRebateAllowed() {
// return this.commonLoanProperties.getInterestRebateAllowed();
// }
public Integer getInterestRateFrequencyMethod() {
return commonLoanProperties.getInterestRateFrequencyMethod();
}
public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) {
this.commonLoanProperties.setInterestRateFrequencyMethod(interestRateFrequencyMethod);
}
public Integer getInterestMethod() {
return commonLoanProperties.getInterestMethod();
}
public void setInterestMethod(Integer interestMethod) {
this.commonLoanProperties.setInterestMethod(interestMethod);
}
public Integer getAmortizationMethod() {
return commonLoanProperties.getAmortizationMethod();
}
public void setAmortizationMethod(Integer amortizationMethod) {
this.commonLoanProperties.setAmortizationMethod(amortizationMethod);
}
public Integer getNumberOfRepayments() {
return commonLoanProperties.getNumberOfRepayments();
}
public void setNumberOfRepayments(Integer numberOfRepayments) {
this.commonLoanProperties.setNumberOfRepayments(numberOfRepayments);
}
public BigDecimal getInterestRatePerPeriod() {
return commonLoanProperties.getInterestRatePerPeriod();
}
public void setInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
this.commonLoanProperties.setInterestRatePerPeriod(interestRatePerPeriod);
}
public BigDecimal getInArrearsToleranceAmount() {
return this.commonLoanProperties.getInArrearsToleranceAmount();
return inArrearsToleranceAmount;
}
public void setInArrearsToleranceAmount(BigDecimal inArrearsToleranceAmount) {
this.commonLoanProperties.setInArrearsToleranceAmount(inArrearsToleranceAmount);
this.inArrearsToleranceAmount = inArrearsToleranceAmount;
}
public Integer getRepaymentEvery() {
return repaymentEvery;
}
public void setRepaymentEvery(Integer repaymentEvery) {
this.repaymentEvery = repaymentEvery;
}
public Integer getRepaymentFrequency() {
return repaymentFrequency;
}
public void setRepaymentFrequency(Integer repaymentFrequency) {
this.repaymentFrequency = repaymentFrequency;
}
public Integer getNumberOfRepayments() {
return numberOfRepayments;
}
public void setNumberOfRepayments(Integer numberOfRepayments) {
this.numberOfRepayments = numberOfRepayments;
}
public BigDecimal getInterestRatePerPeriod() {
return interestRatePerPeriod;
}
public void setInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
this.interestRatePerPeriod = interestRatePerPeriod;
}
public Integer getInterestRateFrequencyMethod() {
return interestRateFrequencyMethod;
}
public void setInterestRateFrequencyMethod(Integer interestRateFrequencyMethod) {
this.interestRateFrequencyMethod = interestRateFrequencyMethod;
}
public Integer getAmortizationMethod() {
return amortizationMethod;
}
public void setAmortizationMethod(Integer amortizationMethod) {
this.amortizationMethod = amortizationMethod;
}
public Integer getInterestMethod() {
return interestMethod;
}
public void setInterestMethod(Integer interestMethod) {
this.interestMethod = interestMethod;
}
public Integer getInterestCalculationPeriodMethod() {
return interestCalculationPeriodMethod;
}
public void setInterestCalculationPeriodMethod(
Integer interestCalculationPeriodMethod) {
this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
}
public boolean isFlexibleRepaymentSchedule() {
return flexibleRepaymentSchedule;
}
public void setFlexibleRepaymentSchedule(boolean flexibleRepaymentSchedule) {
this.flexibleRepaymentSchedule = flexibleRepaymentSchedule;
}
public boolean isInterestRebateAllowed() {
return interestRebateAllowed;
}
public void setInterestRebateAllowed(boolean interestRebateAllowed) {
this.interestRebateAllowed = interestRebateAllowed;
}
public LocalDate getInterestCalculatedFromDate() {
@@ -250,12 +279,73 @@ public class SubmitLoanApplicationCommand {
public void setInterestCalculatedFromDate(LocalDate interestCalculatedFromDate) {
this.interestCalculatedFromDate = interestCalculatedFromDate;
}
public Integer getInterestCalculationPeriodMethod() {
return this.commonLoanProperties.getInterestCalculationPeriodMethod();
public String getExpectedDisbursementDateFormatted() {
return expectedDisbursementDateFormatted;
}
public void setInterestCalculationPeriodMethod(final Integer interestCalculationPeriodMethod) {
this.commonLoanProperties.setInterestCalculationPeriodMethod(interestCalculationPeriodMethod);
public void setExpectedDisbursementDateFormatted(
String expectedDisbursementDateFormatted) {
this.expectedDisbursementDateFormatted = expectedDisbursementDateFormatted;
}
public String getRepaymentsStartingFromDateFormatted() {
return repaymentsStartingFromDateFormatted;
}
public void setRepaymentsStartingFromDateFormatted(
String repaymentsStartingFromDateFormatted) {
this.repaymentsStartingFromDateFormatted = repaymentsStartingFromDateFormatted;
}
public String getInterestCalculatedFromDateFormatted() {
return interestCalculatedFromDateFormatted;
}
public void setInterestCalculatedFromDateFormatted(
String interestCalculatedFromDateFormatted) {
this.interestCalculatedFromDateFormatted = interestCalculatedFromDateFormatted;
}
public String getDateFormat() {
return dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public String getSubmittedOnDateFormatted() {
return submittedOnDateFormatted;
}
public void setSubmittedOnDateFormatted(String submittedOnDateFormatted) {
this.submittedOnDateFormatted = submittedOnDateFormatted;
}
public String getPrincipalFormatted() {
return principalFormatted;
}
public void setPrincipalFormatted(String principalFormatted) {
this.principalFormatted = principalFormatted;
}
public String getInArrearsToleranceAmountFormatted() {
return inArrearsToleranceAmountFormatted;
}
public void setInArrearsToleranceAmountFormatted(
String inArrearsToleranceAmountFormatted) {
this.inArrearsToleranceAmountFormatted = inArrearsToleranceAmountFormatted;
}
public String getInterestRatePerPeriodFormatted() {
return interestRatePerPeriodFormatted;
}
public void setInterestRatePerPeriodFormatted(
String interestRatePerPeriodFormatted) {
this.interestRatePerPeriodFormatted = interestRatePerPeriodFormatted;
}
}
@@ -1,16 +1,10 @@
package org.mifosng.ui;
import java.util.Collection;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
@@ -21,12 +15,6 @@ public interface CommonRestOperations {
void updateProtectedResource(ProtectedResourceDetails loadProtectedResourceDetailsById);
Collection<LoanProductData> retrieveAllLoanProducts();
LoanSchedule calculateLoanSchedule(CalculateLoanScheduleCommand command);
Long submitLoanApplication(SubmitLoanApplicationCommand command);
EntityIdentifier deleteLoan(Long loanId);
EntityIdentifier approveLoan(LoanStateTransitionCommand command);
@@ -2,7 +2,6 @@ package org.mifosng.ui;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.mifosng.configuration.ApplicationConfigurationService;
@@ -10,15 +9,10 @@ import org.mifosng.data.ClientList;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanProductList;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.ui.loanproduct.ClientValidationException;
@@ -81,18 +75,6 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
return requestEntity;
}
@Override
public Collection<LoanProductData> retrieveAllLoanProducts() {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/product/loan/all"));
ResponseEntity<LoanProductList> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
LoanProductList.class);
return s.getBody().getProducts();
}
private ErrorResponseList parseErrors(HttpStatusCodeException e) {
XStream xstream = new XStream();
@@ -118,43 +100,6 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
return errorList;
}
@Override
public LoanSchedule calculateLoanSchedule(
final CalculateLoanScheduleCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/calculate"));
ResponseEntity<LoanSchedule> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.POST,
calculateLoanRepaymentScheduleRequest(command),
LoanSchedule.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public Long submitLoanApplication(final SubmitLoanApplicationCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/new"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri,
submitLoanApplicationRequest(command),
EntityIdentifier.class);
return s.getBody().getEntityId();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier deleteLoan(Long loanId) {
try {
@@ -437,24 +382,4 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
return new HttpEntity<LoanStateTransitionCommand>(command,
requestHeaders);
}
private HttpEntity<SubmitLoanApplicationCommand> submitLoanApplicationRequest(
final SubmitLoanApplicationCommand command) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
return new HttpEntity<SubmitLoanApplicationCommand>(command,
requestHeaders);
}
private HttpEntity<CalculateLoanScheduleCommand> calculateLoanRepaymentScheduleRequest(
final CalculateLoanScheduleCommand command) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
return new HttpEntity<CalculateLoanScheduleCommand>(command,
requestHeaders);
}
}
@@ -13,6 +13,10 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @deprecated - some initial work around cashflow for CreoCore that can be deleted later.
*/
@Deprecated
@Controller
public class CashflowController {
@@ -1,179 +0,0 @@
package org.mifosng.ui.loan;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.ui.CommonRestOperations;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class LoanController {
private final CommonRestOperations commonRestOperations;
@Autowired
public LoanController(final CommonRestOperations commonRestOperations) {
this.commonRestOperations = commonRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@ExceptionHandler(ClientValidationException.class)
public @ResponseBody Collection<ErrorResponse> validationException(ClientValidationException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
return ex.getValidationErrors();
}
private LocalDate parseStringToLocalDate(String eventDate, String dateFieldIdentifier) {
LocalDate eventLocalDate = null;
if (StringUtils.isNotBlank(eventDate)) {
try {
Locale locale = LocaleContextHolder.getLocale();
eventLocalDate = DateTimeFormat.forPattern("dd MMMM yyyy").withLocale(locale).parseLocalDate(eventDate.toLowerCase(locale));
} catch (IllegalArgumentException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.date.format", dateFieldIdentifier, eventDate));
throw new ClientValidationException(validationErrors);
}
}
return eventLocalDate;
}
private BigDecimal parseStringToBigDecimal(String source) {
try {
BigDecimal number = null;
if (StringUtils.isNotBlank(source)) {
String sourceWithoutSpaces = source.replaceAll(" ", "");
Locale locale = LocaleContextHolder.getLocale();
NumberFormat format = NumberFormat.getNumberInstance(locale);
Number parsedNumber = format.parse(sourceWithoutSpaces);
number = BigDecimal.valueOf(Double.valueOf(parsedNumber
.doubleValue()));
}
return number;
} catch (ParseException e) {
List<ErrorResponse> validationErrors = new ArrayList<ErrorResponse>();
validationErrors.add(new ErrorResponse("validation.msg.invalid.number.format", "amount", source));
throw new ClientValidationException(validationErrors);
}
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/loan/new", method = RequestMethod.POST)
public @ResponseBody EntityIdentifier submitNewLoanApplication(
@RequestParam(value="clientId", required=false) Long applicantId,
@RequestParam(value="productId", required=false) Long productId,
@RequestParam(value="submittedOnDate", required=false) String submittedOnDateStr,
@RequestParam(value="submittedOnNote", required=false) String submittedOnNote,
@RequestParam(value="currencyCode", required=false) String currencyCode,
@RequestParam(value="digitsAfterDecimal", required=false) Integer digitsAfterDecimal,
@RequestParam(value="principalMoney", required=false) String principalMoney,
@RequestParam(value="repaidEvery", required=false) Integer repaymentEvery,
@RequestParam(value="selectedRepaymentFrequencyOption", required=false) Integer repaymentFrequency,
@RequestParam(value="numberOfRepayments", required=false) Integer numberOfRepayments,
@RequestParam(value="selectedAmortizationMethodOption", required=false) Integer amortizationMethod,
@RequestParam(value="inArrearsTolerance", required=false) String inArrearsTolerance,
@RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate,
@RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod,
@RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod,
@RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestRateCalculatedInPeriod,
@RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr,
@RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr,
@RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr
) {
final LocalDate submittedOnDate = parseStringToLocalDate(submittedOnDateStr, "submittedOnDate");
final Number toleranceAmount = parseStringToBigDecimal(inArrearsTolerance);
final Number principal = parseStringToBigDecimal(principalMoney);
final Number interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRate);
final boolean flexibleRepaymentSchedule = false;
final boolean interestRebateAllowed = false;
final LocalDate expectedDisbursementDate = parseStringToLocalDate(expectedDisbursementDateStr, "expectedDisbursementDate");
final LocalDate repaymentsStartingFromDate = parseStringToLocalDate(repaymentsStartingFromDateStr, "repaymentsStartingFromDate");
final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate");
CalculateLoanScheduleCommand calculateLoanScheduleCommand = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod,
interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
LoanSchedule loanSchedule = this.commonRestOperations.calculateLoanSchedule(calculateLoanScheduleCommand);
SubmitLoanApplicationCommand command = new SubmitLoanApplicationCommand(applicantId, productId,
submittedOnDate, submittedOnNote, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate,
loanSchedule, currencyCode, digitsAfterDecimal, principal,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
toleranceAmount, flexibleRepaymentSchedule, interestRebateAllowed);
Long identifier = this.commonRestOperations.submitLoanApplication(command);
return new EntityIdentifier(identifier);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/loanschedule/calculate", method = RequestMethod.POST)
public @ResponseBody LoanSchedule calculateLoanSchedule(
@RequestParam(value="currencyCode", required=false) String currencyCode,
@RequestParam(value="digitsAfterDecimal", required=false) Integer digitsAfterDecimal,
@RequestParam(value="principalMoney", required=false) String principalMoney,
@RequestParam(value="repaidEvery", required=false) Integer repaymentEvery,
@RequestParam(value="selectedRepaymentFrequencyOption", required=false) Integer repaymentFrequency,
@RequestParam(value="numberOfRepayments", required=false) Integer numberOfRepayments,
@RequestParam(value="selectedAmortizationMethodOption", required=false) Integer amortizationMethod,
@RequestParam(value="nominalInterestRate", required=false) String nominalInterestRate,
@RequestParam(value="selectedInterestFrequencyOption", required=false) Integer interestRateFrequencyMethod,
@RequestParam(value="selectedInterestMethodOption", required=false) Integer interestMethod,
@RequestParam(value="interestRateCalculatedInPeriod", required=false) Integer interestRateCalculatedInPeriod,
@RequestParam(value="expectedDisbursementDate", required=false) String expectedDisbursementDateStr,
@RequestParam(value="repaymentsStartingFromDate", required=false) String repaymentsStartingFromDateStr,
@RequestParam(value="interestCalculatedFromDate", required=false) String interestCalculatedFromDateStr
) {
final Number principal = parseStringToBigDecimal(principalMoney);
final Number interestRatePerPeriod = parseStringToBigDecimal(nominalInterestRate);
final boolean flexibleRepaymentSchedule = false;
final boolean interestRebateAllowed = false;
final LocalDate expectedDisbursementDate = parseStringToLocalDate(expectedDisbursementDateStr, "expectedDisbursementDate");
final LocalDate repaymentsStartingFromDate = parseStringToLocalDate(repaymentsStartingFromDateStr, "repaymentsStartingFromDate");
final LocalDate interestCalculatedFromDate = parseStringToLocalDate(interestCalculatedFromDateStr, "interestCalculatedFromDate");
CalculateLoanScheduleCommand command = new CalculateLoanScheduleCommand(currencyCode, digitsAfterDecimal, principal, interestRatePerPeriod,
interestRateFrequencyMethod, interestMethod, interestRateCalculatedInPeriod, repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod,
flexibleRepaymentSchedule, interestRebateAllowed, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
return this.commonRestOperations.calculateLoanSchedule(command);
}
}
@@ -206,7 +206,7 @@ $(document).ready(function() {
return o;
};
function popupConfirmationDialogAndPost(url, titleCode, width, height, tabIndex, redirectUrl) {
function popupConfirmationDialogAndPost(url, submitType, titleCode, width, height, tabIndex, redirectUrl) {
var dialogDiv = $("<div id='dialog-form'><div id='formerrors'></div>" + jQuery.i18n.prop('text.confirmation.required') + "</div>");
var confirmButton = jQuery.i18n.prop('dialog.button.confirm');
@@ -216,7 +216,9 @@ $(document).ready(function() {
buttonsOpts[confirmButton] = function() {
var jqxhr = $.ajax({
url: url,
type: 'POST',
type: submitType,
contentType: 'application/json',
dataType: 'json',
success: function(data, textStatus, jqXHR) {
dialogDiv.dialog("close");
if (tabIndex > 0) {
@@ -322,70 +324,70 @@ $(document).ready(function() {
dataType: 'json',
cache: false,
success: function(data, textStatus, jqXHR) {
console.log(data);
var formHtml = $(templateSelector).render(data);
dialogDiv.append(formHtml);
var saveButton = jQuery.i18n.prop('dialog.button.save');
var cancelButton = jQuery.i18n.prop('dialog.button.cancel');
var buttonsOpts = {};
buttonsOpts[saveButton] = function() {
console.log(data);
var formHtml = $(templateSelector).render(data);
$('#notSelectedItems option').each(function(i) {
dialogDiv.append(formHtml);
var saveButton = jQuery.i18n.prop('dialog.button.save');
var cancelButton = jQuery.i18n.prop('dialog.button.cancel');
var buttonsOpts = {};
buttonsOpts[saveButton] = function() {
$('#notSelectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
$('#selectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
$('#selectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
var newFormData = JSON.stringify($('#entityform').serializeObject());
console.log(newFormData);
var jqxhr = $.ajax({
url: postUrl,
type: submitType,
contentType: 'application/json',
dataType: 'json',
data: newFormData,
success: saveSuccessFunction,
error: function(jqXHR, textStatus, errorThrown) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
}
});
};
buttonsOpts[cancelButton] = function() {$(this).dialog( "close" );};
dialogDiv.dialog({
title: jQuery.i18n.prop(titleCode),
width: width,
height: height,
modal: true,
buttons: buttonsOpts,
close: function() {
// if i dont do this, theres a problem with errors being appended to dialog view second time round
$(this).remove();
},
open: function (event, ui) {
$('#add').click(function() {
return !$('#notSelectedItems option:selected').remove().appendTo('#selectedItems');
});
$('#remove').click(function() {
return !$('#selectedItems option:selected').remove().appendTo('#notSelectedItems');
});
$('.datepickerfield').datepicker({constrainInput: true, maxDate: 0, dateFormat: 'dd MM yy'});
$("#entityform textarea").first().focus();
$('#entityform input').first().focus();
}
}).dialog('open');
}
});
var newFormData = JSON.stringify($('#entityform').serializeObject());
console.log(newFormData);
var jqxhr = $.ajax({
url: postUrl,
type: submitType,
contentType: 'application/json',
dataType: 'json',
data: newFormData,
success: saveSuccessFunction,
error: function(jqXHR, textStatus, errorThrown) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
}
});
};
buttonsOpts[cancelButton] = function() {$(this).dialog( "close" );};
dialogDiv.dialog({
title: jQuery.i18n.prop(titleCode),
width: width,
height: height,
modal: true,
buttons: buttonsOpts,
close: function() {
// if i dont do this, theres a problem with errors being appended to dialog view second time round
$(this).remove();
},
open: function (event, ui) {
$('#add').click(function() {
return !$('#notSelectedItems option:selected').remove().appendTo('#selectedItems');
});
$('#remove').click(function() {
return !$('#selectedItems option:selected').remove().appendTo('#notSelectedItems');
});
$('.datepickerfield').datepicker({constrainInput: true, maxDate: 0, dateFormat: 'dd MM yy'});
$("#entityform textarea").first().focus();
$('#entityform input').first().focus();
}
}).dialog('open');
}
});
jqxhr.error(function(jqXHR, textStatus, errorThrown) {
@@ -435,19 +437,6 @@ $(document).ready(function() {
// retrieve accounts summary info
refreshLoanSummaryInfo();
// retrieve additional info
var additionalFieldsParams = {
url: "http://localhost:8080/mifosng-provider/",
datasetType: "portfolio_client",
datasetPKValue: data.id,
datasetTypeDiv: "clientadditionaldata",
headingPrefix: "",
headingClass: "",
labelClass: "longrowlabel",
valueClass: "rowvalue"
};
jQuery.stretchyData.displayAllExtraData(additionalFieldsParams);
// bind click listeners to buttons.
$('.casflowbtn').button().click(function(e) {
var linkId = this.id;
@@ -489,24 +478,25 @@ $(document).ready(function() {
refreshNoteWidget();
} else {
var tableHtml = $("#loanDataTabTemplate").render(data);
var currentTab = $("#newtabs").children(".ui-tabs-panel").not(".ui-tabs-hide");
currentTab.html(tableHtml);
// retrieve additional info
var additionalFieldsParams = {
url: "http://localhost:8080/mifosng-provider/",
datasetType: "portfolio_loan",
datasetType: "portfolio_client",
datasetPKValue: data.id,
datasetTypeDiv: "loanadditionaldata" + data.id,
datasetTypeDiv: "clientadditionaldata",
headingPrefix: "",
headingClass: "",
labelClass: "longrowlabel",
valueClass: "rowvalue"
};
jQuery.stretchyData.displayAllExtraData(additionalFieldsParams);
} else {
var tableHtml = $("#loanDataTabTemplate").render(data);
var currentTab = $("#newtabs").children(".ui-tabs-panel").not(".ui-tabs-hide");
currentTab.html(tableHtml);
var curTabID = currentTab.prop("id")
@@ -560,13 +550,13 @@ $(document).ready(function() {
var linkId = this.id;
var loanId = linkId.replace("deletebtn", "");
var url = '${rootContext}portfolio/loan/' + loanId + '/delete';
var url = 'http://localhost:8080/mifosng-provider/api/v1/loans/' + loanId;
var width = 400;
var height = 225;
var redirectUrl = '${clientUrl}';
popupConfirmationDialogAndPost(url, 'dialog.title.confirmation.required', width, height, 0, redirectUrl);
popupConfirmationDialogAndPost(url, 'DELETE', 'dialog.title.confirmation.required', width, height, 0, redirectUrl);
e.preventDefault();
});
$('button.deleteloan span').text(jQuery.i18n.prop('dialog.button.delete.loan'));
@@ -660,6 +650,19 @@ $(document).ready(function() {
var curTabID = curTab.prop("id")
}
});
// additional data
var additionalFieldsParams = {
url: "http://localhost:8080/mifosng-provider/",
datasetType: "portfolio_loan",
datasetPKValue: data.id,
datasetTypeDiv: "loanadditionaldata" + data.id,
headingPrefix: "",
headingClass: "",
labelClass: "longrowlabel",
valueClass: "rowvalue"
};
jQuery.stretchyData.displayAllExtraData(additionalFieldsParams);
}
}
}
@@ -173,13 +173,13 @@ $.views.registerHelpers({
};
function calculateAnnualPercentageRate() {
var periodInterestRate = parseFloat($('#nominalInterestRate').val());
var periodInterestRate = parseFloat($('#interestRatePerPeriodFormatted').val());
if (isNaN(periodInterestRate)) {
periodInterestRate = 0;
}
var periodsInYear = 12;
var periodType = $('#selectedInterestFrequencyOption').val();
var periodType = $('#interestRateFrequencyMethod').val();
if (periodType == 3) {
periodsInYear = 1;
} else if (periodType == 2) {
@@ -220,15 +220,15 @@ $.views.registerHelpers({
calculateLoanSchedule();
// change detection
$('#principalMoney').change(function() {
$('#principalFormatted').change(function() {
calculateLoanSchedule();
});
$('#repaidEvery').change(function() {
$('#repaymentEvery').change(function() {
calculateLoanSchedule();
});
$('#selectedRepaymentFrequencyOption').change(function() {
$('#repaymentFrequency').change(function() {
calculateLoanSchedule();
});
@@ -236,37 +236,37 @@ $.views.registerHelpers({
calculateLoanSchedule();
});
$('#expectedDisbursementDate').change(function() {
$('#expectedDisbursementDateFormatted').change(function() {
calculateLoanSchedule();
});
$('#repaymentsStartingFromDate').change(function() {
$('#repaymentsStartingFromDateFormatted').change(function() {
calculateLoanSchedule();
});
$('#nominalInterestRate').change(function() {
$('#interestRatePerPeriodFormatted').change(function() {
calculateAnnualPercentageRate();
calculateLoanSchedule();
});
$('#selectedInterestFrequencyOption').change(function() {
$('#interestRateFrequencyMethod').change(function() {
calculateAnnualPercentageRate();
calculateLoanSchedule();
});
$('#selectedAmortizationMethodOption').change(function() {
$('#amortizationMethod').change(function() {
calculateLoanSchedule();
});
$('#selectedInterestMethodOption').change(function() {
$('#interestMethod').change(function() {
calculateLoanSchedule();
});
$('#interestRateCalculatedInPeriod').change(function() {
$('#interestCalculationPeriodMethod').change(function() {
calculateLoanSchedule();
});
$('#interestCalculatedFromDate').change(function() {
$('#interestCalculatedFromDateFormatted').change(function() {
calculateLoanSchedule();
});
@@ -291,14 +291,17 @@ $.views.registerHelpers({
}
function calculateLoanSchedule() {
var calculateLoanScheduleurl = '${rootContext}portfolio/loanschedule/calculate';
var calculateLoanScheduleurl = 'http://localhost:8080/mifosng-provider/api/v1/loans?command=calculateLoanSchedule';
var form_data = $('#entityform').serialize();
var newFormData = JSON.stringify($('#entityform').serializeObject());
console.log(newFormData);
var jqxhr2 = $.ajax({
url: calculateLoanScheduleurl,
type: 'POST',
data: form_data,
contentType: 'application/json',
dataType: 'json',
data: newFormData,
success: function(data, textStatus, jqXHR) {
removeErrors("#formerrors");
var loanScheduleHtml = $("#newLoanScheduleTemplate").render(data);
@@ -313,17 +316,20 @@ $.views.registerHelpers({
}
function submitLoanApplication() {
var submitLoanApplicationUrl = '${rootContext}portfolio/client/${clientId}/loan/new';
var submitLoanApplicationUrl = 'http://localhost:8080/mifosng-provider/api/v1/loans';
var form_data = $('#entityform').serialize();
var newFormData = JSON.stringify($('#entityform').serializeObject());
console.log(newFormData);
var jqxhr2 = $.ajax({
url: submitLoanApplicationUrl,
type: 'POST',
data: form_data,
contentType: 'application/json',
dataType: 'json',
data: newFormData,
success: function(data, textStatus, jqXHR) {
var url = '${rootContext}portfolio/client/${clientId}';
window.location.href = url;
var localAppUrlToSwitchToClientPage = '${rootContext}portfolio/client/${clientId}';
window.location.href = localAppUrlToSwitchToClientPage;
}
});
@@ -331,9 +337,8 @@ $.views.registerHelpers({
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
});
}
// on page load
var jqxhr = $.ajax({
url: 'http://localhost:8080/mifosng-provider/api/v1/loans/template?clientId=${clientId}',
type: 'GET',
@@ -54,7 +54,7 @@
<form id="entityform">
<div id="formerrors"></div>
<input type="hidden" id="clientId" name="clientId" value="{{=clientId}}" />
<input type="hidden" id="applicantId" name="applicantId" value="{{=clientId}}" />
<input type="hidden" id="currencyCode" name="currencyCode" value="{{=selectedProduct.principalMoney.currencyCode}}" />
<input type="hidden" id="digitsAfterDecimal" name="digitsAfterDecimal" value="{{=$ctx.number(selectedProduct.principalMoney.currencyDigitsAfterDecimal)}}" />
@@ -103,16 +103,16 @@
<tr>
<td><spring:message code="form.label.loan.product.loan.amount"/></td>
<td>
<input id="principalMoney" name="principalMoney" title="" type="text" value="{{=$ctx.money(selectedProduct.principalMoney)}}" style="width: 125px;" />
<input id="principalMoney.currencyCode" name="principalMoney.currencyCode" title="" type="text" value="{{=selectedProduct.principalMoney.displaySymbol}}" style="width: 40px;" disabled="disabled" />
<input id="principalFormatted" name="principalFormatted" title="" type="text" value="{{=$ctx.money(selectedProduct.principalMoney)}}" style="width: 125px;" />
<input id="principalCurrencyCode" name="principalCurrencyCode" title="" type="text" value="{{=selectedProduct.principalMoney.displaySymbol}}" style="width: 40px;" disabled="disabled" />
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.repaidevery"/></td>
<td>
<input id="repaidEvery" name="repaidEvery" title="" type="text" value="{{=$ctx.number(selectedProduct.repaidEvery)}}" style="width: 50px;" />
<input id="repaymentEvery" name="repaymentEvery" title="" type="text" value="{{=$ctx.number(selectedProduct.repaidEvery)}}" style="width: 50px;" />
<select name="selectedRepaymentFrequencyOption" id="selectedRepaymentFrequencyOption" title="" style="width: 121px;">
<select name="repaymentFrequency" id="repaymentFrequency" title="" style="width: 121px;">
{{#each selectedProduct.repaymentFrequencyOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.repaymentPeriodFrequency)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
@@ -149,9 +149,9 @@
<tr>
<td><spring:message code="form.label.loan.product.nominal.interestrate"/></td>
<td>
<input id="nominalInterestRate" name="nominalInterestRate" title="" type="text" value="{{=$ctx.decimal(selectedProduct.interestRatePerPeriod, 4)}}" style="width: 60px;" />
<input id="interestRatePerPeriodFormatted" name="interestRatePerPeriodFormatted" title="" type="text" value="{{=$ctx.decimal(selectedProduct.interestRatePerPeriod, 4)}}" style="width: 60px;" />
<select name="selectedInterestFrequencyOption" id="selectedInterestFrequencyOption" title="" style="width: 115px;">
<select name="interestRateFrequencyMethod" id="interestRateFrequencyMethod" title="" style="width: 115px;">
{{#each selectedProduct.interestFrequencyOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.interestRatePeriod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
@@ -171,7 +171,7 @@
<tr>
<td><spring:message code="form.label.loan.product.amortization"/></td>
<td>
<select name="selectedAmortizationMethodOption" id="selectedAmortizationMethodOption" title="" style="width: 178px;">
<select name="amortizationMethod" id="amortizationMethod" title="" style="width: 178px;">
{{#each selectedProduct.possibleAmortizationOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.amortizationMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
@@ -185,7 +185,7 @@
<tr>
<td><spring:message code="form.label.loan.product.interest.method"/></td>
<td>
<select name="selectedInterestMethodOption" id="selectedInterestMethodOption" title="" style="width: 180px;">
<select name="interestMethod" id="interestMethod" title="" style="width: 180px;">
{{#each selectedProduct.possibleInterestOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.interestMethod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
@@ -199,7 +199,7 @@
<tr>
<td><spring:message code="form.label.loan.product.interest.rate.calcutated.in.period"/></td>
<td>
<select id="interestRateCalculatedInPeriod" name="interestRateCalculatedInPeriod" title="" style="width: 220px;">
<select id="interestCalculationPeriodMethod" name="interestCalculationPeriodMethod" title="" style="width: 220px;">
{{#each selectedProduct.possibleInterestRateCalculatedInPeriodOptions}}
{{#if $ctx.number($parent.parent.data.selectedProduct.interestRateCalculatedInPeriod)===$ctx.number(id)}}
<option value="{{=$ctx.number(id)}}" selected="selected">{{=value}}</option>
@@ -213,14 +213,14 @@
<tr>
<td><spring:message code="form.label.loan.interest.charged.from"/></td>
<td>
<input id="interestCalculatedFromDate" name="interestCalculatedFromDate" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(interestCalculatedFromDate)}}" style="width:172px;"/>
<input id="interestCalculatedFromDateFormatted" name="interestCalculatedFromDateFormatted" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(interestCalculatedFromDate)}}" style="width:172px;"/>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.product.arrears.tolerance"/></td>
<td>
<input id="inArrearsTolerance" name="inArrearsTolerance" title="" type="text" value="{{=$ctx.money(selectedProduct.inArrearsTolerance)}}" style="width: 125px;" />
<input id="inArrearsTolerance.currencyCode" name="inArrearsTolerance.currencyCode" title="" type="text" value="{{=selectedProduct.inArrearsTolerance.displaySymbol}}" style="width: 40px;" disabled="disabled" />
<input id="inArrearsToleranceAmountFormatted" name="inArrearsToleranceAmountFormatted" title="" type="text" value="{{=$ctx.money(selectedProduct.inArrearsTolerance)}}" style="width: 125px;" />
<input id="inArrearsCurrencyCode" name="inArrearsCurrencyCode" title="" type="text" value="{{=selectedProduct.inArrearsTolerance.displaySymbol}}" style="width: 40px;" disabled="disabled" />
</td>
</tr>
</table>
@@ -1,209 +0,0 @@
package org.mifosng.platform;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.platform.exceptions.NewDataValidationException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public class CalculateLoanScheduleCommandValidator {
private final CalculateLoanScheduleCommand command;
public CalculateLoanScheduleCommandValidator(CalculateLoanScheduleCommand command) {
this.command = command;
}
public void validate() {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
if (StringUtils.isBlank(command.getCurrencyCode())) {
ErrorResponse error = new ErrorResponse("validation.msg.product.currency.cannot.be.empty", "currencyCode");
dataValidationErrors.add(error);
}
if (command.getDigitsAfterDecimal() != null) {
if (command.getDigitsAfterDecimal() < 0 || command.getDigitsAfterDecimal() > 6) {
ErrorResponse error = new ErrorResponse("validation.msg.product.currency.digitsAfterDecimal.must.be.between.zero.and.six.inclusive", "selectedDigitsAfterDecimal", command.getDigitsAfterDecimal());
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.currency.digitsAfterDecimal.cannot.be.empty", "selectedDigitsAfterDecimal");
dataValidationErrors.add(error);
}
if (command.getPrincipal() != null) {
if (command.getPrincipal().doubleValue() <= Double.valueOf("0.0")) {
ErrorResponse error = new ErrorResponse("validation.msg.product.principal.amount.must.be.greater.than.zero", "principalMoney");
dataValidationErrors.add(error);
}
// TODO - check number of digits before decimal and after decimal
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.principal.amount.cannot.be.empty", "principalMoney");
dataValidationErrors.add(error);
}
if (command.getRepaymentFrequency() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
ErrorResponse error = null;
switch (frequencyType) {
case DAYS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 365);
break;
case WEEKS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 52);
break;
case MONTHS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 12);
break;
case YEARS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 2);
break;
default:
error = new ErrorResponse("validation.msg.product.repayment.frequency.invalid", "selectedRepaymentFrequencyOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.repayment.frequency.cannot.be.empty", "selectedRepaymentFrequencyOption");
dataValidationErrors.add(error);
}
if (command.getNumberOfRepayments() != null) {
if (command.getNumberOfRepayments() < 1 || command.getNumberOfRepayments() > 100) {
ErrorResponse error = new ErrorResponse("validation.msg.product.number.of.repayments.is.out.of.range", "numberOfRepayments");
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.number.of.repayments.cannot.be.empty", "numberOfRepayments");
dataValidationErrors.add(error);
}
if (command.getAmortizationMethod() != null) {
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
ErrorResponse error = null;
switch (amortizationMethod) {
case EQUAL_PRINCIPAL:
break;
case EQUAL_INSTALLMENTS:
break;
default:
error = new ErrorResponse("validation.msg.product.amortization.method.invalid", "selectedAmortizationMethodOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.amortization.method.cannot.be.empty", "selectedAmortizationMethodOption");
dataValidationErrors.add(error);
}
if (command.getExpectedDisbursementDate() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.expectedDisbursementDate.method.cannot.be.blank", "expectedDisbursementDate");
dataValidationErrors.add(error);
} else {
if (command.getRepaymentsStartingFromDate() != null
&& command.getExpectedDisbursementDate().isAfter(command.getRepaymentsStartingFromDate())) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.expectedDisbursementDate.cannot.be.after.first.repayment.date", "expectedDisbursementDate");
dataValidationErrors.add(error);
}
}
if (command.getRepaymentsStartingFromDate() != null && command.getInterestCalculatedFromDate() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.interestCalculatedFromDate.must.be.entered.when.using.repayments.startfrom.field", "interestCalculatedFromDate");
dataValidationErrors.add(error);
} else if (command.getRepaymentsStartingFromDate() == null && command.getInterestCalculatedFromDate() != null) {
// validate interestCalculatedFromDate is after or on repaymentsStartingFromDate
if (command.getExpectedDisbursementDate().isAfter(command.getInterestCalculatedFromDate())) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.interestCalculatedFromDate.cannot.be.before.disbursement.date", "interestCalculatedFromDate");
dataValidationErrors.add(error);
}
}
if (command.getInterestRatePerPeriod()!= null) {
if (command.getInterestRatePerPeriod().doubleValue() < Double.valueOf("0.0")) {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.rate.per.period.amount.cannot.be.negative", "nominalInterestRate");
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.rate.per.period.amount.cannot.be.empty", "nominalInterestRate");
dataValidationErrors.add(error);
}
// interest rate period type
if (command.getInterestRateFrequencyMethod() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
ErrorResponse error = null;
switch (frequencyType) {
case DAYS:
break;
case WEEKS:
break;
case MONTHS:
break;
case YEARS:
break;
default:
error = new ErrorResponse("validation.msg.product.interest.frequency.invalid", "selectedInterestFrequencyOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.frequency.cannot.be.empty", "selectedInterestFrequencyOption");
dataValidationErrors.add(error);
}
if (command.getInterestMethod() != null) {
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
ErrorResponse error = null;
switch (interestMethod) {
case DECLINING_BALANCE:
break;
case FLAT:
break;
default:
error = new ErrorResponse("validation.msg.product.interest.method.invalid", "selectedInterestMethodOption");
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ErrorResponse error = new ErrorResponse("validation.msg.product.interest.method.cannot.be.empty", "selectedInterestMethodOption");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new NewDataValidationException(dataValidationErrors, "Validation errors exist.");
}
}
private ErrorResponse validateNumberExistsAndInRange(String errorCodePreFix, String fieldName, Integer value, int min, int max) {
ErrorResponse error = null;
if (value == null) {
error = new ErrorResponse(errorCodePreFix + ".cannot.be.empty", fieldName);
} else {
if (value < min || value > max) {
error = new ErrorResponse(errorCodePreFix + ".outside.allowed.range", fieldName);
}
}
return error;
}
}
@@ -51,6 +51,7 @@ import org.mifosng.platform.loan.domain.LoanStatus;
import org.mifosng.platform.loan.domain.LoanStatusRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loan.service.CalculationPlatformService;
import org.mifosng.platform.organisation.domain.Office;
import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.organisation.domain.Organisation;
@@ -452,7 +453,6 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
String currencyCode = matchingProduct.getCurrency().getCode();
int digitsAfterDecimal = matchingProduct.getCurrency().getDigitsAfterDecimal();
boolean flexibleRepaymentSchedule = matchingProduct.isFlexibleRepaymentSchedule();
Integer repaymentEvery = matchingProduct.getRepayEvery();
Integer repaymentFrequency = matchingProduct.getRepaymentPeriodFrequencyType().getValue();
Integer amortizationMethod = matchingProduct.getAmortizationMethod().getValue();
@@ -469,8 +469,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
interestRatePeriodFrequency,
interestMethod, interestCalculationPeriodMethod,
repaymentEvery,
repaymentFrequency, numberOfRepayments, amortizationMethod, flexibleRepaymentSchedule,
matchingProduct.isInterestRebateAllowed(), expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
repaymentFrequency, numberOfRepayments, amortizationMethod, expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateLoanScheduleCommand);
@@ -478,8 +477,7 @@ public class ImportPlatformServiceImpl implements ImportPlatformService {
expectedDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate, loanSchedule,
currencyCode, digitsAfterDecimal, principal,
interestRatePerPeriod, interestRatePeriodFrequency, interestMethod, interestCalculationPeriodMethod,
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount.getAmount(),
flexibleRepaymentSchedule, matchingProduct.isInterestRebateAllowed());
repaymentEvery, repaymentFrequency, numberOfRepayments, amortizationMethod, toleranceAmount.getAmount());
parsedNewLoanDetails.setExternalId(loanExternalId);
@@ -5,18 +5,14 @@ import static org.mifosng.platform.Specifications.loansThatMatch;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import javax.sql.DataSource;
import org.joda.time.LocalDate;
import org.mifosng.data.ClientData;
import org.mifosng.data.CurrencyData;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.MoneyData;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.OrganisationReadModel;
import org.mifosng.platform.client.service.ClientReadPlatformService;
import org.mifosng.platform.currency.domain.ApplicationCurrency;
@@ -4,7 +4,6 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
@@ -14,12 +13,6 @@ public interface WritePlatformService {
void updateUsernamePasswordOnFirstTimeLogin(UpdateUsernamePasswordCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_LOAN_THAT_IS_SUBMITTED_AND_NOT_APPROVED')")
EntityIdentifier deleteLoan(Long loanId);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE', 'CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE')")
EntityIdentifier submitLoanApplication(SubmitLoanApplicationCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_APPROVE_LOAN_ROLE', 'CAN_APPROVE_LOAN_IN_THE_PAST_ROLE')")
EntityIdentifier approveLoanApplication(LoanStateTransitionCommand command);
@@ -3,7 +3,6 @@ package org.mifosng.platform;
import static org.mifosng.platform.Specifications.loansThatMatch;
import static org.mifosng.platform.Specifications.usersThatMatch;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -13,39 +12,27 @@ import org.joda.time.LocalDate;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.MoneyData;
import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UpdateUsernamePasswordCommand;
import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.client.domain.Note;
import org.mifosng.platform.client.domain.NoteRepository;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.ApplicationDomainRuleException;
import org.mifosng.platform.exceptions.NoAuthorizationException;
import org.mifosng.platform.exceptions.UnAuthenticatedUserException;
import org.mifosng.platform.infrastructure.BasicPasswordEncodablePlatformUser;
import org.mifosng.platform.infrastructure.PlatformPasswordEncoder;
import org.mifosng.platform.infrastructure.PlatformUser;
import org.mifosng.platform.infrastructure.UsernameAlreadyExistsException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanBuilder;
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.LoanProduct;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
import org.mifosng.platform.loan.domain.LoanProductRepository;
import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanStatus;
@@ -53,6 +40,7 @@ import org.mifosng.platform.loan.domain.LoanStatusRepository;
import org.mifosng.platform.loan.domain.LoanTransaction;
import org.mifosng.platform.loan.domain.LoanTransactionRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loan.service.CalculationPlatformService;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.domain.AppUserRepository;
import org.mifosng.platform.user.domain.PlatformUserRepository;
@@ -69,8 +57,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
private final PlatformUserRepository platformUserRepository;
private final PlatformPasswordEncoder platformPasswordEncoder;
private final ClientRepository clientRepository;
private final LoanProductRepository loanProductRepository;
private final LoanRepository loanRepository;
private final LoanStatusRepository loanStatusRepository;
private final CalculationPlatformService calculationPlatformService;
@@ -81,9 +67,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
public WritePlatformServiceJpaRepositoryImpl(
final PlatformUserRepository platformUserRepository,
final PlatformPasswordEncoder platformPasswordEncoder,
final ClientRepository clientRepository,
final NoteRepository noteRepository,
final LoanProductRepository loanProductRepository,
final LoanRepository loanRepository,
final LoanTransactionRepository loanTransactionRepository,
final LoanStatusRepository loanStatusRepository,
@@ -91,9 +75,7 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
) {
this.platformUserRepository = platformUserRepository;
this.platformPasswordEncoder = platformPasswordEncoder;
this.clientRepository = clientRepository;
this.noteRepository = noteRepository;
this.loanProductRepository = loanProductRepository;
this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository;
this.loanStatusRepository = loanStatusRepository;
@@ -145,105 +127,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
return currentUser;
}
@Transactional
@Override
public EntityIdentifier submitLoanApplication(final SubmitLoanApplicationCommand command) {
AppUser currentUser = extractAuthenticatedUser();
SubmitLoanApplicationCommandValidator validator = new SubmitLoanApplicationCommandValidator(command);
validator.validate();
LocalDate submittedOn = command.getSubmittedOnDate();
if (this.isBeforeToday(submittedOn) && currentUser.hasNotPermissionForAnyOf("CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE", "PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE")) {
throw new NoAuthorizationException("Cannot add backdated loan.");
}
LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
Client client = this.clientRepository.findOne(command.getApplicantId());
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command
.getInterestRatePerPeriod().doubleValue());
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
// apr calculator
BigDecimal defaultAnnualNominalInterestRate = BigDecimal.ZERO;
switch (interestPeriodFrequencyType) {
case DAYS:
break;
case WEEKS:
defaultAnnualNominalInterestRate = command
.getInterestRatePerPeriod()
.multiply(BigDecimal.valueOf(52));
break;
case MONTHS:
defaultAnnualNominalInterestRate = command
.getInterestRatePerPeriod()
.multiply(BigDecimal.valueOf(12));
break;
case YEARS:
defaultAnnualNominalInterestRate = command
.getInterestRatePerPeriod().multiply(BigDecimal.valueOf(1));
break;
case INVALID:
break;
}
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
final Integer repayEvery = command.getRepaymentEvery();
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType
.fromInt(command.getRepaymentFrequency());
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
final boolean flexibleRepaymentSchedule = command.isFlexibleRepaymentSchedule();
final boolean interestRebateAllowed = command.isInterestRebateAllowed();
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency,
command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount(),
flexibleRepaymentSchedule, interestRebateAllowed);
LoanSchedule loanSchedule = command.getLoanSchedule();
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
Loan loan = new LoanBuilder()
.with(currentUser.getOrganisation())
.with(loanProduct).with(client)
.with(loanRepaymentScheduleDetail)
.build();
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
MoneyData readPrincipalDue = scheduledLoanInstallment
.getPrincipalDue();
MoneyData readInterestDue = scheduledLoanInstallment
.getInterestDue();
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
loan, scheduledLoanInstallment.getInstallmentNumber(),
scheduledLoanInstallment.getPeriodStart(),
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
readInterestDue.getAmount());
loan.addRepaymentScheduleInstallment(installment);
}
loan.submitApplication(submittedOn, command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(), command.getInterestCalculatedFromDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getSubmittedOnNote())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getSubmittedOnNote());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getId());
}
private boolean isBeforeToday(final LocalDate date) {
return date.isBefore(new LocalDate());
}
@@ -253,34 +136,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
@Transactional
@Override
public EntityIdentifier deleteLoan(Long loanId) {
AppUser currentUser = extractAuthenticatedUser();
Loan loan = this.loanRepository.findOne(loansThatMatch(
currentUser.getOrganisation(), loanId));
if (loan == null) {
throw new ApplicationDomainRuleException(loanIdentifierDoesNotExistError(loanId), "No loan exists with id: " + loanId);
}
if (loan.isNotSubmittedAndPendingApproval()) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.cannot.delete.loan.in.its.present.state", "eventDate");
throw new ApplicationDomainRuleException(Arrays.asList(errorResponse), "Loan must be in pending approval state.");
}
Long clientId = loan.getClient().getId();
List<Note> relatedNotes = this.noteRepository.findByLoanId(loan.getId());
this.noteRepository.deleteInBatch(relatedNotes);
this.loanRepository.delete(loanId);
return new EntityIdentifier(clientId);
}
private List<ErrorResponse> loanIdentifierDoesNotExistError(Long loanId) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.loan.with.identifier.exists", "id", loanId);
return Arrays.asList(errorResponse);
@@ -444,7 +299,6 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
Integer repaidEvery = loan.getLoanRepaymentScheduleDetail().getRepayEvery();
Integer selectedRepaymentFrequency = loan.getLoanRepaymentScheduleDetail().getRepaymentPeriodFrequencyType().getValue();
Integer selectedRepaymentSchedule = loan.getLoanRepaymentScheduleDetail().getAmortizationMethod().getValue();
boolean flexibleRepaymentSchedule = loan.isFlexibleRepaymentSchedule();
// use annual percentage rate to re-calculate loan schedule for late disbursement
Number interestRatePerPeriod = interestRatePerYear;
@@ -452,12 +306,11 @@ public class WritePlatformServiceJpaRepositoryImpl implements WritePlatformServi
Integer interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod().getValue();
Integer interestCalculationInPeriod = loan.getLoanRepaymentScheduleDetail().getInterestCalculationPeriodMethod().getValue();
boolean interestRebateAllowed = loan.getLoanRepaymentScheduleDetail().isInterestRebateAllowed();
CalculateLoanScheduleCommand calculateCommand = new CalculateLoanScheduleCommand(currencyCode, currencyDigits, principalAsDecimal,
interestRatePerPeriod, interestRateFrequencyMethod, interestMethod, interestCalculationInPeriod,
repaidEvery, selectedRepaymentFrequency, numberOfInstallments,
selectedRepaymentSchedule, flexibleRepaymentSchedule, interestRebateAllowed, actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
selectedRepaymentSchedule, actualDisbursementDate, repaymentsStartingFromDate, interestCalculatedFromDate);
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateCommand);
@@ -1,7 +1,12 @@
package org.mifosng.platform.api;
import java.math.BigDecimal;
import java.util.Locale;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@@ -9,10 +14,20 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.WritePlatformService;
import org.mifosng.platform.api.infrastructure.ApiDataConversionService;
import org.mifosng.platform.loan.service.CalculationPlatformService;
import org.mifosng.platform.loan.service.LoanReadPlatformService;
import org.mifosng.platform.loan.service.LoanWritePlatformService;
import org.mifosng.platform.user.domain.AppUser;
import org.mifosng.platform.user.domain.AppUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
@@ -31,9 +46,21 @@ public class LoansApiResource {
@Autowired
private LoanReadPlatformService loanReadPlatformService;
@Autowired
private LoanWritePlatformService loanWritePlatformService;
@Autowired
private ReadPlatformService readPlatformService;
@Autowired
private WritePlatformService writePlatformService;
@Autowired
private CalculationPlatformService calculationPlatformService;
@Autowired
private ApiDataConversionService apiDataConversionService;
@Autowired
private AppUserRepository appUserRepository;
@@ -51,6 +78,8 @@ public class LoansApiResource {
@Produces({ MediaType.APPLICATION_JSON})
public Response retrieveDetailsForNewLoanApplicationStepOne(@QueryParam("clientId") final Long clientId, @QueryParam("productId") final Long productId) {
hardcodeUserIntoSecurityContext();
NewLoanWorkflowStepOneData workflowData = this.loanReadPlatformService.retrieveClientAndProductDetails(clientId, productId);
return Response.ok().entity(workflowData).build();
@@ -68,4 +97,58 @@ public class LoansApiResource {
return Response.ok().entity(loanAccount).build();
}
@POST
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({MediaType.APPLICATION_JSON})
public Response calculateLoanSchedule(@QueryParam("command") final String commandParam, final SubmitLoanApplicationCommand command) {
hardcodeUserIntoSecurityContext();
LocalDate expectedDisbursementDate = apiDataConversionService.convertFrom(command.getExpectedDisbursementDateFormatted(), "expectedDisbursementDateFormatted", command.getDateFormat());
LocalDate repaymentsStartingFromDate = apiDataConversionService.convertFrom(command.getRepaymentsStartingFromDateFormatted(), "repaymentsStartingFromDateFormatted", command.getDateFormat());
LocalDate interestCalculatedFromDate = apiDataConversionService.convertFrom(command.getInterestCalculatedFromDateFormatted(), "interestCalculatedFromDateFormatted", command.getDateFormat());
LocalDate submittedOnDate = apiDataConversionService.convertFrom(command.getSubmittedOnDateFormatted(), "submittedOnDateFormatted", command.getDateFormat());
command.setExpectedDisbursementDate(expectedDisbursementDate);
command.setRepaymentsStartingFromDate(repaymentsStartingFromDate);
command.setInterestCalculatedFromDate(interestCalculatedFromDate);
command.setSubmittedOnDate(submittedOnDate);
// FIXME - pass in locale through query string or in 'request body'
Locale clientApplicationLocale = Locale.UK;
BigDecimal principal = this.apiDataConversionService.convertFrom(command.getPrincipalFormatted(), "principalFormatted", clientApplicationLocale);
BigDecimal interestRatePerPeriod = this.apiDataConversionService.convertFrom(command.getInterestRatePerPeriodFormatted(), "interestRatePerPeriodFormatted", clientApplicationLocale);
BigDecimal inArrearsToleranceAmount = this.apiDataConversionService.convertFrom(command.getInArrearsToleranceAmountFormatted(), "inArrearsToleranceAmountFormatted", clientApplicationLocale);
command.setPrincipal(principal);
command.setInterestRatePerPeriod(interestRatePerPeriod);
command.setInArrearsToleranceAmount(inArrearsToleranceAmount);
CalculateLoanScheduleCommand calculateLoanScheduleCommand = command.toCalculateLoanScheduleCommand();
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(calculateLoanScheduleCommand);
// for now just auto generating the loan schedule and setting support for 'manual' loan schedule creation later.
command.setLoanSchedule(loanSchedule);
if (StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase("calculateLoanSchedule")) {
return Response.ok().entity(loanSchedule).build();
}
EntityIdentifier identifier = this.loanWritePlatformService.submitLoanApplication(command);
return Response.ok().entity(identifier).build();
}
@DELETE
@Path("{loanId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({ MediaType.APPLICATION_JSON})
public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) {
hardcodeUserIntoSecurityContext();
EntityIdentifier identifier = this.loanWritePlatformService.deleteLoan(loanId);
return Response.ok().entity(identifier).build();
}
}
@@ -1,9 +1,14 @@
package org.mifosng.platform.api.infrastructure;
import java.math.BigDecimal;
import java.util.Locale;
import org.joda.time.LocalDate;
public interface ApiDataConversionService {
LocalDate convertFrom(String dateAsString, String parameterName, String dateFormat);
BigDecimal convertFrom(String principalFormatted, String parameterName, Locale clientApplicationLocale);
}
@@ -1,5 +1,8 @@
package org.mifosng.platform.api.infrastructure;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -33,4 +36,28 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
return eventLocalDate;
}
@Override
public BigDecimal convertFrom(String numericalValueFormatted, String parameterName, Locale clientApplicationLocale) {
try {
BigDecimal number = null;
if (StringUtils.isNotBlank(numericalValueFormatted)) {
String sourceWithoutSpaces = numericalValueFormatted.replaceAll(" ", "");
NumberFormat format = NumberFormat.getNumberInstance(clientApplicationLocale);
Number parsedNumber = format.parse(sourceWithoutSpaces);
number = BigDecimal.valueOf(Double.valueOf(parsedNumber.doubleValue()));
}
return number;
} catch (ParseException e) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.number.format", "The parameter " + parameterName + " is invalid.", parameterName, numericalValueFormatted);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}
@@ -0,0 +1,29 @@
package org.mifosng.platform.exceptions;
/**
* A {@link RuntimeException} thrown when valid api request end up violating some domain rule.
*/
public class PlatformDomainRuleException extends RuntimeException {
private final String globalisationMessageCode;
private final String defaultUserMessage;
private final Object[] defaultUserMessageArgs;
public PlatformDomainRuleException(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) {
this.globalisationMessageCode = globalisationMessageCode;
this.defaultUserMessage = defaultUserMessage;
this.defaultUserMessageArgs = defaultUserMessageArgs;
}
public String getGlobalisationMessageCode() {
return globalisationMessageCode;
}
public String getDefaultUserMessage() {
return defaultUserMessage;
}
public Object[] getDefaultUserMessageArgs() {
return defaultUserMessageArgs;
}
}
@@ -70,6 +70,10 @@ public class DerivedLoanDataProcessor {
Collections.sort(loanRepayments, new LoanRepaymentDataComparator());
if (loanRepayments.isEmpty()) {
loanRepayments = null;
}
return new DerivedLoanData(processRepaymentScheduleData, summaryData, loanRepayments);
}
@@ -131,6 +131,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Transient
private final InterestRebateCalculatorFactory interestRebateCalculatorFactory = new DailyEquivalentInterestRebateCalculatorFactory();
public static Loan createNew(Organisation organisation,
LoanProduct loanProduct, Client client,
LoanProductRelatedDetail loanRepaymentScheduleDetail) {
return new Loan(organisation, client, loanProduct, loanRepaymentScheduleDetail, null);
}
public Loan() {
this.organisation = null;
this.client = null;
@@ -3,6 +3,10 @@ package org.mifosng.platform.loan.domain;
import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.organisation.domain.Organisation;
/**
* remove in favour of static factory method on {@link Loan}. No advantage to builder patter in this case?
*/
@Deprecated
public class LoanBuilder {
private Organisation organisation;
@@ -0,0 +1,218 @@
package org.mifosng.platform.loan.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
public class CalculateLoanScheduleCommandValidator {
private final CalculateLoanScheduleCommand command;
public CalculateLoanScheduleCommandValidator(CalculateLoanScheduleCommand command) {
this.command = command;
}
public void validate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (StringUtils.isBlank(command.getCurrencyCode())) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.cannot.be.empty", "The parameter currencyCode cannot be blank.", "currencyCode");
dataValidationErrors.add(error);
}
if (command.getDigitsAfterDecimal() != null) {
if (command.getDigitsAfterDecimal() < 0 || command.getDigitsAfterDecimal() > 6) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.digitsAfterDecimal.must.be.between.zero.and.six.inclusive", "The parameter digitsAfterDecimal must be a value between 1 and 6.", "digitsAfterDecimal", command.getDigitsAfterDecimal(), 1, 6);
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.currency.digitsAfterDecimal.cannot.be.empty", "The parameter digitsAfterDecimal cannot be blank.", "digitsAfterDecimal");
dataValidationErrors.add(error);
}
// FIXME - validate the number of digits before and after decimal provided (before is limited by database field, after is limited by number selected for product (and ultimately by the database field))
if (command.getPrincipal() != null) {
if (command.getPrincipal().doubleValue() <= Double.valueOf("0.0")) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.must.be.greater.than.zero", "The parameter principalMoney must be greater than zero.", "principalMoney");
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.cannot.be.empty", "The parameter principalMoney cannot be blank.", "principalMoney");
dataValidationErrors.add(error);
}
if (command.getRepaymentFrequency() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
ApiParameterError error = null;
switch (frequencyType) {
case DAYS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 365);
break;
case WEEKS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 52);
break;
case MONTHS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 12);
break;
case YEARS:
error = validateNumberExistsAndInRange("validation.msg.product.repayment.repaidEvery", "repaidEvery", command.getRepaymentEvery(), 1, 2);
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.repayment.frequency.invalid", "The parameter repaymentFrequency is invalid.", "repaymentFrequency", command.getRepaymentFrequency());
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.repayment.frequency.cannot.be.empty", "The parameter repaymentFrequency cannot be empty.", "repaymentFrequency");
dataValidationErrors.add(error);
}
if (command.getNumberOfRepayments() != null) {
if (command.getNumberOfRepayments() < 1 || command.getNumberOfRepayments() > 100) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.number.of.repayments.is.out.of.range", "The parameter numberOfRepayments must between {1} and {2}.", "numberOfRepayments", command.getNumberOfRepayments(), 1, 100);
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.number.of.repayments.cannot.be.empty", "The parameter numberOfRepayments cannot be empty.", "numberOfRepayments");
dataValidationErrors.add(error);
}
if (command.getAmortizationMethod() != null) {
AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
ApiParameterError error = null;
switch (amortizationMethod) {
case EQUAL_PRINCIPAL:
break;
case EQUAL_INSTALLMENTS:
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.amortization.method.invalid", "The parameter amortizationMethod is invalid.", "amortizationMethod", command.getAmortizationMethod());
dataValidationErrors.add(error);
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.amortization.method.cannot.be.empty", "The parameter amortizationMethod cannot be empty.", "amortizationMethod");
dataValidationErrors.add(error);
}
if (command.getExpectedDisbursementDate() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.expectedDisbursementDate.method.cannot.be.blank", "The parameter expectedDisbursementDate cannot be empty.", "expectedDisbursementDate");
dataValidationErrors.add(error);
} else {
if (command.getRepaymentsStartingFromDate() != null
&& command.getExpectedDisbursementDate().isAfter(command.getRepaymentsStartingFromDate())) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.expectedDisbursementDate.cannot.be.after.first.repayment.date",
"The parameter expectedDisbursementDate has a date which falls after the given first repayment date.", "expectedDisbursementDate",
command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate());
dataValidationErrors.add(error);
}
}
if (command.getRepaymentsStartingFromDate() != null && command.getInterestCalculatedFromDate() == null) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.interestCalculatedFromDate.must.be.entered.when.using.repayments.startfrom.field",
"The parameter interestCalculatedFromDate cannot be empty when first repayment date is provided.", "interestCalculatedFromDate", command.getRepaymentsStartingFromDate());
dataValidationErrors.add(error);
} else if (command.getRepaymentsStartingFromDate() == null && command.getInterestCalculatedFromDate() != null) {
// validate interestCalculatedFromDate is after or on repaymentsStartingFromDate
if (command.getExpectedDisbursementDate().isAfter(command.getInterestCalculatedFromDate())) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.interestCalculatedFromDate.cannot.be.before.disbursement.date",
"The parameter interestCalculatedFromDate cannot be before the date given for expected disbursement.", "interestCalculatedFromDate", command.getInterestCalculatedFromDate(), command.getExpectedDisbursementDate());
dataValidationErrors.add(error);
}
}
if (command.getInterestRatePerPeriod()!= null) {
if (command.getInterestRatePerPeriod().doubleValue() < Double.valueOf("0.0")) {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.rate.per.period.amount.cannot.be.negative",
"The parameter interestRatePerPeriodFormatted cannot be less than zero.", "interestRatePerPeriodFormatted",
command.getInterestRatePerPeriod());
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.rate.per.period.amount.cannot.be.empty",
"The parameter interestRatePerPeriodFormatted cannot be empty.", "interestRatePerPeriodFormatted",
command.getInterestRatePerPeriod());
dataValidationErrors.add(error);
}
// interest rate period type
if (command.getInterestRateFrequencyMethod() != null) {
PeriodFrequencyType frequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
ApiParameterError error = null;
switch (frequencyType) {
case DAYS:
break;
case WEEKS:
break;
case MONTHS:
break;
case YEARS:
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.interest.frequency.invalid",
"The parameter interestRateFrequencyMethod cannot be empty.", "interestRateFrequencyMethod", command.getInterestRateFrequencyMethod());
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.frequency.cannot.be.empty",
"The parameter interestRateFrequencyMethod cannot be empty.", "interestRateFrequencyMethod");
dataValidationErrors.add(error);
}
if (command.getInterestMethod() != null) {
InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
ApiParameterError error = null;
switch (interestMethod) {
case DECLINING_BALANCE:
break;
case FLAT:
break;
default:
error = ApiParameterError.parameterError("validation.msg.product.interest.method.invalid",
"The parameter interestMethod is invalid.", "interestMethod", command.getInterestMethod());
break;
}
if (error != null) {
dataValidationErrors.add(error);
}
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.interest.method.cannot.be.empty",
"The parameter interestMethod cannot be empty.", "interestMethod");
dataValidationErrors.add(error);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
private ApiParameterError validateNumberExistsAndInRange(String errorCodePreFix, String fieldName, Integer value, int min, int max) {
ApiParameterError error = null;
if (value == null) {
error = ApiParameterError.parameterError(errorCodePreFix + ".cannot.be.empty", "The parameter " + fieldName + " cannot be blank.", fieldName);
} else {
if (value < min || value > max) {
error = ApiParameterError.parameterError(errorCodePreFix + ".outside.allowed.range", "The parameter " + fieldName + " must be between {0} and {1} inclusive.", fieldName, min, max);
}
}
return error;
}
}
@@ -1,4 +1,4 @@
package org.mifosng.platform;
package org.mifosng.platform.loan.service;
import org.joda.time.LocalDate;
import org.mifosng.data.LoanPayoffReadModel;
@@ -1,4 +1,4 @@
package org.mifosng.platform;
package org.mifosng.platform.loan.service;
import java.math.BigDecimal;
@@ -88,7 +88,6 @@ public class CalculationPlatformServiceImpl implements
@Override
public LoanPayoffReadModel calculatePayoffOn(final Long loanId, final LocalDate payoffDate) {
// TODO - use jdbc approach to calculating payoff if possible.
final Loan loan = this.loanRepository.findOne(loanId);
final LoanPayoffSummary payoffSummary = loan.getPayoffSummaryOn(payoffDate);
@@ -0,0 +1,14 @@
package org.mifosng.platform.loan.service;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.springframework.security.access.prepost.PreAuthorize;
public interface LoanWritePlatformService {
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE', 'CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE')")
EntityIdentifier submitLoanApplication(SubmitLoanApplicationCommand command);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_LOAN_THAT_IS_SUBMITTED_AND_NOT_APPROVED')")
EntityIdentifier deleteLoan(Long loanId);
}
@@ -0,0 +1,171 @@
package org.mifosng.platform.loan.service;
import static org.mifosng.platform.Specifications.loansThatMatch;
import java.math.BigDecimal;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.MoneyData;
import org.mifosng.data.ScheduledLoanInstallment;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.client.domain.Note;
import org.mifosng.platform.client.domain.NoteRepository;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.exceptions.NoAuthorizationException;
import org.mifosng.platform.exceptions.PlatformDomainRuleException;
import org.mifosng.platform.exceptions.PlatformResourceNotFoundException;
import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.DefaultLoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.InterestCalculationPeriodMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.Loan;
import org.mifosng.platform.loan.domain.LoanLifecycleStateMachine;
import org.mifosng.platform.loan.domain.LoanProduct;
import org.mifosng.platform.loan.domain.LoanProductRelatedDetail;
import org.mifosng.platform.loan.domain.LoanProductRepository;
import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.loan.domain.LoanStatus;
import org.mifosng.platform.loan.domain.LoanStatusRepository;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
import org.mifosng.platform.loanschedule.domain.AprCalculator;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.user.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
private final PlatformSecurityContext context;
private final LoanRepository loanRepository;
private final LoanProductRepository loanProductRepository;
private final LoanStatusRepository loanStatusRepository;
private final ClientRepository clientRepository;
private final NoteRepository noteRepository;
private final AprCalculator aprCalculator = new AprCalculator();
@Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final LoanRepository loanRepository, final LoanStatusRepository loanStatusRepository, final LoanProductRepository loanProductRepository,
final ClientRepository clientRepository, final NoteRepository noteRepository) {
this.context = context;
this.loanRepository = loanRepository;
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
this.loanStatusRepository = loanStatusRepository;
this.noteRepository = noteRepository;
}
private boolean isBeforeToday(final LocalDate date) {
return date.isBefore(new LocalDate());
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
List<LoanStatus> allowedLoanStatuses = this.loanStatusRepository.findAll();
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
@Transactional
@Override
public EntityIdentifier submitLoanApplication(final SubmitLoanApplicationCommand command) {
AppUser currentUser = context.authenticatedUser();
SubmitLoanApplicationCommandValidator validator = new SubmitLoanApplicationCommandValidator(command);
validator.validate();
LocalDate submittedOn = command.getSubmittedOnDate();
if (this.isBeforeToday(submittedOn) && currentUser.hasNotPermissionForAnyOf("CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE", "PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE")) {
throw new NoAuthorizationException("Cannot add backdated loan.");
}
// FIXME - looks like assembly of LoanProductRelatedDetail is common with loan product and calculate loan schedule areas. remove duplication into an assembler.
LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
Client client = this.clientRepository.findOne(command.getApplicantId());
MonetaryCurrency currency = new MonetaryCurrency(command.getCurrencyCode(), command.getDigitsAfterDecimal());
final BigDecimal defaultNominalInterestRatePerPeriod = BigDecimal.valueOf(command.getInterestRatePerPeriod().doubleValue());
final PeriodFrequencyType interestPeriodFrequencyType = PeriodFrequencyType.fromInt(command.getInterestRateFrequencyMethod());
BigDecimal defaultAnnualNominalInterestRate = aprCalculator.calculateFrom(interestPeriodFrequencyType, command.getInterestRatePerPeriod());
final InterestMethod interestMethod = InterestMethod.fromInt(command.getInterestMethod());
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = InterestCalculationPeriodMethod.fromInt(command.getInterestCalculationPeriodMethod());
final Integer repayEvery = command.getRepaymentEvery();
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command.getRepaymentFrequency());
final Integer defaultNumberOfInstallments = command.getNumberOfRepayments();
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.getAmortizationMethod());
final boolean flexibleRepaymentSchedule = command.isFlexibleRepaymentSchedule();
final boolean interestRebateAllowed = command.isInterestRebateAllowed();
LoanProductRelatedDetail loanRepaymentScheduleDetail = new LoanProductRelatedDetail(currency,
command.getPrincipal(), defaultNominalInterestRatePerPeriod, interestPeriodFrequencyType, defaultAnnualNominalInterestRate,
interestMethod, interestCalculationPeriodMethod,
repayEvery, repaymentFrequencyType, defaultNumberOfInstallments, amortizationMethod, command.getInArrearsToleranceAmount(),
flexibleRepaymentSchedule, interestRebateAllowed);
LoanSchedule loanSchedule = command.getLoanSchedule();
List<ScheduledLoanInstallment> loanRepaymentSchedule = loanSchedule.getScheduledLoanInstallments();
Loan loan = Loan.createNew(currentUser.getOrganisation(), loanProduct, client, loanRepaymentScheduleDetail);
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
MoneyData readPrincipalDue = scheduledLoanInstallment.getPrincipalDue();
MoneyData readInterestDue = scheduledLoanInstallment.getInterestDue();
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(
loan, scheduledLoanInstallment.getInstallmentNumber(),
scheduledLoanInstallment.getPeriodStart(),
scheduledLoanInstallment.getPeriodEnd(), readPrincipalDue.getAmount(),
readInterestDue.getAmount());
loan.addRepaymentScheduleInstallment(installment);
}
loan.submitApplication(submittedOn, command.getExpectedDisbursementDate(), command.getRepaymentsStartingFromDate(), command.getInterestCalculatedFromDate(), defaultLoanLifecycleStateMachine());
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(command.getSubmittedOnNote())) {
Note note = Note.loanNote(currentUser.getOrganisation(), loan, command.getSubmittedOnNote());
this.noteRepository.save(note);
}
return new EntityIdentifier(loan.getId());
}
@Transactional
@Override
public EntityIdentifier deleteLoan(Long loanId) {
AppUser currentUser = context.authenticatedUser();
Loan loan = this.loanRepository.findOne(loansThatMatch(currentUser.getOrganisation(), loanId));
if (loan == null) {
throw new PlatformResourceNotFoundException("error.msg.loan.id.invalid", "Loan with identifier {0} does not exist", loanId);
}
if (loan.isNotSubmittedAndPendingApproval()) {
throw new PlatformDomainRuleException("error.msg.cannot.delete.loan.in.its.present.state", "Loan with identifier {0} cannot be deleted in its current state.", loanId);
}
Long clientId = loan.getClient().getId();
List<Note> relatedNotes = this.noteRepository.findByLoanId(loan.getId());
this.noteRepository.deleteInBatch(relatedNotes);
this.loanRepository.delete(loanId);
return new EntityIdentifier(clientId);
}
}
@@ -1,12 +1,12 @@
package org.mifosng.platform;
package org.mifosng.platform.loan.service;
import java.util.ArrayList;
import java.util.List;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ApiParameterError;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.platform.exceptions.NewDataValidationException;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
public class SubmitLoanApplicationCommandValidator {
@@ -17,19 +17,23 @@ public class SubmitLoanApplicationCommandValidator {
}
public void validate() {
List<ErrorResponse> dataValidationErrors = new ArrayList<ErrorResponse>();
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
if (command.getLoanSchedule() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.submit.loan.loan.schedule.cannot.be.blank", "loanSchedule");
ApiParameterError error = ApiParameterError.parameterError("validation.msg.submit.loan.loan.schedule.cannot.be.blank",
"The parameter loanSchedule cannot be empty.", "loanSchedule");
dataValidationErrors.add(error);
}
if (command.getSubmittedOnDate() == null) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.submitted.on.date.cannot.be.blank", "submittedOnDate");
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.blank",
"The parameter submittedOnDateFormatted cannot be empty.", "submittedOnDateFormatted");
dataValidationErrors.add(error);
} else {
if (command.getSubmittedOnDate().isAfter(command.getExpectedDisbursementDate())) {
ErrorResponse error = new ErrorResponse("validation.msg.loan.submitted.on.date.cannot.be.after.expectedDisbursementDate", "submittedOnDate");
ApiParameterError error = ApiParameterError.parameterError("validation.msg.loan.submitted.on.date.cannot.be.after.expectedDisbursementDate",
"The date of parameter submittedOnDateFormatted cannot fall after the date given for expectedDisbursementDateFormatted.", "submittedOnDateFormatted",
command.getSubmittedOnDateFormatted(), command.getExpectedDisbursementDateFormatted());
dataValidationErrors.add(error);
}
}
@@ -45,8 +49,6 @@ public class SubmitLoanApplicationCommandValidator {
command.getRepaymentFrequency(),
command.getNumberOfRepayments(),
command.getAmortizationMethod(),
command.isFlexibleRepaymentSchedule(),
command.isInterestRebateAllowed(),
command.getExpectedDisbursementDate(),
command.getRepaymentsStartingFromDate(),
command.getInterestCalculatedFromDate());
@@ -54,12 +56,12 @@ public class SubmitLoanApplicationCommandValidator {
CalculateLoanScheduleCommandValidator validator = new CalculateLoanScheduleCommandValidator(
calculateLoanScheduleCommand);
validator.validate();
} catch (NewDataValidationException e) {
dataValidationErrors.addAll(e.getValidationErrors());
} catch (PlatformApiDataValidationException e) {
dataValidationErrors.addAll(e.getErrors());
}
if (!dataValidationErrors.isEmpty()) {
throw new NewDataValidationException(dataValidationErrors, "Data validation errors exist.");
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors);
}
}
}
@@ -11,6 +11,8 @@ import org.mifosng.platform.loan.domain.AmortizationMethod;
import org.mifosng.platform.loan.domain.InterestMethod;
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
// FIXME - There is a lot of common validation across parameters for LoanProduct create/update, loan schedule calculation and submit new loan application flows.
// - reduce duplicator across this validators.
public class LoanProductCommandValidator {
public void validateForUpdate(LoanProductCommand command) {
@@ -67,7 +69,6 @@ public class LoanProductCommandValidator {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.must.be.greater.than.zero", "The parameter amount must be greater than zero.", "amount");
dataValidationErrors.add(error);
}
// TODO - check number of digits before decimal and after decimal
} else {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.product.principal.amount.cannot.be.empty", "The parameter amount cannot be blank.", "amount");
dataValidationErrors.add(error);
@@ -17,13 +17,19 @@ import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.mifosng.data.LoanPayoffReadModel;
import org.mifosng.data.MoneyData;
import org.mifosng.platform.CalculationPlatformService;
import org.mifosng.platform.loan.service.CalculationPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.sun.jersey.api.json.JSONWithPadding;
/**
* Payoff functionality was something CreoCore was looking for e.g. if paid in full on date x what should be paid? full minus any rebate or incentive provided.
*
* NOTE: JSONP functionality not needed now due to use of CORS
*/
@Deprecated
@Path("/open/loan/{loanId}")
@Component
@Scope("singleton")
@@ -5,7 +5,6 @@ import java.util.Arrays;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -20,15 +19,11 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.platform.CalculationPlatformService;
import org.mifosng.platform.ReadPlatformService;
import org.mifosng.platform.ReadPlatformServiceImpl;
import org.mifosng.platform.WritePlatformService;
@@ -49,92 +44,12 @@ public class LoanResource {
private final static Logger logger = LoggerFactory.getLogger(ReadPlatformServiceImpl.class);
@Autowired
private CalculationPlatformService calculationPlatformService;
@Autowired
private ReadPlatformService readPlatformService;
@Autowired
private WritePlatformService writePlatformService;
@POST
@Path("calculate")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response calculateLoanSchedule(final CalculateLoanScheduleCommand command) {
try {
LoanSchedule loanSchedule = this.calculationPlatformService.calculateLoanSchedule(command);
return Response.ok().entity(loanSchedule).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
}
@POST
@Path("new")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response submitLoanApplication(final SubmitLoanApplicationCommand command) {
try {
EntityIdentifier identifier = this.writePlatformService.submitLoanApplication(command);
return Response.ok().entity(identifier).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
}
}
@DELETE
@Path("{loanId}")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response deleteLoanApplication(@PathParam("loanId") final Long loanId) {
try {
EntityIdentifier identifier = this.writePlatformService.deleteLoan(loanId);
return Response.ok().entity(identifier).build();
} catch (UnAuthenticatedUserException e) {
throw new WebApplicationException(Response.status(Status.UNAUTHORIZED).build());
} catch (AccessDeniedException e) {
ErrorResponse errorResponse = new ErrorResponse("error.msg.no.permission", "id");
ErrorResponseList list = new ErrorResponseList(Arrays.asList(errorResponse));
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(list).build());
} catch (ApplicationDomainRuleException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getErrors())).build());
} catch (NewDataValidationException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(e.getValidationErrors())).build());
} catch (Exception e) {
logger.error(e.getMessage(), e);
List<ErrorResponse> allErrors = new ArrayList<ErrorResponse>();
ErrorResponse err = new ErrorResponse("unknown.error", "error", e.getMessage());
allErrors.add(err);
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new ErrorResponseList(allErrors)).build());
}
}
@POST
@Path("approve")
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })