MIFOSX-226: add data scoping on loans for retrieveAll and retrieveOne
This commit is contained in:
+243
-2
@@ -292,7 +292,7 @@
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td>loans</td>
|
<td>loans</td>
|
||||||
<td><a href="#loans_create">Submit a new Loan Application</a></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>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -4413,6 +4413,247 @@ GET https://DomainName/api/v1/loans/{loanId}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<a id="loans_list" name="loans_list" class="old-syle-anchor"> </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"> </a>
|
<a id="loans_calculate" name="loans_calculate" class="old-syle-anchor"> </a>
|
||||||
<div class="method-section">
|
<div class="method-section">
|
||||||
<div class="method-description">
|
<div class="method-description">
|
||||||
@@ -13611,7 +13852,7 @@ GET https://DomainName/api/v1/search?query=000000111
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td>loans</td>
|
<td>loans</td>
|
||||||
<td><a href="#loans_create">Submit a new Loan Application</a></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>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
+2
-2
@@ -238,7 +238,7 @@ public class LoansApiResource {
|
|||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
|
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
|
||||||
|
|
||||||
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveLoanAccountDetails(loanId);
|
final LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId);
|
||||||
|
|
||||||
Collection<LoanTransactionData> loanRepayments = null;
|
Collection<LoanTransactionData> loanRepayments = null;
|
||||||
LoanScheduleData repaymentSchedule = null;
|
LoanScheduleData repaymentSchedule = null;
|
||||||
@@ -486,7 +486,7 @@ public class LoansApiResource {
|
|||||||
// normal
|
// normal
|
||||||
// template method to avoid 'assign' on api. and avoid needing to query
|
// template method to avoid 'assign' on api. and avoid needing to query
|
||||||
// for officeId and loanOfficerId in current way!
|
// 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
|
final Collection<StaffData> allowedLoanOfficers = this.staffReadPlatformService.retrieveAllLoanOfficersByOffice(loanBasicDetails
|
||||||
.officeId());
|
.officeId());
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleDat
|
|||||||
|
|
||||||
public interface LoanReadPlatformService {
|
public interface LoanReadPlatformService {
|
||||||
|
|
||||||
LoanAccountData retrieveLoanAccountDetails(Long loanId);
|
LoanAccountData retrieveOne(Long loanId);
|
||||||
|
|
||||||
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData);
|
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData);
|
||||||
|
|
||||||
|
|||||||
+26
-6
@@ -66,6 +66,7 @@ import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrateg
|
|||||||
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
||||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||||
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||||
|
import org.mifosplatform.useradministration.domain.AppUser;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.dao.DataAccessException;
|
import org.springframework.dao.DataAccessException;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
@@ -120,16 +121,22 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanAccountData retrieveLoanAccountDetails(final Long loanId) {
|
public LoanAccountData retrieveOne(final Long loanId) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.authenticatedUser();
|
final AppUser currentUser = context.authenticatedUser();
|
||||||
|
final String hierarchy = currentUser.getOffice().getHierarchy();
|
||||||
|
final String hierarchySearchString = hierarchy + "%";
|
||||||
|
|
||||||
LoanMapper rm = new LoanMapper();
|
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) {
|
} catch (EmptyResultDataAccessException e) {
|
||||||
throw new LoanNotFoundException(loanId);
|
throw new LoanNotFoundException(loanId);
|
||||||
}
|
}
|
||||||
@@ -176,12 +183,25 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
@Override
|
@Override
|
||||||
public Page<LoanAccountData> retrieveAll(final SearchParameters searchParameters) {
|
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);
|
StringBuilder sqlBuilder = new StringBuilder(200);
|
||||||
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
|
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
|
||||||
sqlBuilder.append(loaanLoanMapper.loanSchema());
|
sqlBuilder.append(loaanLoanMapper.loanSchema());
|
||||||
|
|
||||||
final Object[] objectArray = new Object[1];
|
// TODO - for time being this will data scope list of loans returned to
|
||||||
int arrayPos = 0;
|
// 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();
|
String sqlQueryCriteria = searchParameters.getSqlSearch();
|
||||||
if (StringUtils.isNotBlank(sqlQueryCriteria)) {
|
if (StringUtils.isNotBlank(sqlQueryCriteria)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user