handle client resource and sub resources for notes and loanaccount summaries.

This commit is contained in:
Keith Woodlock
2012-05-15 16:45:16 +01:00
parent dc1c39c5ec
commit 04d4fbaf85
32 changed files with 1324 additions and 1695 deletions
@@ -2,27 +2,18 @@ package org.mifosng.ui;
import java.util.Collection;
import org.mifosng.data.ClientData;
import org.mifosng.data.ClientDataWithAccountsData;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.EnumOptionReadModel;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.LoanProductData;
import org.mifosng.data.LoanRepaymentData;
import org.mifosng.data.LoanSchedule;
import org.mifosng.data.NewLoanWorkflowStepOneData;
import org.mifosng.data.NoteData;
import org.mifosng.data.PermissionData;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.NoteCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UserCommand;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
public interface CommonRestOperations {
@@ -31,18 +22,11 @@ public interface CommonRestOperations {
void updateProtectedResource(ProtectedResourceDetails loadProtectedResourceDetailsById);
Collection<PermissionData> retrieveAllPermissions();
Collection<EnumOptionReadModel> retrieveAllPermissionGroups();
Collection<ClientData> retrieveAllIndividualClients();
Collection<LoanProductData> retrieveAllLoanProducts();
LoanSchedule calculateLoanSchedule(CalculateLoanScheduleCommand command);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationStepOneDetails(
Long clientId);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationStepOneDetails(Long clientId);
Long submitLoanApplication(SubmitLoanApplicationCommand command);
@@ -72,26 +56,5 @@ public interface CommonRestOperations {
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
EntityIdentifier updateCurrentUserDetails(UserCommand command);
ClientData retrieveNewIndividualClient();
ClientData retrieveClientDetails(Long clientId);
EntityIdentifier enrollClient(EnrollClientCommand command);
EntityIdentifier addNote(NoteCommand command);
EntityIdentifier updateNote(NoteCommand command);
NoteData retrieveClientNote(Long clientId, Long noteId);
Collection<NoteData> retrieveClientNotes(Long clientId);
ClientDataWithAccountsData retrieveClientAccount(Long clientId);
LoanAccountData retrieveLoanAccount(Long loanId);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationDetails(Long clientId,
Long productId);
NewLoanWorkflowStepOneData retrieveNewLoanApplicationDetails(Long clientId, Long productId);
}
@@ -6,34 +6,22 @@ import java.util.Collection;
import java.util.List;
import org.mifosng.configuration.ApplicationConfigurationService;
import org.mifosng.data.ClientData;
import org.mifosng.data.ClientDataWithAccountsData;
import org.mifosng.data.ClientList;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.EnumOptionList;
import org.mifosng.data.EnumOptionReadModel;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.ErrorResponseList;
import org.mifosng.data.LoanAccountData;
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.NoteData;
import org.mifosng.data.NoteDataList;
import org.mifosng.data.PermissionData;
import org.mifosng.data.PermissionList;
import org.mifosng.data.command.AdjustLoanTransactionCommand;
import org.mifosng.data.command.CalculateLoanScheduleCommand;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.LoanStateTransitionCommand;
import org.mifosng.data.command.LoanTransactionCommand;
import org.mifosng.data.command.NoteCommand;
import org.mifosng.data.command.SubmitLoanApplicationCommand;
import org.mifosng.data.command.UndoLoanApprovalCommand;
import org.mifosng.data.command.UndoLoanDisbursalCommand;
import org.mifosng.data.command.UserCommand;
import org.mifosng.ui.loanproduct.ClientValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
@@ -94,17 +82,6 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
return requestEntity;
}
@Override
public Collection<ClientData> retrieveAllIndividualClients() {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/client/all"));
ResponseEntity<ClientList> s = this.oauthRestServiceTemplate.exchange(
restUri, HttpMethod.GET, emptyRequest(), ClientList.class);
return s.getBody().getClients();
}
@Override
public Collection<LoanProductData> retrieveAllLoanProducts() {
URI restUri = URI.create(getBaseServerUrl().concat(
@@ -524,234 +501,4 @@ public class CommonRestOperationsImpl implements CommonRestOperations {
return new HttpEntity<CalculateLoanScheduleCommand>(command,
requestHeaders);
}
@Override
public EntityIdentifier updateCurrentUserDetails(UserCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/admin/user/current"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri, createUserRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
private HttpEntity<UserCommand> createUserRequest(final UserCommand command) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
HttpEntity<UserCommand> requestEntity = new HttpEntity<UserCommand>(
command, requestHeaders);
return requestEntity;
}
@Override
public Collection<PermissionData> retrieveAllPermissions() {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/admin/permissions/all"));
ResponseEntity<PermissionList> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
PermissionList.class);
return s.getBody().getPermissions();
}
@Override
public Collection<EnumOptionReadModel> retrieveAllPermissionGroups() {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/admin/permissiongroup/all"));
ResponseEntity<EnumOptionList> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
EnumOptionList.class);
return s.getBody().getOptions();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public ClientData retrieveNewIndividualClient() {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/client/new"));
ResponseEntity<ClientData> s = this.oauthRestServiceTemplate.exchange(
restUri, HttpMethod.GET, emptyRequest(), ClientData.class);
return s.getBody();
}
@Override
public ClientData retrieveClientDetails(Long clientId) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/client/").concat(clientId.toString()));
ResponseEntity<ClientData> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
ClientData.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier enrollClient(EnrollClientCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/client/new"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri, enrollClientRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
private HttpEntity<EnrollClientCommand> enrollClientRequest(
final EnrollClientCommand command) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
return new HttpEntity<EnrollClientCommand>(command, requestHeaders);
}
@Override
public EntityIdentifier addNote(NoteCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl()
.concat("api/protected/client/")
.concat(command.getClientId().toString())
.concat("/note/new"));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri, noteRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public EntityIdentifier updateNote(NoteCommand command) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/note/").concat(command.getId().toString()));
ResponseEntity<EntityIdentifier> s = this.oauthRestServiceTemplate
.postForEntity(restUri, noteRequest(command),
EntityIdentifier.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public NoteData retrieveClientNote(Long clientId, Long noteId) {
try {
URI restUri = URI.create(getBaseServerUrl()
.concat("api/protected/client/")
.concat(clientId.toString()).concat("/note/")
.concat(noteId.toString()));
ResponseEntity<NoteData> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
NoteData.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public Collection<NoteData> retrieveClientNotes(Long clientId) {
try {
URI restUri = URI.create(getBaseServerUrl()
.concat("api/protected/client/")
.concat(clientId.toString()).concat("/note/all"));
ResponseEntity<NoteDataList> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
NoteDataList.class);
return s.getBody().getNotes();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
private HttpEntity<NoteCommand> noteRequest(final NoteCommand command) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept", "application/xml");
requestHeaders.set("Content-Type", "application/xml");
return new HttpEntity<NoteCommand>(command, requestHeaders);
}
@Override
public ClientDataWithAccountsData retrieveClientAccount(Long clientId) {
try {
URI restUri = URI.create(getBaseServerUrl()
.concat("api/protected/client/")
.concat(clientId.toString()).concat("/withaccounts"));
ResponseEntity<ClientDataWithAccountsData> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
ClientDataWithAccountsData.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
@Override
public LoanAccountData retrieveLoanAccount(Long loanId) {
try {
URI restUri = URI.create(getBaseServerUrl().concat(
"api/protected/loan/" + loanId));
ResponseEntity<LoanAccountData> s = this.oauthRestServiceTemplate
.exchange(restUri, HttpMethod.GET, emptyRequest(),
LoanAccountData.class);
return s.getBody();
} catch (HttpStatusCodeException e) {
ErrorResponseList errorList = parseErrors(e);
throw new ClientValidationException(errorList.getErrors());
}
}
}
@@ -1,50 +0,0 @@
package org.mifosng.ui.admin;
import java.util.Collection;
import javax.servlet.http.HttpServletResponse;
import org.mifosng.data.ErrorResponse;
import org.mifosng.ui.loanproduct.ClientValidationException;
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.ResponseBody;
/**
* Controller for user and organisation administration functions.
*/
@Controller
public class AdministrationController {
@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();
}
@RequestMapping(value = "/org/admin", method = RequestMethod.GET)
public String organisationAdminScreen() {
return "admin/orghome";
}
@RequestMapping(value = "/org/admin/user", method = RequestMethod.GET)
public String userAdminScreen() {
return "admin/userhome";
}
@RequestMapping(value = "/org/admin/settings", method = RequestMethod.GET)
public String userSettingsScreen() {
return "admin/accountsettings";
}
}
@@ -0,0 +1,74 @@
package org.mifosng.ui.admin;
import org.mifosng.ui.reporting.ReportingRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
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;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller responsible for routing requests on client application to correct jsp page.
*/
@Controller
public class ApplicationPagesRoutesController {
private final ReportingRestOperations reportingRestOperations;
@Autowired
public ApplicationPagesRoutesController(final ReportingRestOperations reportingRestOperations) {
this.reportingRestOperations = reportingRestOperations;
}
@ExceptionHandler(AccessDeniedException.class)
public String accessDeniedException() {
return "unAuthorizedAction";
}
@RequestMapping(value="/forceOAuth", method = RequestMethod.GET)
public String forceOAuth() {
this.reportingRestOperations.hackToForceAuthentication();
return "redirect:/home";
}
@RequestMapping(value="/", method = RequestMethod.GET)
public String redirectToIndexPage() {
return "redirect:/home";
}
@RequestMapping(value = "/home", method = RequestMethod.GET)
public ModelAndView showIndividualClientsOnHomePage() {
return new ModelAndView("/home");
}
@RequestMapping(value="/switchToClient", method = RequestMethod.POST)
public String switchToClientView(@RequestParam("client") final Long client) {
return "redirect:/portfolio/client/" + client;
}
@RequestMapping(value = "/portfolio/client/{clientId}", method = RequestMethod.GET)
public String viewClientAccount(final Model model, @PathVariable("clientId") final Long clientId) {
model.addAttribute("clientId", clientId);
return "client/viewClientAccount";
}
@RequestMapping(value = "/org/admin", method = RequestMethod.GET)
public String organisationAdminScreen() {
return "admin/orghome";
}
@RequestMapping(value = "/org/admin/user", method = RequestMethod.GET)
public String userAdminScreen() {
return "admin/userhome";
}
@RequestMapping(value = "/org/admin/settings", method = RequestMethod.GET)
public String userSettingsScreen() {
return "admin/accountsettings";
}
}
@@ -1,180 +0,0 @@
package org.mifosng.ui.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
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.ClientData;
import org.mifosng.data.ClientDataWithAccountsData;
import org.mifosng.data.ClientList;
import org.mifosng.data.EntityIdentifier;
import org.mifosng.data.ErrorResponse;
import org.mifosng.data.LoanAccountData;
import org.mifosng.data.NoteData;
import org.mifosng.data.command.EnrollClientCommand;
import org.mifosng.data.command.NoteCommand;
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.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
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;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class ClientController {
private final CommonRestOperations commonRestOperations;
@Autowired
public ClientController(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;
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/note/all", method = RequestMethod.GET)
public @ResponseBody List<NoteData> retrieveClientNotes(@PathVariable("clientId") Long clientId) {
Collection<NoteData> clientNotes = this.commonRestOperations.retrieveClientNotes(clientId);
return new ArrayList<NoteData>(clientNotes);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/note/new", method = RequestMethod.GET)
public @ResponseBody NoteData retrieveNewNoteDetails(@PathVariable("clientId") Long clientId) {
NoteData noteData = new NoteData();
noteData.setClientId(clientId);
return noteData;
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/note/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier addClientNote(@PathVariable("clientId") Long clientId,
@RequestParam(value="note", required=false) String note) {
NoteCommand command = new NoteCommand();
command.setClientId(clientId);
command.setNote(note);
return this.commonRestOperations.addNote(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}/note/{noteId}", method = RequestMethod.GET)
public @ResponseBody NoteData retrieveExistingNoteDetails(@PathVariable("clientId") Long clientId, @PathVariable("noteId") Long noteId) {
return this.commonRestOperations.retrieveClientNote(clientId, noteId);
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/{clientId}/note/{noteId}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier updateClientNote(@PathVariable("clientId") Long clientId, @PathVariable("noteId") Long noteId,
@RequestParam(value="note", required=false) String note) {
NoteCommand command = new NoteCommand();
command.setId(noteId);
command.setClientId(clientId);
command.setNote(note);
return this.commonRestOperations.updateNote(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/all", method = RequestMethod.GET)
public @ResponseBody ClientList viewAllClients() {
return new ClientList(this.commonRestOperations.retrieveAllIndividualClients());
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/new", method = RequestMethod.GET)
public @ResponseBody ClientData newIndividualClientDetails() {
ClientData clientData = this.commonRestOperations.retrieveNewIndividualClient();
return clientData;
}
@RequestMapping(consumes="application/x-www-form-urlencoded", produces="application/json", value = "/portfolio/client/new", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody EntityIdentifier createClient(HttpServletRequest request,
@RequestParam(value="office", required=false) Long officeId,
@RequestParam(value="firstname", required=false) String firstname,
@RequestParam(value="lastname", required=false) String lastname,
@RequestParam(value="fullname", required=false) String fullname,
@RequestParam(value="joiningDate", required=false) String joiningDate,
@RequestParam(value="externalId", required=false) String externalId) {
LocalDate joiningLocalDate = parseStringToLocalDate(joiningDate, "joiningDate");
EnrollClientCommand command = new EnrollClientCommand(firstname, lastname, fullname, officeId, joiningLocalDate);
return this.commonRestOperations.enrollClient(command);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/client/{clientId}", method = RequestMethod.GET)
public @ResponseBody ClientDataWithAccountsData viewClientAccountData(@PathVariable("clientId") Long clientId) {
return this.commonRestOperations.retrieveClientAccount(clientId);
}
@RequestMapping(consumes="application/json", produces="application/json", value = "/portfolio/loan/{loanId}", method = RequestMethod.GET)
public @ResponseBody LoanAccountData viewLoanAccountData(@PathVariable("loanId") Long loanId) {
return this.commonRestOperations.retrieveLoanAccount(loanId);
}
@RequestMapping(value = "/portfolio/client/{clientId}", method = RequestMethod.GET)
public String viewClientAccount(final Model model, @PathVariable("clientId") final Long clientId) {
ClientData clientData = this.commonRestOperations.retrieveClientDetails(clientId);
model.addAttribute("clientId", clientId);
model.addAttribute("clientDisplayName", clientData.getDisplayName());
return "client/viewClientAccount";
}
}
@@ -1,30 +0,0 @@
package org.mifosng.ui.client;
public class DropdownOption {
private String label;
private String value;
public static DropdownOption of(String label, String value) {
DropdownOption option = new DropdownOption();
option.setLabel(label);
option.setValue(value);
return option;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@@ -1,18 +0,0 @@
package org.mifosng.ui.client;
import org.joda.time.DateTime;
import org.springframework.format.annotation.DateTimeFormat;
public class PayoffFormBean {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private DateTime payoffDate = new DateTime();
public DateTime getPayoffDate() {
return this.payoffDate;
}
public void setPayoffDate(final DateTime payoffDate) {
this.payoffDate = payoffDate;
}
}
@@ -1,43 +0,0 @@
package org.mifosng.ui.home;
import org.mifosng.ui.reporting.ReportingRestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.servlet.ModelAndView;
@Controller
public class HomeController {
private final ReportingRestOperations reportingRestOperations;
@Autowired
public HomeController(final ReportingRestOperations reportingRestOperations) {
this.reportingRestOperations = reportingRestOperations;
}
@RequestMapping(value="/forceOAuth", method = RequestMethod.GET)
public String forceOAuth() {
this.reportingRestOperations.hackToForceAuthentication();
return "redirect:/home";
}
@RequestMapping(value="/", method = RequestMethod.GET)
public String redirectToIndexPage() {
return "redirect:/home";
}
@RequestMapping(value = "/home", method = RequestMethod.GET)
public ModelAndView showIndividualClientsOnHomePage() {
return new ModelAndView("/home");
}
@RequestMapping(value="/switchToClient", method = RequestMethod.POST)
public String switchToClientView(@RequestParam("client") final Long client) {
return "redirect:/portfolio/client/" + client;
}
}
@@ -12,8 +12,6 @@
<jsp:include page="../common-head.jsp" />
<c:url value="/" var="rootContext" />
<c:url value="/portfolio/client/${clientId}" var="clientUrl" />
<c:url value="/portfolio/client/${clientId}/note/all" var="allClientNotesUrl" />
<style>
div.notecontainer {wdith:300px;min-width:300px;max-width:400px;}
span.noteusername {font-weight:bold;font-style: italic;}
@@ -314,16 +312,17 @@ $(document).ready(function() {
}).dialog('open');
}
function popupDialogWithFormView(url, titleCode, templateSelector, width, height, tabIndex, minOffset, defaultOffset, maxOffset) {
// function popupDialogWithFormView(getUrl, postUrl, submitType, titleCode, templateSelector, width, height, tabIndex, minOffset, defaultOffset, maxOffset) {
function popupDialogWithFormView(getUrl, postUrl, submitType, titleCode, templateSelector, width, height, saveSuccessFunction) {
var dialogDiv = $("<div id='dialog-form'></div>");
var jqxhr = $.ajax({
url: url,
url: getUrl,
type: 'GET',
contentType: 'application/json',
dataType: 'json',
cache: false,
success: function(data, textStatus, jqXHR) {
console.log(data);
var formHtml = $(templateSelector).render(data);
dialogDiv.append(formHtml);
@@ -333,25 +332,29 @@ $(document).ready(function() {
var buttonsOpts = {};
buttonsOpts[saveButton] = function() {
$('.multiSelectedItems option').each(function(i) {
$('#notSelectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
var form_data = $('#entityform').serialize();
$('#selectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
var newFormData = JSON.stringify($('#entityform').serializeObject());
console.log(newFormData);
var jqxhr = $.ajax({
url: url,
type: 'POST',
data: form_data,
success: function(data, textStatus, jqXHR) {
dialogDiv.dialog("close");
$newtabs.tabs('load', tabIndex);
url: postUrl,
type: submitType,
contentType: 'application/json',
dataType: 'json',
data: newFormData,
success: saveSuccessFunction,
error: function(jqXHR, textStatus, errorThrown) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
}
});
jqxhr.error(function(jqXHR, textStatus, errorThrown) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
});
};
buttonsOpts[cancelButton] = function() {$(this).dialog( "close" );};
@@ -367,20 +370,19 @@ $(document).ready(function() {
$(this).remove();
},
open: function (event, ui) {
$('.multiadd').click(function() {
return !$('.multiNotSelectedItems option:selected').remove().appendTo('#selectedItems');
$('#add').click(function() {
return !$('#notSelectedItems option:selected').remove().appendTo('#selectedItems');
});
$('.multiremove').click(function() {
return !$('.multiSelectedItems option:selected').remove().appendTo('#notSelectedItems');
$('#remove').click(function() {
return !$('#selectedItems option:selected').remove().appendTo('#notSelectedItems');
});
$('.datepickerfield').datepicker({constrainInput: true, minDate: minOffset, defaultDate: defaultOffset, maxDate: maxOffset, dateFormat: 'dd MM yy'});
$('.datepickerfield').datepicker({constrainInput: true, maxDate: 0, dateFormat: 'dd MM yy'});
$("#entityform textarea").first().focus();
$('#entityform input').first().focus();
calculateAnnualPercentageRate();
}
}).dialog('open');
}
@@ -389,7 +391,7 @@ $(document).ready(function() {
jqxhr.error(function(jqXHR, textStatus, errorThrown) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
});
}
}
var tab_counter = 1;
var $newtabs = $("#newtabs").tabs({
@@ -410,6 +412,9 @@ $(document).ready(function() {
$(anchor.hash).html("error occured while ajax loading.");
},
success: function(data, status, xhr) {
console.log(data);
var currentTabIndex = $newtabs.tabs('option', 'selected');
var currentTabAnchor = $newtabs.data('tabs').anchors[currentTabIndex];
//$(currentTabAnchor).data('cache.tabs', true)
@@ -421,12 +426,20 @@ $(document).ready(function() {
var maxOffset = 0; // today
if (currentTabIndex < 1) {
console.log("success: client tab.");
var tableHtml = $("#clientDataTabTemplate").render(data);
$("#clienttab").html(tableHtml);
$("#clienttabname").html(data.displayName);
// retrieve accounts summary info
refreshLoanSummaryInfo();
// retrieve additional info
var extraDataParams = {
url: '${rootContext}',
datasetType: "portfolio_client",
datasetPKValue: data.clientInfo.id,
datasetPKValue: data.id,
datasetTypeDiv: "clientadditionaldata",
headingPrefix: "",
headingClass: "",
@@ -435,6 +448,7 @@ $(document).ready(function() {
};
jQuery.stretchyData.displayAllExtraData(extraDataParams);
// bind click listeners to buttons.
$('.casflowbtn').button().click(function(e) {
var linkId = this.id;
var clientId = linkId.replace("cashflowbtn", "");
@@ -456,27 +470,23 @@ $(document).ready(function() {
$('.addnotebtn').button().click(function(e) {
var linkId = this.id;
var clientId = linkId.replace("addnotebtn", "");
var url = '${rootContext}portfolio/client/' + clientId + '/note/new';
var getAndPostUrl = 'http://localhost:8080/mifosng-provider/api/v1/clients/' + clientId + '/notes';
var templateSelector = "#noteFormTemplate";
var width = 600;
var height = 400;
var defaultOffset = data.clientInfo.maxJoinedOnOffsetFromToday;
popupDialogWithFormView(url, "dialog.title.add.note", templateSelector, width, height, currentTabIndex, defaultOffset, defaultOffset, maxOffset);
var saveSuccessFunction = function(data, textStatus, jqXHR) {
$("#dialog-form").dialog("close");
refreshNoteWidget();
}
popupDialogWithFormView(getAndPostUrl, getAndPostUrl, 'POST', "dialog.title.add.note", templateSelector, width, height, saveSuccessFunction);
e.preventDefault();
});
$('button.addnotebtn span').text(jQuery.i18n.prop('dialog.button.add.note'));
$("a.openloanaccount").click( function(e) {
var tab_title = $(this).attr('title');
var tab_href = this.href;
$newtabs.tabs( "add", tab_href, tab_title );
tab_counter++;
e.preventDefault();
});
refreshNoteWidget();
refreshNoteWidget();
} else {
@@ -654,22 +664,52 @@ $(document).ready(function() {
}
});
// function to retrieve and display loan summary information in it placeholder
function refreshLoanSummaryInfo() {
var jqxhr = $.ajax({
url: 'http://localhost:8080/mifosng-provider/api/v1/clients/${clientId}/loans',
type: 'GET',
contentType: 'application/json',
dataType: 'json',
cache: false,
success: function(data, textStatus, jqXHR) {
console.log(data);
var tableHtml = $("#clientAccountSummariesTemplate").render(data);
$("#clientaccountssummary").html(tableHtml);
$("a.openloanaccount").click( function(e) {
var tab_title = $(this).attr('title');
var tab_href = this.href;
$newtabs.tabs( "add", tab_href, tab_title );
e.preventDefault();
});
}
});
jqxhr.error(function(jqXHR, textStatus, errorThrown) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
});
}
function refreshNoteWidget() {
var noteArray = new Array();
var arrayIndex = 0;
var jqxhr = $.ajax({
url: '${allClientNotesUrl}',
url: 'http://localhost:8080/mifosng-provider/api/v1/clients/${clientId}/notes',
type: 'GET',
contentType: 'application/json',
dataType: 'json',
cache: false,
success: function(data, textStatus, jqXHR) {
success: function(data, textStatus, jqXHR) {
console.log(data);
var noteParent = new Object();
noteParent.title = jQuery.i18n.prop('widget.notes.heading');
noteParent.items = data;
noteParent.items = data.notes;
var tableHtml = $("#noteListViewTemplate").render(noteParent);
$("#clienttabrightpane").html(tableHtml);
@@ -677,14 +717,17 @@ $(document).ready(function() {
$('.editclientnote').click(function(e) {
var linkId = this.id;
var noteId = linkId.replace("editclientnotelink", "");
var url = '${rootContext}portfolio/client/${clientId}/note/' + noteId;
var getAndPutUrl = 'http://localhost:8080/mifosng-provider/api/v1/clients/${clientId}/notes/' + noteId;
var templateSelector = "#noteFormTemplate";
var width = 600;
var height = 400;
var defaultOffset = -400; // would be nice to return the max allowed offset for date for client note
var maxOffset = 0;
var currentTabIndex = 0;
popupDialogWithFormView(url, "dialog.title.edit.note", templateSelector, width, height, currentTabIndex, defaultOffset, defaultOffset, maxOffset);
var saveSuccessFunction = function(data, textStatus, jqXHR) {
$("#dialog-form").dialog("close");
refreshNoteWidget();
}
popupDialogWithFormView(getAndPutUrl, getAndPutUrl, 'PUT', "dialog.title.edit.note", templateSelector, width, height, saveSuccessFunction);
e.preventDefault();
});
}
@@ -730,7 +773,7 @@ $(document).ready(function() {
<div id="newtabs">
<ul>
<li><a href="http://localhost:8080/mifosng-provider/api/v1/clients/${clientId}" title="clienttab" class="topleveltab">${clientDisplayName}</a></li>
<li><a href="http://localhost:8080/mifosng-provider/api/v1/clients/${clientId}" title="clienttab" class="topleveltab"><span id="clienttabname">Loading...</span></a></li>
</ul>
<div id="clienttab">
</div>
@@ -13,96 +13,72 @@
<script>
$(document).ready(function() {
// these helpers are registered for the jsViews and jsRender functionality to fix bug with display zero!
$.views.registerHelpers({
money: function(monetaryObj) {
Globalize.culture().numberFormat.currency.symbol = monetaryObj.displaySymbol;
var digits = monetaryObj.currencyDigitsAfterDecimal.toFixed(0);
return Globalize.format(monetaryObj.amount, "n" + digits);
},
moneyWithCurrency: function(monetaryObj) {
Globalize.culture().numberFormat.currency.symbol = monetaryObj.displaySymbol;
var digits = monetaryObj.currencyDigitsAfterDecimal.toFixed(0);
return Globalize.format(monetaryObj.amount, "c" + digits);
},
decimal: function(number, digits) {
try {
return number.toFixed(digits);
} catch(e) {
return number +"(NaN)";
}
},
number: function(number) {
try {
return number.toFixed(0);
} catch(e) {
return number +"(NaN)";
}
},
numberGreaterThanZero: function(number) {
money: function(monetaryObj) {
Globalize.culture().numberFormat.currency.symbol = monetaryObj.displaySymbol;
var digits = monetaryObj.currencyDigitsAfterDecimal.toFixed(0);
return Globalize.format(monetaryObj.amount, "n" + digits);
},
moneyWithCurrency: function(monetaryObj) {
Globalize.culture().numberFormat.currency.symbol = monetaryObj.displaySymbol;
var digits = monetaryObj.currencyDigitsAfterDecimal.toFixed(0);
return Globalize.format(monetaryObj.amount, "c" + digits);
},
decimal: function(number, digits) {
try {
var num = number.toFixed(0);
return num > 0;
return Globalize.format(number, "n" + digits);
} catch(e) {
return false;
return number +"(NaN)";
}
},
json: function(obj) {
number: function(number) {
try {
return JSON.stringify(obj);
return Globalize.format(number, "n0");
} catch(e) {
return "" + e;
return number +"(NaN)";
}
},
globalDate: function(dateParts) {
try {
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) {
try {
var d = new Date(dateParts);
return Globalize.format(d,"F");
} catch(e) {
return "??";
}
}
},
numberGreaterThanZero: function(number) {
try {
var num = number.toFixed(0);
return num > 0;
} catch(e) {
return false;
}
},
globalDate: function(localDateAsISOString) {
try {
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(dateInMillis) {
try {
var d = new Date(dateInMillis);
return Globalize.format(d,"F");
} catch(e) {
return "??";
}
}
});
function handleXhrError(jqXHR, textStatus, errorThrown, templateSelector, placeholderDiv) {
if (jqXHR.status === 0) {
alert('No connection. Verify application is running.');
} else if (jqXHR.status == 401) {
alert('Unauthorized. [401]');
} else if (jqXHR.status == 404) {
alert('Requested page not found. [404]');
} else if (jqXHR.status == 405) {
alert('HTTP verb not supported [405]: ' + errorThrown);
} else if (jqXHR.status == 500) {
alert('Internal Server Error [500].');
} else if (errorThrown === 'parsererror') {
alert('Requested JSON parse failed.');
} else if (errorThrown === 'timeout') {
alert('Time out error.');
} else if (errorThrown === 'abort') {
alert('Ajax request aborted.');
} else {
function removeErrors(placeholderDiv) {
// remove error class from all input fields
var $inputs = $('#entityform :input');
@@ -111,102 +87,196 @@ function handleXhrError(jqXHR, textStatus, errorThrown, templateSelector, placeh
});
$(placeholderDiv).html("");
var jsonErrors = JSON.parse(jqXHR.responseText);
var errorArray = new Array();
var arrayIndex = 0;
$.each(jsonErrors, function() {
var fieldId = '#' + this.field;
$(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.value = this.value;
errorArray[arrayIndex] = errorObj;
arrayIndex++
});
var templateArray = new Array();
var templateErrorObj = new Object();
templateErrorObj.title = "You have the following errors:";
templateErrorObj.errors = errorArray;
templateArray[0] = templateErrorObj;
var formErrorsHtml = $(templateSelector).render(templateArray);
$(placeholderDiv).append(formErrorsHtml);
}
}
function popupDialogWithFormView(url, titleCode, templateSelector, width, height, successFunction) {
var dialogDiv = $("<div id='dialog-form'></div>");
var jqxhr = $.ajax({
url: url,
type: 'GET',
contentType: 'application/json',
dataType: 'json',
cache: false,
success: function(data, textStatus, jqXHR) {
function handleXhrError(jqXHR, textStatus, errorThrown, templateSelector, placeholderDiv) {
if (jqXHR.status === 0) {
alert('No connection. Verify application is running.');
} else if (jqXHR.status == 401) {
alert('Unauthorized. [401]');
} else if (jqXHR.status == 404) {
alert('Requested page not found. [404]');
} else if (jqXHR.status == 405) {
alert('HTTP verb not supported [405]: ' + errorThrown);
} else if (jqXHR.status == 500) {
alert('Internal Server Error [500].');
} else if (errorThrown === 'parsererror') {
alert('Requested JSON parse failed.');
} else if (errorThrown === 'timeout') {
alert('Time out error.');
} else if (errorThrown === 'abort') {
alert('Ajax request aborted.');
} else {
var formHtml = $(templateSelector).render(data);
removeErrors(placeholderDiv);
dialogDiv.append(formHtml);
var saveButton = jQuery.i18n.prop('dialog.button.save');
var cancelButton = jQuery.i18n.prop('dialog.button.cancel');
var buttonsOpts = {};
buttonsOpts[saveButton] = function() {
$('.multiSelectedItems option').each(function(i) {
var jsonErrors = JSON.parse(jqXHR.responseText);
console.log(jsonErrors);
var valErrors = jsonErrors.errors;
console.log(valErrors);
var errorArray = new Array();
var arrayIndex = 0;
$.each(valErrors, function() {
var fieldId = '#' + this.parameterName;
$(fieldId).addClass("ui-state-error");
var errorObj = new Object();
errorObj.field = this.parameterName;
errorObj.code = this.userMessageGlobalisationCode;
var argArray = new Array();
var argArrayIndex = 0;
$.each(this.args, function() {
argArray[argArrayIndex] = this.value;
argArrayIndex++;
});
console.log(argArray);
// 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;
arrayIndex++
});
var templateArray = new Array();
var templateErrorObj = new Object();
templateErrorObj.title = jQuery.i18n.prop('error.msg.header');
templateErrorObj.errors = errorArray;
templateArray[0] = templateErrorObj;
var formErrorsHtml = $(templateSelector).render(templateArray);
$(placeholderDiv).append(formErrorsHtml);
}
}
function showNotAvailableDialog(titleCode) {
var dialogDiv = $("<div id='notavailable-dialog-form'></div>");
dialogDiv.append("<p>" + jQuery.i18n.prop('dialog.messages.functionality.not.available') + "</p>");
var okButton = jQuery.i18n.prop('dialog.button.ok');
var buttonsOpts = {};
buttonsOpts[okButton] = function() {$(this).dialog("close");};
dialogDiv.dialog({
title: jQuery.i18n.prop(titleCode),
width: 300,
height: 200,
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();
}
}).dialog('open');
}
$.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 popupDialogWithFormView(getUrl, postUrl, submitType, titleCode, templateSelector, width, height, saveSuccessFunction) {
var dialogDiv = $("<div id='dialog-form'></div>");
var jqxhr = $.ajax({
url: getUrl,
type: 'GET',
contentType: 'application/json',
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() {
$('#notSelectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
$('#selectedItems option').each(function(i) {
$(this).attr("selected", "selected");
});
var form_data = $('#entityform').serialize();
var jqxhr = $.ajax({
url: url,
type: 'POST',
data: form_data,
success: successFunction,
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) {
$('.multiadd').click(function() {
return !$('.multiNotSelectedItems option:selected').remove().appendTo('#selectedItems');
});
$('.multiremove').click(function() {
return !$('.multiSelectedItems option:selected').remove().appendTo('#notSelectedItems');
});
$('.datepickerfield').datepicker({constrainInput: true, maxDate: 0, dateFormat: 'dd MM yy'});
}
}).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) {
handleXhrError(jqXHR, textStatus, errorThrown, "#formErrorsTemplate", "#formerrors");
});
}
$("#tabs").tabs({
"ajaxOptions": {
@@ -234,13 +304,13 @@ function popupDialogWithFormView(url, titleCode, templateSelector, width, height
}
$("#addclient").button().click(function(e) {
var url = '${rootContext}portfolio/client/new';
var getUrl = 'http://localhost:8080/mifosng-provider/api/v1/clients/template';
var postUrl = 'http://localhost:8080/mifosng-provider/api/v1/clients';
var templateSelector = "#clientFormTemplate";
var width = 600;
var height = 350;
popupDialogWithFormView(url, 'dialog.title.add.client', templateSelector, width, height, addClientSuccessFunction);
popupDialogWithFormView(getUrl, postUrl, 'POST', 'dialog.title.add.client', templateSelector, width, height, addClientSuccessFunction);
e.preventDefault();
});
@@ -253,9 +323,6 @@ function popupDialogWithFormView(url, titleCode, templateSelector, width, height
<div id="container">
<jsp:include page="top-navigation.jsp" />
<c:url value="/portfolio/client/all" var="allClientsUrl" />
<c:url value="/portfolio/client/add" var="addClientUrl" />
<div style="float:none; clear:both;">
<div id="spacer" style="line-height: 15px;">&nbsp;</div>
<div id="content">
@@ -264,7 +331,7 @@ function popupDialogWithFormView(url, titleCode, templateSelector, width, height
</sec:authorize>
<div id="tabs">
<ul>
<li><a href="${allClientsUrl}" title="searchtab"><spring:message code="tab.search"/></a></li>
<li><a href="http://localhost:8080/mifosng-provider/api/v1/clients" title="searchtab"><spring:message code="tab.search"/></a></li>
</ul>
<div id="searchtab"></div>
</div>
@@ -325,7 +325,7 @@
<form id="entityform">
<div id="formerrors"></div>
<label for="office"><spring:message code="label.office"/></label>
<select id="office" name="office" title="">
<select id="officeId" name="officeId" title="">
<option value=""><spring:message code="option.office.first.choice"/></option>
{{#each allowedOffices}}
{{#if $ctx.number($parent.parent.data.officeId)===$ctx.number(id)}}
@@ -355,43 +355,13 @@
</tr>
</table>
<label for="joiningDate"><spring:message code="label.joiningdate"/></label>
<input id="joiningDate" name="joiningDate" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(joinedDate)}}"/>
<input type="hidden" id="dateFormat" name="dateFormat" value="dd MMMM yyyy" />
<label for="joiningDateFormatted"><spring:message code="label.joiningdate"/></label>
<input id="joiningDateFormatted" name="joiningDateFormatted" title="" type="text" class="datepickerfield" value="{{=$ctx.globalDate(joinedDate)}}"/>
</form>
</script>
<script id="clientDataTabTemplate" type="text/x-jquery-tmpl">
<c:url value="/" var="rootContext" />
<span id="toolbar" class="ui-widget-header ui-corner-all">
<!--
<button id="cashflowbtn{{=clientInfo.id}}" class="casflowbtn">New cashflow analysis</button>
-->
<sec:authorize access="hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE', 'CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE')">
<button id="newloanbtn{{=clientInfo.id}}" class="newloanbtn">New loan application</button>
</sec:authorize>
<button id="addnotebtn{{=clientInfo.id}}" class="addnotebtn">Add note</button>
</span>
<hr/>
<div id="clienttabcontent">
<div id="clienttableftpane" style="float:left; max-width: 700px; width: 700px;">
<div class="row">
<span class="longrowlabel"><spring:message code="label.client.account.name"/></span>
<span class="rowvalue">{{=clientInfo.displayName}}</span>
</div>
<div class="row">
<span class="longrowlabel"><spring:message code="label.client.account.branch"/></span>
<span class="rowvalue">{{=clientInfo.officeName}}</span>
</div>
<div class="row">
<span class="longrowlabel"><spring:message code="label.client.account.joinedon"/></span>
<span class="rowvalue">{{=$ctx.globalDate(clientInfo.joinedDate)}}</span>
</div>
<br/>
<p><b><spring:message code="heading.client.account.account.overview"/></b></p>
<script id="clientAccountSummariesTemplate" type="text/x-jquery-tmpl">
{{#if $ctx.numberGreaterThanZero(anyLoanCount)}}
{{#if $ctx.numberGreaterThanZero(pendingApprovalLoanCount)}}
@@ -399,7 +369,7 @@
<span class="longrowlabel"><spring:message code="label.client.account.pending.approval.loans"/> ({{=$ctx.number(pendingApprovalLoanCount)}}):</span>
<span class="rowvalue">
{{#each pendingApprovalLoans}}
<span class="loanaccount"><a href="${rootContext}portfolio/loan/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
<span class="loanaccount"><a href="http://localhost:8080/mifosng-provider/api/v1/loans/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
{{/each}}
</span>
</div>
@@ -410,7 +380,7 @@
<span class="longrowlabel"><spring:message code="label.client.account.pending.disbursal.loans"/> ({{=$ctx.number(awaitingDisbursalLoanCount)}}):</span>
<span class="rowvalue">
{{#each awaitingDisbursalLoans}}
<span class="loanaccount"><a href="${rootContext}portfolio/loan/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
<span class="loanaccount"><a href="http://localhost:8080/mifosng-provider/api/v1/loans/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
{{/each}}
</span>
</div>
@@ -421,7 +391,7 @@
<span class="longrowlabel"><spring:message code="label.client.account.active.loans"/> ({{=$ctx.number(activeLoanCount)}}):</span>
<span class="rowvalue">
{{#each openLoans}}
<span class="loanaccount"><a href="${rootContext}portfolio/loan/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
<span class="loanaccount"><a href="http://localhost:8080/mifosng-provider/api/v1/loans/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
{{/each}}
</span>
</div>
@@ -432,7 +402,7 @@
<div class="longrowlabel"><spring:message code="label.client.account.closed.loans"/> ({{=$ctx.number(closedLoanCount)}}):</div>
<div class="rowvalue">
{{#each closedLoans}}
<span class="loanaccount"><a href="${rootContext}portfolio/loan/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
<span class="loanaccount"><a href="http://localhost:8080/mifosng-provider/api/v1/loans/{{=id}}" id="loan{{=id}}" class="openloanaccount" title="{{=loanProductName}}: &#35;{{=id}}">{{=loanProductName}}: &#35;{{=id}}</a></span>
{{/each}}
</div>
</div>
@@ -443,14 +413,45 @@
<spring:message code="label.client.account.no.loans.exist"/>
</div>
{{/if}}
</script>
<div id="clientadditionaldata"></div>
</div>
<!-- end of left pane content -->
<script id="clientDataTabTemplate" type="text/x-jquery-tmpl">
<span id="toolbar" class="ui-widget-header ui-corner-all">
<!--
<button id="cashflowbtn{{=id}}" class="casflowbtn">New cashflow analysis</button>
-->
<sec:authorize access="hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_SUBMIT_NEW_LOAN_APPLICATION_ROLE', 'CAN_SUBMIT_HISTORIC_LOAN_APPLICATION_ROLE')">
<button id="newloanbtn{{=id}}" class="newloanbtn">New loan application</button>
</sec:authorize>
<div id="clienttabrightpane" style="float:right;">
</div>
<button id="addnotebtn{{=id}}" class="addnotebtn">Add note</button>
</span>
<hr/>
<div id="clienttabcontent">
<div id="clienttableftpane" style="float:left; max-width: 700px; width: 700px;">
<div class="row">
<span class="longrowlabel"><spring:message code="label.client.account.name"/></span>
<span class="rowvalue">{{=displayName}}</span>
</div>
<div class="row">
<span class="longrowlabel"><spring:message code="label.client.account.branch"/></span>
<span class="rowvalue">{{=officeName}}</span>
</div>
<div class="row">
<span class="longrowlabel"><spring:message code="label.client.account.joinedon"/></span>
<span class="rowvalue">{{=$ctx.globalDate(joinedDate)}}</span>
</div>
<p><b><spring:message code="heading.client.account.account.overview"/></b></p>
<div id="clientaccountssummary"></div>
<div id="clientadditionaldata"></div>
</div>
<!-- end of left pane content -->
<!-- placeholder for notes content -->
<div id="clienttabrightpane" style="float:right;"></div>
</div>
<div style="clear: both;"></div>
</script>