MIFOSX-226: add data scoping on loans for retrieveAll and retrieveOne

This commit is contained in:
Keith Woodlock
2013-05-09 13:50:13 +01:00
parent d097ef966c
commit f206f20eca
4 changed files with 272 additions and 11 deletions
+243 -2
View File
@@ -292,7 +292,7 @@
<td></td>
<td>loans</td>
<td><a href="#loans_create">Submit a new Loan Application</a></td>
<td></td>
<td><a href="#loans_list">List Loans/Loan Applications</a></td>
<td></td>
<td></td>
</tr>
@@ -4413,6 +4413,247 @@ GET https://DomainName/api/v1/loans/{loanId}
</div>
</div>
<a id="loans_list" name="loans_list" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>List Loans</h4>
<p>The <i>list</i> capability of loans can support <b>pagination</b> and <b>sorting</b>.</p>
<h5>Optional Arguments</h5>
<dl class="argument-list">
<dt>offset</dt>
<dd>
Integer <span>optional</span>, defaults to 0
</dd>
<dd>Indicates from what result to start from.</dd>
<dt>limit</dt>
<dd>
Integer <span>optional</span>, defaults to 200
</dd>
<dd>Restricts the size of results returned. To override the default and return all entries you must explicitly pass a non-positive integer value for limit e.g. limit=0, or limit=-1</dd>
<dt>orderBy</dt>
<dd>
String <span>optional</span>, one of <span>displayName, accountNo, officeId, officeName</span>
</dd>
<dd>Orders the results by the field indicated.</dd>
<dt>sortBy</dt>
<dd>
String <span>optional</span>, one of <span>ASC, DESC</span>
</dd>
<dd>Indicates what way to order results if <i>orderBy</i> is used.</dd>
<dt>officeId</dt>
<dd>
Integer <span>optional</span>
</dd>
<dd>Provides ability to restrict list of loans returned based on the office there associated with.</dd>
<dt>underHierarchy</dt>
<dd>
String <span>optional</span>
</dd>
<dd>Use the office hierarchy string to return all loans under a given hierarchy.</dd>
<dt>accountNo</dt>
<dd>
String <span>optional</span>
</dd>
<dd>Use account no. of loans to restrict results.</dd>
<dt>externalId</dt>
<dd>
String <span>optional</span>
</dd>
<dd>Use externalId of loan to restrict results.</dd>
<dt>sqlSearch</dt>
<dd>
String <span>optional</span>
</dd>
<dd>Use an sql fragment valid for the underlying loan schema to filter results. e.g. display_name like %K%</dd>
</dl>
<p>Example Requests:</p>
<div class=apiClick>loans</div>
<br>
<div class=apiClick>loans?fields=accountNo</div>
<br>
<div class=apiClick>loans?offset=10&limit=50</div>
<br>
<div class=apiClick>loans?orderBy=accountNo,sortOrder=DESC</div>
</div>
<div class="method-example">
<code class="method-declaration">
GET https://DomainName/mifosng-provider/api/v1/loans
</code>
<code class="method-response">
{
"totalFilteredRecords": 1,
"pageItems": [
{
"id": 1,
"accountNo": "000000001",
"status": {
"id": 300,
"code": "loanStatusType.active",
"value": "Active",
"pendingApproval": false,
"waitingForDisbursal": false,
"active": true,
"closedObligationsMet": false,
"closedWrittenOff": false,
"closedRescheduled": false,
"closed": false,
"overpaid": false
},
"clientId": 1,
"clientName": "Change To IndividualName",
"clientOfficeId": 1,
"loanProductId": 1,
"loanProductName": "AgriCredit",
"loanProductDescription": "test",
"loanType": {
"id": 1,
"code": "loanType.individual",
"value": "Individual"
},
"currency": {
"code": "USD",
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"principal": 10000,
"termFrequency": 12,
"termPeriodFrequencyType": {
"id": 2,
"code": "termFrequency.periodFrequencyType.months",
"value": "Months"
},
"numberOfRepayments": 12,
"repaymentEvery": 1,
"repaymentFrequencyType": {
"id": 2,
"code": "repaymentFrequency.periodFrequencyType.months",
"value": "Months"
},
"interestRatePerPeriod": 2,
"interestRateFrequencyType": {
"id": 2,
"code": "interestRateFrequency.periodFrequencyType.months",
"value": "Per month"
},
"annualInterestRate": 24,
"amortizationType": {
"id": 1,
"code": "amortizationType.equal.installments",
"value": "Equal installments"
},
"interestType": {
"id": 0,
"code": "interestType.declining.balance",
"value": "Declining Balance"
},
"interestCalculationPeriodType": {
"id": 1,
"code": "interestCalculationPeriodType.same.as.repayment.period",
"value": "Same as repayment period"
},
"transactionProcessingStrategyId": 2,
"timeline": {
"submittedOnDate": [
2012,
6,
1
],
"submittedByUsername": "mifos",
"submittedByFirstname": "App",
"submittedByLastname": "Administrator",
"approvedOnDate": [
2012,
6,
1
],
"approvedByUsername": "mifos",
"approvedByFirstname": "App",
"approvedByLastname": "Administrator",
"expectedDisbursementDate": [
2012,
6,
1
],
"actualDisbursementDate": [
2012,
6,
1
],
"disbursedByUsername": "mifos",
"disbursedByFirstname": "App",
"disbursedByLastname": "Administrator",
"expectedMaturityDate": [
2013,
6,
1
]
},
"summary": {
"currency": {
"code": "USD",
"name": "US Dollar",
"decimalPlaces": 2,
"displaySymbol": "$",
"nameCode": "currency.USD",
"displayLabel": "US Dollar ($)"
},
"principalDisbursed": 10000,
"principalPaid": 3073.07,
"principalWrittenOff": 0,
"principalOutstanding": 6926.93,
"principalOverdue": 5999.92,
"interestCharged": 1347.15,
"interestPaid": 709.33,
"interestWaived": 0,
"interestWrittenOff": 0,
"interestOutstanding": 637.82,
"interestOverdue": 619.28,
"feeChargesCharged": 0,
"feeChargesDueAtDisbursementCharged": 0,
"feeChargesPaid": 0,
"feeChargesWaived": 0,
"feeChargesWrittenOff": 0,
"feeChargesOutstanding": 0,
"feeChargesOverdue": 0,
"penaltyChargesCharged": 0,
"penaltyChargesPaid": 0,
"penaltyChargesWaived": 0,
"penaltyChargesWrittenOff": 0,
"penaltyChargesOutstanding": 0,
"penaltyChargesOverdue": 0,
"totalExpectedRepayment": 11347.15,
"totalRepayment": 3782.4,
"totalExpectedCostOfLoan": 1347.15,
"totalCostOfLoan": 709.33,
"totalWaived": 0,
"totalWrittenOff": 0,
"totalOutstanding": 7564.75,
"totalOverdue": 6619.2,
"overdueSinceDate": [
2012,
11,
1
]
},
"feeChargesAtDisbursementCharged": 0
}
]
}
</code>
</div>
</div>
<a id="loans_calculate" name="loans_calculate" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
@@ -13611,7 +13852,7 @@ GET https://DomainName/api/v1/search?query=000000111
<td></td>
<td>loans</td>
<td><a href="#loans_create">Submit a new Loan Application</a></td>
<td></td>
<td><a href="#loans_list">List Loans/Loan Applications</a></td>
<td></td>
<td></td>
</tr>
@@ -238,7 +238,7 @@ public class LoansApiResource {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId);
Collection<LoanTransactionData> loanRepayments = null;
LoanScheduleData repaymentSchedule = null;
@@ -486,7 +486,7 @@ public class LoansApiResource {
// normal
// template method to avoid 'assign' on api. and avoid needing to query
// for officeId and loanOfficerId in current way!
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId);
final Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails
.officeId());
@@ -17,7 +17,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDat
public interface LoanReadPlatformService {
LoanAccountData retrieveLoanAccountDetails(Long loanId);
LoanAccountData retrieveOne(Long loanId);
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData);
@@ -66,6 +66,7 @@ import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrateg
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
@@ -120,16 +121,22 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
}
@Override
public LoanAccountData retrieveLoanAccountDetails(final Long loanId) {
public LoanAccountData retrieveOne(final Long loanId) {
try {
context.authenticatedUser();
final AppUser currentUser = context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%";
LoanMapper rm = new LoanMapper();
String sql = "select " + rm.loanSchema() + " where l.id = ?";
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("select ");
sqlBuilder.append(rm.loanSchema());
sqlBuilder.append(" join m_office o on o.id = c.office_id");
sqlBuilder.append(" where l.id=? and o.hierarchy like ?");
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanId });
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), rm, new Object[] { loanId, hierarchySearchString });
} catch (EmptyResultDataAccessException e) {
throw new LoanNotFoundException(loanId);
}
@@ -176,12 +183,25 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public Page<LoanAccountData> retrieveAll(final SearchParameters searchParameters) {
final AppUser currentUser = context.authenticatedUser();
final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%";
StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
sqlBuilder.append(loaanLoanMapper.loanSchema());
final Object[] objectArray = new Object[1];
int arrayPos = 0;
// TODO - for time being this will data scope list of loans returned to
// only loans that have a client associated.
// to support senario where loan has group_id only OR client_id will
// probably require a UNION query
// but that at present is an edge case
sqlBuilder.append(" join m_office o on o.id = c.office_id");
sqlBuilder.append(" where o.hierarchy like ?");
final Object[] objectArray = new Object[2];
objectArray[0] = hierarchySearchString;
int arrayPos = 1;
String sqlQueryCriteria = searchParameters.getSqlSearch();
if (StringUtils.isNotBlank(sqlQueryCriteria)) {