From c58d5412799de9cd8d303e853940abd9da2df92d Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 13 Jun 2013 00:13:02 +0100 Subject: [PATCH] MIFOSX-445: default the list of loan officer options for new loan application to pick up on loan officers in offices higher in its office hierarchy. add optional parameter to allow people to restrict this list to only the selected branch. --- api-docs/apiLive.htm | 15 ++- .../staff/api/StaffApiResource.java | 22 +---- .../service/StaffReadPlatformService.java | 12 ++- .../service/StaffReadPlatformServiceImpl.java | 96 ++++++++++++++++--- .../loanaccount/api/BulkLoansApiResource.java | 2 +- .../loanaccount/api/LoansApiResource.java | 34 ++++++- 6 files changed, 136 insertions(+), 45 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 2c7a1d70e..c30f91868 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -3901,12 +3901,14 @@ GET https://DomainName/mifosng-provider/api/v1/groups The type of transaction processing strategy to be used.
At present some MFI specific ones exist. This is an important point of change for many MFIs.
Responsible for things like payment order and how early or late repayments should be processed.
- Out of the box, four available: + Out of the box options available are:
  1. id=1, code=mifos-standard-strategy, name=Mifos style
  2. id=2, code=heavensfamily-strategy, name=Heavensfamily
  3. id=3, code=creocore-strategy, name=Creocore
  4. id=4, code=rbi-india-strategy, name=RBI (India)
  5. +
  6. id=5, code=principal-interest-penalties-fees-order-strategy, name=Principal Interest Penalties Fees Order
  7. +
  8. id=6, code=interest-principal-penalties-fees-order-strategy, name=Interest Principal Penalties Fees Order
@@ -3973,6 +3975,13 @@ GET https://DomainName/mifosng-provider/api/v1/groups
If entered, productId, productName and selectedProduct fields are returned.
+ +
loanOfficersInSelectedOfficeOnly
+
+ Boolean optional +
+
Defaults to false if not provided. If loanOfficersInSelectedOfficeOnly=true only loan officers + who are associated with the select branch are returned.

Example Requests:

