move functionality around new loan application to loan resource.

This commit is contained in:
Keith Woodlock
2012-05-16 18:21:35 +01:00
parent 75054feeee
commit bf7783fc94
14 changed files with 218 additions and 236 deletions
@@ -6,7 +6,6 @@ import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
@@ -26,8 +25,6 @@ public interface CommonRestOperations {
LoanSchedule calculateLoanSchedule(CalculateLoanScheduleCommand command);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationStepOneDetails(Long clientId);
Long submitLoanApplication(SubmitLoanApplicationCommand command);
EntityIdentifier deleteLoan(Long loanId);
@@ -55,6 +52,4 @@ public interface CommonRestOperations {
LoanRepaymentData retrieveNewLoanWaiverDetails(Long loanId);
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationDetails(Long clientId, Long productId);
}
@@ -14,7 +14,6 @@ import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanProductList;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
@@ -138,49 +137,6 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
}
}
@Override
public NewLoanWorkflowStepOneData retrieveNewLoanApplicationStepOneDetails(
final Long clientId) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/new/" + clientId.toString()
+ "/workflow/one"));
ResponseEntity<NewLoanWorkflowStepOneData> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
NewLoanWorkflowStepOneData.class);
// for now ensure user must select product on step one (even if
// there is only one product!)
NewLoanWorkflowStepOneData data = s.getBody();
data.setProductId(null);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public NewLoanWorkflowStepOneData retrieveNewLoanApplicationDetails(
Long clientId, Long productId) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/new/" + clientId.toString()
+ "/product/" + productId.toString()));
ResponseEntity<NewLoanWorkflowStepOneData> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
NewLoanWorkflowStepOneData.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public Long submitLoanApplication(final SubmitLoanApplicationCommand command) {
try {
@@ -1,8 +1,12 @@
package org.mifosng.ui.admin;
import java.security.Principal;
import org.mifosng.oauth.ConsumerUserDetails;
import org.mifosng.ui.reporting.ReportingRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -71,4 +75,18 @@ public class ApplicationPagesRoutesController {
public String userSettingsScreen() {
return "admin/accountsettings";
}
@RequestMapping(value = "/portfolio/client/{clientId}/loan/new", method = RequestMethod.GET)
public String loadLoanCreationWorkflow(final Model model, @PathVariable("clientId") final Long clientId, final Principal principal) {
UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) principal;
ConsumerUserDetails user = (ConsumerUserDetails) authenticationToken.getPrincipal();
if (user.hasNoAuthorityToSumitLoanApplication()) {
throw new AccessDeniedException("");
}
model.addAttribute("clientId", clientId);
return "newloanapplication";
}
}
@@ -1,7 +1,6 @@
package org.mifosng.ui.loan;
import java.math.BigDecimal;
import java.security.Principal;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
@@ -17,20 +16,15 @@ 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.NewLoanWorkflowStepOneData;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.oauth.ConsumerUserDetails;
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.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@@ -97,20 +91,6 @@ public class LoanController {
}
}
@RequestMapping(value = "/portfolio/client/{clientId}/loan/new", method = RequestMethod.GET)
public String loadLoanCreationWorkflow(final Model model, @PathVariable("clientId") final Long clientId, final Principal principal) {
UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) principal;
ConsumerUserDetails user = (ConsumerUserDetails) authenticationToken.getPrincipal();
if (user.hasNoAuthorityToSumitLoanApplication()) {
throw new AccessDeniedException("");
}
model.addAttribute("clientId", clientId);
return "newloanapplication";
}
@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,
@@ -196,16 +176,4 @@ public class LoanController {
return this.commonRestOperations.calculateLoanSchedule(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/product/{productId}/new", method = RequestMethod.GET)
public @ResponseBody NewLoanWorkflowStepOneData retrieveNewLoan(@PathVariable("clientId") Long clientId, @PathVariable("productId") Long productId) {
return this.commonRestOperations.retrieveNewLoanApplicationDetails(clientId, productId);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/loan/new/workflow/one", method = RequestMethod.GET)
public @ResponseBody NewLoanWorkflowStepOneData retrieveNewLoan(@PathVariable("clientId") Long clientId) {
return this.commonRestOperations.retrieveNewLoanApplicationStepOneDetails(clientId);
}
}
@@ -461,6 +461,7 @@ $(document).ready(function() {
$('.newloanbtn').button().click(function(e) {
var linkId = this.id;
var clientId = linkId.replace("newloanbtn", "");
// switch to jsp page focussed on new loan application flow.
var url = '${rootContext}portfolio/client/' + clientId + '/loan/new';
window.location.href = url;
e.preventDefault();
@@ -10,11 +10,10 @@
<head>
<jsp:include page="common-head.jsp" />
<c:url value="/" var="rootContext" />
<c:url value="/portfolio/client/${clientId}/loan/new/workflow/one" var="newLoanUrl" />
<script>
$(document).ready(function() {
$.views.registerHelpers({
$.views.registerHelpers({
money: function(monetaryObj) {
@@ -31,19 +30,19 @@ $(document).ready(function() {
return Globalize.format(monetaryObj.amount, "c" + digits);
},
decimal: function(number, digits) {
try {
return Globalize.format(number, "n" + digits);
} catch(e) {
return number +"(NaN)";
}
},
try {
return Globalize.format(number, "n" + digits);
} catch(e) {
return number +"(NaN)";
}
},
number: function(number) {
try {
return Globalize.format(number, "n0");
} catch(e) {
return number +"(NaN)";
}
},
try {
return Globalize.format(number, "n0");
} catch(e) {
return number +"(NaN)";
}
},
numberGreaterThanZero: function(number) {
try {
var num = number.toFixed(0);
@@ -52,40 +51,33 @@ $(document).ready(function() {
return false;
}
},
globalDate: function(dateParts) {
globalDate: function(localDateAsISOString) {
try {
if (dateParts === null) {
return "";
} else {
var year = dateParts[0];
var month = parseInt(dateParts[1]) - 1; // month is zero indexed
var day = dateParts[2];
var d = new Date();
d.setFullYear(year,month,day);
return Globalize.format(d,"dd MMMM yyyy");
}
if (localDateAsISOString === null) return "";
var dateParts = localDateAsISOString.split("-")
var year = dateParts[0];
var month = parseInt(dateParts[1]) - 1; // month is zero indexed
var day = dateParts[2];
var d = new Date();
d.setFullYear(year,month,day);
return Globalize.format(d,"dd MMMM yyyy");
} catch(e) {
return "??";
}
},
globalDateTime: function(dateParts) {
globalDateTime: function(dateInMillis) {
try {
if (dateParts === null) {
return "";
} else {
var d = new Date(dateParts);
return Globalize.format(d,"F");
}
var d = new Date(dateInMillis);
return Globalize.format(d,"F");
} catch(e) {
return "??";
}
}
});
function removeErrors(placeholderDiv) {
// remove error class from all input fields
var $inputs = $('#entityform :input');
@@ -119,17 +111,25 @@ $(document).ready(function() {
removeErrors(placeholderDiv);
var jsonErrors = JSON.parse(jqXHR.responseText);
var valErrors = jsonErrors.errors;
var errorArray = new Array();
var arrayIndex = 0;
$.each(jsonErrors, function() {
var fieldId = '#' + this.field;
$.each(valErrors, function() {
var fieldId = '#' + this.parameterName;
$(fieldId).addClass("ui-state-error");
var errorObj = new Object();
errorObj.field = this.field;
errorObj.code = this.code;
errorObj.message = jQuery.i18n.prop(this.code, this.args);
errorObj.field = this.parameterName;
errorObj.code = this.userMessageGlobalisationCode;
var argArray = new Array();
var argArrayIndex = 0;
$.each(this.args, function() {
argArray[argArrayIndex] = this.value;
argArrayIndex++;
});
// hardcoded support for six arguments
errorObj.message = jQuery.i18n.prop(this.userMessageGlobalisationCode, argArray[0], argArray[1], argArray[2], argArray[3], argArray[4], argArray[5]);
errorObj.value = this.value;
errorArray[arrayIndex] = errorObj;
@@ -149,6 +149,29 @@ $(document).ready(function() {
}
}
$.fn.serializeObject = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
if (this.name === 'selectedItems' || this.name === 'notSelectedItems') {
o[this.name] = new Array();
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
}
});
return o;
};
function calculateAnnualPercentageRate() {
var periodInterestRate = parseFloat($('#nominalInterestRate').val());
if (isNaN(periodInterestRate)) {
@@ -171,7 +194,7 @@ $(document).ready(function() {
function repopulateFullForm(clientId, productId) {
var url = '${rootContext}portfolio/client/' + clientId + '/product/' + productId + '/new';
var url = 'http://localhost:8080/mifosng-provider/api/v1/loans/template?clientId=' + clientId + '&productId=' + productId;
var jqxhr = $.ajax({
url: url,
@@ -312,7 +335,7 @@ $(document).ready(function() {
var jqxhr = $.ajax({
url: '${newLoanUrl}',
url: 'http://localhost:8080/mifosng-provider/api/v1/loans/template?clientId=${clientId}',
type: 'GET',
contentType: 'application/json',
dataType: 'json',
@@ -83,8 +83,9 @@
</td>
<td style="text-align:right;vertical-align:bottom;width:50px;">&nbsp;</td>
<td valign="top">
<label for="submittedOnDate"><spring:message code="form.label.loan.submitted.on"/></label>
<input id="submittedOnDate" name="submittedOnDate" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(submittedOnDate)}}" />
<input type="hidden" id="dateFormat" name="dateFormat" value="dd MMMM yyyy" />
<label for="submittedOnDateFormatted"><spring:message code="form.label.loan.submitted.on"/></label>
<input id="submittedOnDateFormatted" name="submittedOnDateFormatted" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(submittedOnDate)}}" />
<label for="submittedOnNote"><spring:message code="form.label.loan.submitted.on.note"/></label>
<textarea id="submittedOnNote" name="submittedOnNote" rows="2" cols="50" draggable="false">{{=submittedOnNote}}</textarea>
@@ -131,13 +132,13 @@
<tr>
<td><spring:message code="form.label.loan.expected.disbursement.date.on"/></td>
<td>
<input id="expectedDisbursementDate" name="expectedDisbursementDate" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(expectedDisbursementDate)}}" style="width:172px;"/>
<input id="expectedDisbursementDateFormatted" name="expectedDisbursementDateFormatted" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(expectedDisbursementDate)}}" style="width:172px;"/>
</td>
</tr>
<tr>
<td><spring:message code="form.label.loan.first.repayment.on"/></td>
<td>
<input id="repaymentsStartingFromDate" name="repaymentsStartingFromDate" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(repaymentsStartingFromDate)}}" style="width:172px;"/>
<input id="repaymentsStartingFromDateFormatted" name="repaymentsStartingFromDateFormatted" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(repaymentsStartingFromDate)}}" style="width:172px;"/>
</td>
</tr>
</table>