loans/template?templateType=individual&clientId=1
@@ -6995,12 +7004,14 @@ Request Body: The type of transaction processing strategy to be used.
At present some MFI specific ones exist. This is an important point of change for many MFIs.
Responsible for things like payment order and how early or late repayments should be processed.
- Out of the box, four available: + Out of the box options available are:
  1. id=1, code=mifos-standard-strategy, name=Mifos style
  2. id=2, code=heavensfamily-strategy, name=Heavensfamily
  3. id=3, code=creocore-strategy, name=Creocore
  4. id=4, code=rbi-india-strategy, name=RBI (India)
  5. +
  6. id=5, code=principal-interest-penalties-fees-order-strategy, name=Principal Interest Penalties Fees Order
  7. +
  8. id=6, code=interest-principal-penalties-fees-order-strategy, name=Interest Principal Penalties Fees Order
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java index 1138c4e18..22009a5e7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/api/StaffApiResource.java @@ -22,7 +22,6 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; -import org.apache.commons.lang.StringUtils; import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; @@ -81,8 +80,7 @@ public class StaffApiResource { context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); - final String extraCriteria = getStaffCriteria(sqlSearch, officeId); - final Collection staff = this.readPlatformService.retrieveAllStaff(extraCriteria); + final Collection staff = this.readPlatformService.retrieveAllStaff(sqlSearch, officeId); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, staff, RESPONSE_DATA_PARAMETERS); @@ -131,22 +129,4 @@ public class StaffApiResource { return this.toApiJsonSerializer.serialize(result); } - - private String getStaffCriteria(final String sqlSearch, final Long officeId) { - - String extraCriteria = ""; - - if (sqlSearch != null) { - extraCriteria = " and (" + sqlSearch + ")"; - } - if (officeId != null) { - extraCriteria += " and office_id = " + officeId; - } - - if (StringUtils.isNotBlank(extraCriteria)) { - extraCriteria = extraCriteria.substring(4); - } - - return extraCriteria; - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformService.java index d9fb62873..3d5e42ef0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformService.java @@ -13,9 +13,15 @@ public interface StaffReadPlatformService { StaffData retrieveStaff(Long staffId); - Collection retrieveAllStaff(final String extraCriteria); - Collection retrieveAllStaffForDropdown(Long officeId); - Collection retrieveAllLoanOfficersByOffice(final Long officeId); + Collection retrieveAllLoanOfficersInOfficeById(final Long officeId); + + /** + * returns all loan officers in offices that are above the provided + * officeId. + */ + Collection retrieveAllLoanOfficersInOfficeAndItsParentOfficeHierarchy(Long officeId); + + Collection retrieveAllStaff(String sqlSearch, Long officeId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java index 18c3d381c..677e46cd1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/staff/service/StaffReadPlatformServiceImpl.java @@ -26,6 +26,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; private final StaffLookupMapper lookupMapper = new StaffLookupMapper(); + private final StaffInOfficeHierarchyMapper staffInOfficeHierarchyMapper = new StaffInOfficeHierarchyMapper(); @Autowired public StaffReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) { @@ -56,6 +57,43 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { } } + private static final class StaffInOfficeHierarchyMapper implements RowMapper { + + final String schema; + + public StaffInOfficeHierarchyMapper() { + + StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("s.id as id, s.office_id as officeId, o.name as officeName,"); + sqlBuilder.append("s.firstname as firstname, s.lastname as lastname,"); + sqlBuilder.append("s.display_name as displayName, s.is_loan_officer as isLoanOfficer "); + sqlBuilder.append("from m_office o "); + sqlBuilder.append("join m_office ohierarchy on o.hierarchy like concat(ohierarchy.hierarchy, '%') "); + sqlBuilder.append("join m_staff s on s.office_id = ohierarchy.id and s.is_loan_officer is true "); + sqlBuilder.append("where o.id = ? "); + + this.schema = sqlBuilder.toString(); + } + + public String schema() { + return this.schema; + } + + @Override + public StaffData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String firstname = rs.getString("firstname"); + final String lastname = rs.getString("lastname"); + final String displayName = rs.getString("displayName"); + final Long officeId = rs.getLong("officeId"); + final String officeName = rs.getString("officeName"); + final boolean isLoanOfficer = rs.getBoolean("isLoanOfficer"); + + return StaffData.instance(id, firstname, lastname, displayName, officeId, officeName, isLoanOfficer); + } + } + private static final class StaffLookupMapper implements RowMapper { private final String schemaSql; @@ -84,19 +122,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { } @Override - public Collection retrieveAllStaff(final String extraCriteria) { - - final StaffMapper rm = new StaffMapper(); - String sql = "select " + rm.schema(); - if (StringUtils.isNotBlank(extraCriteria)) { - sql += " where " + extraCriteria; - } - sql = sql + " order by s.lastname"; - return this.jdbcTemplate.query(sql, rm, new Object[] {}); - } - - @Override - public Collection retrieveAllLoanOfficersByOffice(final Long officeId) { + public Collection retrieveAllLoanOfficersInOfficeById(final Long officeId) { return retrieveAllStaff(" office_id=" + officeId + " and is_loan_officer=1"); } @@ -132,4 +158,48 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService { throw new StaffNotFoundException(staffId); } } + + @Override + public Collection retrieveAllStaff(final String sqlSearch, final Long officeId) { + final String extraCriteria = getStaffCriteria(sqlSearch, officeId); + return retrieveAllStaff(extraCriteria); + } + + private Collection retrieveAllStaff(final String extraCriteria) { + + final StaffMapper rm = new StaffMapper(); + String sql = "select " + rm.schema(); + if (StringUtils.isNotBlank(extraCriteria)) { + sql += " where " + extraCriteria; + } + sql = sql + " order by s.lastname"; + return this.jdbcTemplate.query(sql, rm, new Object[] {}); + } + + private String getStaffCriteria(final String sqlSearch, final Long officeId) { + + String extraCriteria = ""; + + if (sqlSearch != null) { + extraCriteria = " and (" + sqlSearch + ")"; + } + if (officeId != null) { + extraCriteria += " and office_id = " + officeId; + } + + if (StringUtils.isNotBlank(extraCriteria)) { + extraCriteria = extraCriteria.substring(4); + } + + return extraCriteria; + } + + @Override + public Collection retrieveAllLoanOfficersInOfficeAndItsParentOfficeHierarchy(final Long officeId) { + + String sql = "select " + this.staffInOfficeHierarchyMapper.schema(); + sql = sql + " order by s.lastname"; + + return this.jdbcTemplate.query(sql, this.staffInOfficeHierarchyMapper, new Object[] { officeId }); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/BulkLoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/BulkLoansApiResource.java index 7e9b2db4b..3350bf505 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/BulkLoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/BulkLoansApiResource.java @@ -88,7 +88,7 @@ public class BulkLoansApiResource { StaffAccountSummaryCollectionData staffAccountSummaryCollectionData = null; if (officeId != null) { - loanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId); + loanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersInOfficeById(officeId); } if (loanOfficerId != null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index 3e9437e05..19536882e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.loanaccount.api; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -12,6 +13,7 @@ import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -162,6 +164,7 @@ public class LoansApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String template(@QueryParam("clientId") final Long clientId, @QueryParam("groupId") final Long groupId, @QueryParam("productId") final Long productId, @QueryParam("templateType") final String templateType, + @DefaultValue("false") @QueryParam("loanOfficersInSelectedOfficeOnly") final boolean loanOfficersInSelectedOfficeOnly, @Context final UriInfo uriInfo) { context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions); @@ -223,7 +226,28 @@ public class LoansApiResource { throw new NotSupportedLoanTemplateTypeException(errorMsg, templateType); } - if (officeId != null) allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(officeId); + if (officeId != null) { + if (loanOfficersInSelectedOfficeOnly) { + // only bring back loan officers in selected branch/office + Collection loanOfficersInBranch = this.staffReadPlatformService + .retrieveAllLoanOfficersInOfficeById(officeId); + + if (!CollectionUtils.isEmpty(loanOfficersInBranch)) { + allowedLoanOfficers = new ArrayList(loanOfficersInBranch); + } + } else { + // by default bring back all loan officers in selected + // branch/office as well as loan officers in officer above + // this office + Collection loanOfficersInHierarchy = this.staffReadPlatformService + .retrieveAllLoanOfficersInOfficeAndItsParentOfficeHierarchy(officeId); + + if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) { + allowedLoanOfficers = new ArrayList(loanOfficersInHierarchy); + } + } + } + // add product options, allowed loan officers and calendar options // (calendar options will be null in individual loan) newLoanAccount = LoanAccountData.associationsAndTemplate(newLoanAccount, productOptions, allowedLoanOfficers, calendarOptions); @@ -336,9 +360,9 @@ public class LoansApiResource { chargeTemplate = this.loanChargeReadPlatformService.retrieveLoanChargeTemplate(); if (loanBasicDetails.officeId() != null) { - allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.officeId()); + allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersInOfficeById(loanBasicDetails.officeId()); } else if (loanBasicDetails.groupOfficeId() != null) { - allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails.groupOfficeId()); + allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersInOfficeById(loanBasicDetails.groupOfficeId()); } loanPurposeOptions = this.codeValueReadPlatformService.retrieveCodeValuesByCode("LoanPurpose"); @@ -492,8 +516,8 @@ public class LoansApiResource { // for officeId and loanOfficerId in current way! final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId); - final Collection allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails - .officeId()); + final Collection allowedLoanOfficers = this.staffReadPlatformService + .retrieveAllLoanOfficersInOfficeById(loanBasicDetails.officeId()); final Long fromLoanOfficerId = loanBasicDetails.loanOfficerId(); final BulkTransferLoanOfficerData loanReassignmentData = BulkTransferLoanOfficerData.template(fromLoanOfficerId,