Merge pull request #735 from lingalamadhukar/mifosx-871

MIFOSX-871 Advanced search query with search operators
This commit is contained in:
Vishwas Babu A J
2014-02-09 00:58:53 +05:30
10 changed files with 988 additions and 4 deletions
+213
View File
@@ -1406,6 +1406,14 @@
<td></td>
<td></td>
</tr>
<tr class="alt">
<td><a href="#advance_search">AdHoc Search</a></td>
<td>search/advance</td>
<td><a href="#advance_search_resource">Advance Search</td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</div>
@@ -21053,6 +21061,211 @@ GET https://DomainName/api/v1/search?query=000000111
</code>
</div>
</div>
<a id="advance_search" name="advance_search" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h3>Advance Search</h3>
<p>AdHoc Query search supported parameters and field descriptions</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Field Descriptions</div></td>
</tr>
<tr class=alt>
<td>entities</td>
</tr>
<tr>
<td class=fielddesc>Entities to be included for search.(presently support added only for loans)<br></td>
</tr>
<tr class=alt>
<td>loanStatus</td>
</tr>
<tr>
<td class=fielddesc>Loan status values to be included for search.<br>
Supported loan status:["all","active","overpaid","closed","writeoff"]
</td>
</tr>
<tr class=alt>
<td>loanProducts</td>
</tr>
<tr>
<td class=fielddesc>Array of loanproduct ids.
</td>
</tr>
<tr class=alt>
<td>offices</td>
</tr>
<tr>
<td class=fielddesc>Array of office ids.
</td>
</tr>
<tr class=alt>
<td>loanDateOption</td>
</tr>
<tr>
<td class=fielddesc>Date condition name for search query.<br>
Supported values "approvalDate" or "createdDate" or "disbursalDate".
</td>
</tr>
<tr class=alt>
<td>loanFromDate</td>
</tr>
<tr>
<td class=fielddesc>Start date for 'loanDateOption' in 'yyyy-MM-dd' format. </td>
</tr>
<tr class=alt>
<td>loanToDate</td>
</tr>
<tr>
<td class=fielddesc>End date for 'loanDateOption' in 'yyyy-MM-dd' format. </td>
</tr>
<tr class=alt>
<td>includeOutStandingAmountPercentage</td>
</tr>
<tr>
<td class=fielddesc>It is a Boolean value, allows to search percentage of loan outstanding amount with in a given range</td>
</tr>
<tr class=alt>
<td>outStandingAmountPercentageCondition</td>
</tr>
<tr>
<td class=fielddesc>Condition type for outstanding amount percentage.
Supported values: between or &lt;= or &gt;= or &lt; or &gt; or = <br>
When 'outStandingAmountPercentageCondition' is <b>between</b> required fields <b>minOutStandingAmountPercentage</b> and <b>maxOutStandingAmountPercentage</b>
</td>
</tr>
<tr class=alt>
<td>minOutStandingAmountPercentage</td>
</tr>
<tr>
<td class=fielddesc>Minimum percentage of outstanding amount. <br> Mandatory when 'outStandingAmountPercentageCondition' type is 'between' </td>
</tr>
<tr class=alt>
<td>maxOutStandingAmountPercentage </td>
</tr>
<tr>
<td class=fielddesc>Maximum percentage of outstanding amount. <br> Mandatory when 'outStandingAmountPercentageCondition' type is 'between' </td>
</tr>
<tr class=alt>
<td>outStandingAmountPercentage</td>
</tr>
<tr>
<td class=fielddesc>Percentage of outstanding amount for search. <br> Mandatory when 'outStandingAmountPercentageCondition' type is otherthan 'between' </td>
</tr>
<tr class=alt>
<td>includeOutstandingAmount</td>
</tr>
<tr>
<td class=fielddesc>Boolean value, allows user to search loan outstanding amount with in a given range</td>
</tr>
<tr class=alt>
<td>outstandingAmountCondition</td>
</tr>
<tr>
<td class=fielddesc>Condition type to search outstanding amount.
Supported values: between or &lt;= or &gt;= or &lt; or &gt; or = <br>
When 'outstandingAmountCondition' is <b>between</b> required fields <b>minOutStandingAmount</b> and <b>maxOutStandingAmount</b>
</td>
</tr>
<tr class=alt>
<td>minOutStandingAmount</td>
</tr>
<tr>
<td class=fielddesc>Minimum value of outstanding amount for outstandingAmountCondition type between. <br> Mandatory when 'outstandingAmountCondition' type is 'between' </td>
</tr>
<tr class=alt>
<td>maxOutStandingAmount</td>
</tr>
<tr>
<td class=fielddesc>Maximum value of outstanding amount for outstandingAmountCondition type between. <br> Mandatory when 'outstandingAmountCondition' type is 'between' </td>
</tr>
<tr class=alt>
<td>outStandingAmount</td>
</tr>
<tr>
<td class=fielddesc>Default value of outstanding amount for search. <br> Mandatory when 'outstandingAmountCondition' type is otherthan 'between' </td>
</tr>
</table>
</div>
</div>
<a id="advance_search_resource" name="advance_search_resource" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>AdHoc Query Search</h4>
<p>
AdHocQuery search has more search options, it is a <b>POST</b> request, it uses request body to send search parameters<br>
</p>
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Mandatory Fields</div></td>
</tr>
<tr class=alt>
<td>entities</td>
</tr>
</table>
<br />
<table class=matrixHeading>
<tr class="matrixHeadingBG">
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>
loanStatus, loanProducts, offices, loanDateOption, loanFromDate, loanToDate, <br>includeOutStandingAmountPercentage, outStandingAmountPercentageCondition, <br>
minOutStandingAmountPercentage and maxOutStandingAmountPercentage OR outStandingAmountPercentage, <br>includeOutstandingAmount, outstandingAmountCondition, <br>
minOutstandingAmount and maxOutstandingAmount OR outstandingAmount</td>
</tr>
</table>
</div>
<div class="method-example">
<code class="method-declaration">
POST https://DomainName/api/v1/search/advance
</code>
<code class="method-request">
POST search/advance
Content-Type: application/json Request Body:
{
"locale": "en",
"dateFormat": "yyyy-MM-dd",
"entities": ["loans"],
"loanStatus": ["all","active","overpaid"],
"loanProducts": ["65","97","73","82"],
"offices": ["1","10","100"],
"loanDateOption": "approvalDate",
"loanFromDate": "2013-01-01",
"loanToDate": "2014-01-27",
"includeOutStandingAmountPercentage": true,
"outStandingAmountPercentageCondition": "<=",
"outStandingAmountPercentage": "80",
"includeOutstandingAmount": true,
"outstandingAmountCondition": "between",
"minOutstandingAmount": "100",
"maxOutstandingAmount": "10000"
}
</code>
<code class="method-response">
[{
"officeName": "HFC",
"loanProductName": "01 BC3M",
"count": 86,
"loanOutStanding": 5692.41,
"percentage": 76.4
},
{
"officeName": "keithoffice",
"loanProductName": "IGL",
"count": 3,
"loanOutStanding": 4168.23,
"percentage": 56.9
},
{
"officeName": "Rio",
"loanProductName": "IGL",
"count": 1,
"loanOutStanding": 1681.12,
"percentage": 55.56
}]
</code>
</div>
</div>
<!-- ends Search API docs-->
@@ -110,7 +110,7 @@ public class LoanProductData {
* Used when returning lookup information about loan product for dropdowns.
*/
public static LoanProductData lookup(final Long id, final String name) {
final String shortName = null;
final String shortName = null;
final String description = null;
final CurrencyData currency = null;
final BigDecimal principal = null;
@@ -100,4 +100,34 @@ public class SearchConstants {
return this.value;
}
}
public static enum SEARCH_LOAN_DATE {
APPROVAL_DATE("approvalDate"), CREATED_DATE("createdDate"), DISBURSAL_DATE("disbursalDate");
private final String value;
private SEARCH_LOAN_DATE(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<String>();
static {
for (final SEARCH_LOAN_DATE param : SEARCH_LOAN_DATE.values()) {
values.add(param.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
}
@@ -10,6 +10,7 @@ import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
@@ -21,6 +22,9 @@ import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.portfolio.search.SearchConstants.SEARCH_RESPONSE_PARAMETERS;
import org.mifosplatform.portfolio.search.data.AdHocQueryDataValidator;
import org.mifosplatform.portfolio.search.data.AdHocQuerySearchConditions;
import org.mifosplatform.portfolio.search.data.AdHocSearchQueryData;
import org.mifosplatform.portfolio.search.data.SearchConditions;
import org.mifosplatform.portfolio.search.data.SearchData;
import org.mifosplatform.portfolio.search.service.SearchReadPlatformService;
@@ -36,18 +40,33 @@ public class SearchApiResource {
private final Set<String> searchResponseParameters = SEARCH_RESPONSE_PARAMETERS.getAllValues();
private final SearchReadPlatformService searchReadPlatformService;
private final ToApiJsonSerializer<SearchData> toApiJsonSerializer;
private final ToApiJsonSerializer<Object> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final AdHocQueryDataValidator fromApiJsonDeserializer;
@Autowired
public SearchApiResource(final SearchReadPlatformService searchReadPlatformService,
final ToApiJsonSerializer<SearchData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper) {
final ToApiJsonSerializer<Object> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final AdHocQueryDataValidator fromApiJsonDeserializer) {
this.searchReadPlatformService = searchReadPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
}
@GET
@Path("/template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAdHocSearchQueryTemplate(@Context final UriInfo uriInfo) {
final AdHocSearchQueryData templateData = this.searchReadPlatformService.retrieveAdHocQueryTemplate();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, templateData);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@@ -62,4 +81,18 @@ public class SearchApiResource {
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, searchResults, this.searchResponseParameters);
}
@POST
@Path("/advance")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String advancedSearch(@Context final UriInfo uriInfo,final String json) {
final AdHocQuerySearchConditions searchConditions = this.fromApiJsonDeserializer.retrieveSearchConditions(json);
final Collection<AdHocSearchQueryData> searchResults = this.searchReadPlatformService.retrieveAdHocQueryMatchingData(searchConditions);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, searchResults);
}
}
@@ -0,0 +1,276 @@
package org.mifosplatform.portfolio.search.data;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public class AdHocQueryDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public AdHocQueryDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateAdHocQueryParameters(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
AdHocQuerySearchConstants.AD_HOC_SEARCH_QUERY_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(AdHocQuerySearchConstants.AD_HOC_SEARCH_QUERY_RESOURCE_NAME);
final String[] entities = this.fromApiJsonHelper.extractArrayNamed(AdHocQuerySearchConstants.entitiesParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.entitiesParamName).value(entities).arrayNotEmpty();
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanStatusParamName, element)) {
final String[] loanStatus = this.fromApiJsonHelper.extractArrayNamed(AdHocQuerySearchConstants.loanStatusParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.loanStatusParamName).value(loanStatus).arrayNotEmpty();
if (loanStatus != null && loanStatus.length > 0) {
for (String status : loanStatus) {
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.loanStatusParamName).value(status)
.isOneOfTheseValues(AdHocQuerySearchConstants.loanStatusOptions);
}
}
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanProductsParamName, element)) {
final String[] loanProducts = this.fromApiJsonHelper
.extractArrayNamed(AdHocQuerySearchConstants.loanProductsParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.loanProductsParamName).value(loanProducts).arrayNotEmpty();
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.officesParamName, element)) {
final String[] offices = this.fromApiJsonHelper.extractArrayNamed(AdHocQuerySearchConstants.officesParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.officesParamName).value(offices).arrayNotEmpty();
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanDateOptionParamName, element)) {
final String loanDateOption = this.fromApiJsonHelper.extractStringNamed(AdHocQuerySearchConstants.loanDateOptionParamName,
element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.loanDateOptionParamName).value(loanDateOption)
.isOneOfTheseValues(AdHocQuerySearchConstants.loanDateOptions);
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanFromDateParamName, element)) {
final LocalDate loanFromDate = this.fromApiJsonHelper.extractLocalDateNamed(AdHocQuerySearchConstants.loanFromDateParamName,
element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.loanFromDateParamName).value(loanFromDate).notNull();
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanToDateParamName, element)) {
final LocalDate loanToDate = this.fromApiJsonHelper.extractLocalDateNamed(AdHocQuerySearchConstants.loanToDateParamName,
element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.loanToDateParamName).value(loanToDate).notNull();
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.includeOutStandingAmountPercentageParamName, element)) {
final boolean includeOutStandingAmountPercentage = this.fromApiJsonHelper.extractBooleanNamed(
AdHocQuerySearchConstants.includeOutStandingAmountPercentageParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.includeOutStandingAmountPercentageParamName)
.value(includeOutStandingAmountPercentage).notNull();
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName, element)) {
final String outStandingAmountPercentageCondition = this.fromApiJsonHelper.extractStringNamed(
AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName)
.value(outStandingAmountPercentageCondition)
.isNotOneOfTheseValues(AdHocQuerySearchConstants.AD_HOC_SEARCH_QUERY_CONDITIONS);
if (outStandingAmountPercentageCondition.equals("between")) {
final BigDecimal minOutStandingAmountPercentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.minOutStandingAmountPercentageParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.minOutStandingAmountPercentageParamName)
.value(minOutStandingAmountPercentage).notNull().notLessThanMin(BigDecimal.ZERO);
final BigDecimal maxOutStandingAmountPercentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.maxOutStandingAmountPercentageParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.maxOutStandingAmountPercentageParamName)
.value(maxOutStandingAmountPercentage).notNull().notLessThanMin(BigDecimal.ZERO);
baseDataValidator.reset().comapareMinAndMaxOfTwoBigDecmimalNos(minOutStandingAmountPercentage,
maxOutStandingAmountPercentage);
} else {
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outStandingAmountPercentageParamName, element)) {
final BigDecimal outStandingAmountPercentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.outStandingAmountPercentageParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.outStandingAmountPercentageParamName)
.value(outStandingAmountPercentage).notNull().notLessThanMin(BigDecimal.ZERO);
}
}
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.includeOutstandingAmountParamName, element)) {
final Boolean includeOutstandingAmountParamName = this.fromApiJsonHelper.extractBooleanNamed(
AdHocQuerySearchConstants.includeOutstandingAmountParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.includeOutstandingAmountParamName)
.value(includeOutstandingAmountParamName).notNull();
}
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outstandingAmountConditionParamName, element)) {
final String outstandingAmountCondition = this.fromApiJsonHelper.extractStringNamed(
AdHocQuerySearchConstants.outstandingAmountConditionParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.outstandingAmountConditionParamName)
.value(outstandingAmountCondition).isNotOneOfTheseValues(AdHocQuerySearchConstants.AD_HOC_SEARCH_QUERY_CONDITIONS);
if (outstandingAmountCondition.equals("between")) {
final BigDecimal minOutstandingAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.minOutstandingAmountParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.minOutstandingAmountParamName).value(minOutstandingAmount)
.notNull().notLessThanMin(BigDecimal.ZERO);
final BigDecimal maxOutstandingAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.maxOutstandingAmountParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.maxOutstandingAmountParamName).value(maxOutstandingAmount)
.notNull().notLessThanMin(BigDecimal.ZERO);
baseDataValidator.reset().comapareMinAndMaxOfTwoBigDecmimalNos(minOutstandingAmount, maxOutstandingAmount);
} else {
final BigDecimal outstandingAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.outstandingAmountParamName, element);
baseDataValidator.reset().parameter(AdHocQuerySearchConstants.outstandingAmountParamName).value(outstandingAmount)
.notNull().notLessThanMin(BigDecimal.ZERO);
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public AdHocQuerySearchConditions retrieveSearchConditions(String json) {
validateAdHocQueryParameters(json);
final JsonElement element = this.fromApiJsonHelper.parse(json);
List<String> loanStatus = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanStatusParamName, element)) {
loanStatus = Arrays.asList(this.fromApiJsonHelper.extractArrayNamed(AdHocQuerySearchConstants.loanStatusParamName, element));
}
List<Long> loanProducts = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanProductsParamName, element)) {
loanProducts = extractLongValuesList(Arrays.asList(this.fromApiJsonHelper.extractArrayNamed(
AdHocQuerySearchConstants.loanProductsParamName, element)));
}
List<Long> offices = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.officesParamName, element)) {
offices = extractLongValuesList(Arrays.asList(this.fromApiJsonHelper.extractArrayNamed(
AdHocQuerySearchConstants.officesParamName, element)));
}
String loanDateOption = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanDateOptionParamName, element)) {
loanDateOption = this.fromApiJsonHelper.extractStringNamed(AdHocQuerySearchConstants.loanDateOptionParamName, element);
}
LocalDate loanFromDate = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanFromDateParamName, element)) {
loanFromDate = this.fromApiJsonHelper.extractLocalDateNamed(AdHocQuerySearchConstants.loanFromDateParamName, element);
}
LocalDate loanToDate = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanToDateParamName, element)) {
loanToDate = this.fromApiJsonHelper.extractLocalDateNamed(AdHocQuerySearchConstants.loanToDateParamName, element);
}
Boolean includeOutStandingAmountPercentage = false;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.includeOutStandingAmountPercentageParamName, element)) {
includeOutStandingAmountPercentage = this.fromApiJsonHelper.extractBooleanNamed(
AdHocQuerySearchConstants.includeOutStandingAmountPercentageParamName, element);
}
String outStandingAmountPercentageCondition = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName, element)) {
outStandingAmountPercentageCondition = this.fromApiJsonHelper.extractStringNamed(
AdHocQuerySearchConstants.outStandingAmountPercentageConditionParamName, element);
}
BigDecimal minOutStandingAmountPercentage = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.minOutStandingAmountPercentageParamName, element)) {
minOutStandingAmountPercentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.minOutStandingAmountPercentageParamName, element);
}
BigDecimal maxOutStandingAmountPercentage = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.maxOutStandingAmountPercentageParamName, element)) {
maxOutStandingAmountPercentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.maxOutStandingAmountPercentageParamName, element);
}
BigDecimal outStandingAmountPercentage = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outStandingAmountPercentageParamName, element)) {
outStandingAmountPercentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.outStandingAmountPercentageParamName, element);
}
Boolean includeOutstandingAmountParamName = false;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.includeOutstandingAmountParamName, element)) {
includeOutstandingAmountParamName = this.fromApiJsonHelper.extractBooleanNamed(
AdHocQuerySearchConstants.includeOutstandingAmountParamName, element);
}
String outstandingAmountCondition = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outstandingAmountConditionParamName, element)) {
outstandingAmountCondition = this.fromApiJsonHelper.extractStringNamed(
AdHocQuerySearchConstants.outstandingAmountConditionParamName, element);
}
BigDecimal minOutstandingAmount = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.minOutstandingAmountParamName, element)) {
minOutstandingAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.minOutstandingAmountParamName, element);
}
BigDecimal maxOutstandingAmount = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.maxOutstandingAmountParamName, element)) {
maxOutstandingAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.maxOutstandingAmountParamName, element);
}
BigDecimal outstandingAmount = null;
if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.outstandingAmountParamName, element)) {
outstandingAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
AdHocQuerySearchConstants.outstandingAmountParamName, element);
}
return AdHocQuerySearchConditions.instance(loanStatus, loanProducts, offices, loanDateOption, loanFromDate, loanToDate,
includeOutStandingAmountPercentage, outStandingAmountPercentageCondition, minOutStandingAmountPercentage,
maxOutStandingAmountPercentage, outStandingAmountPercentage, includeOutstandingAmountParamName, outstandingAmountCondition,
minOutstandingAmount, maxOutstandingAmount, outstandingAmount);
}
private List<Long> extractLongValuesList(List<String> listTobeConverted) {
List<Long> tempList = new ArrayList<Long>();
for (String temp : listTobeConverted) {
tempList.add(Long.valueOf(temp));
}
return tempList;
}
}
@@ -0,0 +1,168 @@
package org.mifosplatform.portfolio.search.data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
public class AdHocQuerySearchConditions {
private final List<String> loanStatus;
private final List<Long> loanProducts;
private final List<Long> offices;
private final String loanDateOption;
private final LocalDate loanFromDate;
private final LocalDate loanToDate;
private final Boolean includeOutStandingAmountPercentage;
private final String outStandingAmountPercentageCondition;
private final BigDecimal minOutStandingAmountPercentage;
private final BigDecimal maxOutStandingAmountPercentage;
private final BigDecimal outStandingAmountPercentage;
private final Boolean includeOutstandingAmount;
private final String outstandingAmountCondition;
private final BigDecimal minOutstandingAmount;
private final BigDecimal maxOutstandingAmount;
private final BigDecimal outstandingAmount;
public static AdHocQuerySearchConditions instance(final List<String> loanStatus,
final List<Long> loanProducts, final List<Long> offices, final String loanDateOption, final LocalDate loanFromDate,
final LocalDate loanToDate, final Boolean includeOutStandingAmountPercentage,
final String outStandingAmountPercentageCondition, final BigDecimal minOutStandingAmountPercentage,
final BigDecimal maxOutStandingAmountPercentage, final BigDecimal outStandingAmountPercentage,
final Boolean includeOutstandingAmountParamName, final String outstandingAmountCondition,
final BigDecimal minOutstandingAmount, final BigDecimal maxOutstandingAmount, final BigDecimal outstandingAmount) {
return new AdHocQuerySearchConditions(loanStatus, loanProducts, offices, loanDateOption, loanFromDate, loanToDate,
includeOutStandingAmountPercentage, outStandingAmountPercentageCondition, minOutStandingAmountPercentage,
maxOutStandingAmountPercentage, outStandingAmountPercentage, includeOutstandingAmountParamName, outstandingAmountCondition,
minOutstandingAmount, maxOutstandingAmount, outstandingAmount);
}
public AdHocQuerySearchConditions(final List<String> loanStatus, final List<Long> loanProducts,
final List<Long> offices, final String loanDateOption, final LocalDate loanFromDate, final LocalDate loanToDate,
final Boolean includeOutStandingAmountPercentage, final String outStandingAmountPercentageCondition,
final BigDecimal minOutStandingAmountPercentage, final BigDecimal maxOutStandingAmountPercentage,
final BigDecimal outStandingAmountPercentage, final Boolean includeOutstandingAmount, final String outstandingAmountCondition,
final BigDecimal minOutstandingAmount, final BigDecimal maxOutstandingAmount, final BigDecimal outstandingAmount) {
this.loanStatus = loanStatus;
this.loanProducts = loanProducts;
this.offices = offices;
this.loanDateOption = loanDateOption;
this.loanFromDate = loanFromDate;
this.loanToDate = loanToDate;
this.includeOutStandingAmountPercentage = includeOutStandingAmountPercentage;
this.outStandingAmountPercentageCondition = outStandingAmountPercentageCondition;
this.minOutStandingAmountPercentage = minOutStandingAmountPercentage;
this.maxOutStandingAmountPercentage = maxOutStandingAmountPercentage;
this.outStandingAmountPercentage = outStandingAmountPercentage;
this.includeOutstandingAmount = includeOutstandingAmount;
this.outstandingAmountCondition = outstandingAmountCondition;
this.minOutstandingAmount = minOutstandingAmount;
this.maxOutstandingAmount = maxOutstandingAmount;
this.outstandingAmount = outstandingAmount;
}
public List<String> getLoanStatus() {
return getStatusVluesFromStatusCodes();
}
private List<String> getStatusVluesFromStatusCodes() {
List<String> loanStatusValues = new ArrayList<String>();
if (this.loanStatus != null) {
for (String statusCode : this.loanStatus) {
if (statusCode.equalsIgnoreCase("active")) {
loanStatusValues.add(LoanStatus.ACTIVE.getValue().toString());
}
if (statusCode.equalsIgnoreCase("overpaid")) {
loanStatusValues.add(LoanStatus.OVERPAID.getValue().toString());
}
if (statusCode.equalsIgnoreCase("closed")) {
loanStatusValues.add(LoanStatus.CLOSED_OBLIGATIONS_MET.getValue().toString());
}
if (statusCode.equalsIgnoreCase("writeoff")) {
loanStatusValues.add(LoanStatus.CLOSED_WRITTEN_OFF.getValue().toString());
}
if (statusCode.equalsIgnoreCase("arrears")) {
//TODO - No status code is there for loans which are in active bad standing
}
if (statusCode.equalsIgnoreCase("all")) {
loanStatusValues.add("all");
}
}
}
return loanStatusValues;
}
public List<Long> getLoanProducts() {
return this.loanProducts;
}
public List<Long> getOffices() {
return this.offices;
}
public String getLoanDateOption() {
return this.loanDateOption;
}
public LocalDate getLoanFromDate() {
return this.loanFromDate;
}
public LocalDate getLoanToDate() {
return this.loanToDate;
}
public Boolean getIncludeOutStandingAmountPercentage() {
return this.includeOutStandingAmountPercentage;
}
public String getOutStandingAmountPercentageCondition() {
return this.outStandingAmountPercentageCondition;
}
public BigDecimal getMinOutStandingAmountPercentage() {
return this.minOutStandingAmountPercentage;
}
public BigDecimal getMaxOutStandingAmountPercentage() {
return this.maxOutStandingAmountPercentage;
}
public BigDecimal getOutStandingAmountPercentage() {
return this.outStandingAmountPercentage;
}
public Boolean getIncludeOutstandingAmount() {
return this.includeOutstandingAmount;
}
public String getOutstandingAmountCondition() {
return this.outstandingAmountCondition;
}
public BigDecimal getMinOutstandingAmount() {
return this.minOutstandingAmount;
}
public BigDecimal getMaxOutstandingAmount() {
return this.maxOutstandingAmount;
}
public BigDecimal getOutstandingAmount() {
return this.outstandingAmount;
}
}
@@ -0,0 +1,45 @@
package org.mifosplatform.portfolio.search.data;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public interface AdHocQuerySearchConstants {
public static final String AD_HOC_SEARCH_QUERY_RESOURCE_NAME = "adHocQuery";
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
public static final String entitiesParamName = "entities";
public static final String loanStatusParamName = "loanStatus";
public static final String loanProductsParamName = "loanProducts";
public static final String officesParamName = "offices";
public static final String loanDateOptionParamName = "loanDateOption";
public static final String loanFromDateParamName = "loanFromDate";
public static final String loanToDateParamName = "loanToDate";
public static final String includeOutStandingAmountPercentageParamName = "includeOutStandingAmountPercentage";
public static final String outStandingAmountPercentageConditionParamName = "outStandingAmountPercentageCondition";
public static final String minOutStandingAmountPercentageParamName = "minOutStandingAmountPercentage";
public static final String maxOutStandingAmountPercentageParamName = "maxOutStandingAmountPercentage";
public static final String outStandingAmountPercentageParamName = "outStandingAmountPercentage";
public static final String includeOutstandingAmountParamName = "includeOutstandingAmount";
public static final String outstandingAmountConditionParamName = "outstandingAmountCondition";
public static final String minOutstandingAmountParamName = "minOutstandingAmount";
public static final String maxOutstandingAmountParamName = "maxOutstandingAmount";
public static final String outstandingAmountParamName = "outstandingAmount";
public static final Set<String> AD_HOC_SEARCH_QUERY_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(loanStatusParamName,
loanProductsParamName, officesParamName, loanDateOptionParamName, loanFromDateParamName, loanToDateParamName,
includeOutStandingAmountPercentageParamName, outStandingAmountPercentageConditionParamName,
minOutStandingAmountPercentageParamName, maxOutStandingAmountPercentageParamName, outStandingAmountPercentageParamName,
includeOutstandingAmountParamName, outstandingAmountConditionParamName, minOutstandingAmountParamName,
maxOutstandingAmountParamName, outstandingAmountParamName, localeParamName, dateFormatParamName));
public static final Set<String> AD_HOC_SEARCH_QUERY_CONDITIONS = new HashSet<String>(
Arrays.asList("between", "<=", ">=", "<", ">", "="));
public static final Object[] loanDateOptions = {"approvalDate", "createdDate", "disbursalDate"};
public static final Object[] entityTypeOptions = {"clients", "groups", "loans", "clientIdentifiers"};
public static final Object[] loanStatusOptions = {"all", "active", "overpaid", "arrears", "closed", "writeoff"};
}
@@ -0,0 +1,57 @@
package org.mifosplatform.portfolio.search.data;
import java.math.BigDecimal;
import java.util.Collection;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
public class AdHocSearchQueryData {
@SuppressWarnings("unused")
private final String officeName;
@SuppressWarnings("unused")
private final String loanProductName;
@SuppressWarnings("unused")
private final Integer count;
@SuppressWarnings("unused")
private final BigDecimal loanOutStanding;
@SuppressWarnings("unused")
private final Double percentage;
@SuppressWarnings("unused")
private final Collection<LoanProductData> loanProducts;
@SuppressWarnings("unused")
private final Collection<OfficeData> offices;
public static AdHocSearchQueryData template(final Collection<LoanProductData> loanProducts, final Collection<OfficeData> offices) {
final String officeName = null;
final String loanProductName = null;
final Integer count = null;
final BigDecimal loanOutStanding = null;
final Double percentage = null;
return new AdHocSearchQueryData(officeName, loanProductName, count, loanOutStanding, percentage, loanProducts, offices);
}
public static AdHocSearchQueryData matchedResult(final String officeName, final String loanProductName, final Integer count,
final BigDecimal loanOutStanding, final Double percentage) {
final Collection<LoanProductData> loanProducts = null;
final Collection<OfficeData> offices = null;
return new AdHocSearchQueryData(officeName, loanProductName, count, loanOutStanding, percentage, loanProducts, offices);
}
private AdHocSearchQueryData(final String officeName, final String loanProductName, final Integer count,
final BigDecimal loanOutStanding, final Double percentage, final Collection<LoanProductData> loanProducts,
final Collection<OfficeData> offices) {
this.officeName = officeName;
this.loanProductName = loanProductName;
this.count = count;
this.loanOutStanding = loanOutStanding;
this.percentage = percentage;
this.loanProducts = loanProducts;
this.offices = offices;
}
}
@@ -7,10 +7,16 @@ package org.mifosplatform.portfolio.search.service;
import java.util.Collection;
import org.mifosplatform.portfolio.search.data.AdHocQuerySearchConditions;
import org.mifosplatform.portfolio.search.data.AdHocSearchQueryData;
import org.mifosplatform.portfolio.search.data.SearchConditions;
import org.mifosplatform.portfolio.search.data.SearchData;
public interface SearchReadPlatformService {
Collection<SearchData> retriveMatchingData(SearchConditions searchConditions);
AdHocSearchQueryData retrieveAdHocQueryTemplate();
Collection<AdHocSearchQueryData> retrieveAdHocQueryMatchingData(AdHocQuerySearchConditions searchConditions);
}
@@ -5,13 +5,23 @@
*/
package org.mifosplatform.portfolio.search.service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.mifosplatform.portfolio.search.SearchConstants;
import org.mifosplatform.portfolio.search.data.AdHocQuerySearchConditions;
import org.mifosplatform.portfolio.search.data.AdHocSearchQueryData;
import org.mifosplatform.portfolio.search.data.SearchConditions;
import org.mifosplatform.portfolio.search.data.SearchData;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -26,11 +36,16 @@ public class SearchReadPlatformServiceImpl implements SearchReadPlatformService
private final NamedParameterJdbcTemplate namedParameterjdbcTemplate;
private final PlatformSecurityContext context;
private final LoanProductReadPlatformService loanProductReadPlatformService;
private final OfficeReadPlatformService officeReadPlatformService;
@Autowired
public SearchReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource) {
public SearchReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
final LoanProductReadPlatformService loanProductReadPlatformService, final OfficeReadPlatformService officeReadPlatformService ) {
this.context = context;
this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.loanProductReadPlatformService = loanProductReadPlatformService;
this.officeReadPlatformService = officeReadPlatformService;
}
@Override
@@ -145,4 +160,145 @@ public class SearchReadPlatformServiceImpl implements SearchReadPlatformService
}
@Override
public AdHocSearchQueryData retrieveAdHocQueryTemplate() {
this.context.authenticatedUser();
final Collection<LoanProductData> loanProducts = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup();
final Collection<OfficeData> offices = this.officeReadPlatformService.retrieveAllOfficesForDropdown();
return AdHocSearchQueryData.template(loanProducts, offices);
}
@Override
public Collection<AdHocSearchQueryData> retrieveAdHocQueryMatchingData(final AdHocQuerySearchConditions searchConditions) {
this.context.authenticatedUser();
final AdHocQuerySearchMapper rm = new AdHocQuerySearchMapper();
final MapSqlParameterSource params = new MapSqlParameterSource();
return this.namedParameterjdbcTemplate.query(rm.schema(searchConditions, params), params, rm);
}
private static final class AdHocQuerySearchMapper implements RowMapper<AdHocSearchQueryData> {
private boolean isWhereClauseAdded = false;
//TODO- build the query dynamically based on selected entity types, for now adding query for only loan entity.
public String schema(final AdHocQuerySearchConditions searchConditions, final MapSqlParameterSource params) {
final StringBuffer sql = new StringBuffer();
sql.append("Select a.name as officeName, a.Product as productName, a.cnt as 'count', a.outstandingAmt as outstanding, a.percentOut as percentOut ")
.append("from (select mo.name, mp.name Product, sum(ifnull(ml.total_expected_repayment_derived,0.0)) TotalAmt, count(*) cnt, ")
.append("sum(ifnull(ml.total_outstanding_derived,0.0)) outstandingAmt, ")
.append("(sum(ifnull(ml.total_outstanding_derived,0.0)) * 100 / sum(ifnull(ml.total_expected_repayment_derived,0.0))) percentOut ")
.append("from m_loan ml inner join m_product_loan mp on mp.id=ml.product_id ")
.append("inner join m_client mc on mc.id=ml.client_id ")
.append("inner join m_office mo on mo.id=mc.office_id ");
if (searchConditions.getLoanStatus() != null && searchConditions.getLoanStatus().size() > 0) {
//If user requests for all statuses no need to add loanStatus filter
if (!searchConditions.getLoanStatus().contains("all")) {
checkAndUpdateWhereClause(sql);
params.addValue("loanStatus", searchConditions.getLoanStatus());
sql.append(" ml.loan_status_id in (:loanStatus) ");
}
}
if (searchConditions.getLoanProducts() != null && searchConditions.getLoanProducts().size() > 0 ) {
checkAndUpdateWhereClause(sql);
params.addValue("loanProducts", searchConditions.getLoanProducts());
sql.append(" mp.id in (:loanProducts) ");
}
if (searchConditions.getOffices() != null && searchConditions.getOffices().size() > 0) {
checkAndUpdateWhereClause(sql);
params.addValue("offices", searchConditions.getOffices());
sql.append(" mo.id in (:offices) ");
}
if (StringUtils.isNotBlank(searchConditions.getLoanDateOption())) {
if (searchConditions.getLoanDateOption().equals(SearchConstants.SEARCH_LOAN_DATE.APPROVAL_DATE.getValue())) {
checkAndUpdateWhereClause(sql);
params.addValue("loanFromDate", searchConditions.getLoanFromDate().toDate());
params.addValue("loanToDate", searchConditions.getLoanToDate().toDate());
sql.append(" ( ml.approvedon_date between :loanFromDate and :loanToDate ) ");
} else if (searchConditions.getLoanDateOption().equals(SearchConstants.SEARCH_LOAN_DATE.CREATED_DATE.getValue())) {
checkAndUpdateWhereClause(sql);
params.addValue("loanFromDate", searchConditions.getLoanFromDate().toDate());
params.addValue("loanToDate", searchConditions.getLoanToDate().toDate());
sql.append(" ( ml.submittedon_date between :loanFromDate and :loanToDate ) ");
} else if (searchConditions.getLoanDateOption().equals(SearchConstants.SEARCH_LOAN_DATE.DISBURSAL_DATE.getValue())) {
checkAndUpdateWhereClause(sql);
params.addValue("loanFromDate", searchConditions.getLoanFromDate().toDate());
params.addValue("loanToDate", searchConditions.getLoanToDate().toDate());
sql.append(" ( ml.disbursedon_date between :loanFromDate and :loanToDate ) ");
}
}
sql.append(" group by mo.id) a ");
//update isWhereClauseAdded to false to add filters for derived table
isWhereClauseAdded = false;
if (searchConditions.getIncludeOutStandingAmountPercentage()) {
if (searchConditions.getOutStandingAmountPercentageCondition().equals("between")) {
checkAndUpdateWhereClause(sql);
// params.addValue("outStandingAmountPercentageCondition", searchConditions.getOutStandingAmountPercentageCondition());
params.addValue("minOutStandingAmountPercentage", searchConditions.getMinOutStandingAmountPercentage());
params.addValue("maxOutStandingAmountPercentage", searchConditions.getMaxOutStandingAmountPercentage());
sql.append(" ( a.percentOut between :minOutStandingAmountPercentage and :maxOutStandingAmountPercentage ) ");
} else {
checkAndUpdateWhereClause(sql);
// params.addValue("outStandingAmountPercentageCondition", searchConditions.getOutStandingAmountPercentageCondition());
params.addValue("outStandingAmountPercentage", searchConditions.getOutStandingAmountPercentage());
sql.append(" a.percentOut ")
.append(searchConditions.getOutStandingAmountPercentageCondition())
.append(" :outStandingAmountPercentage ");
}
}
if (searchConditions.getIncludeOutstandingAmount()) {
if (searchConditions.getOutstandingAmountCondition().equals("between")) {
checkAndUpdateWhereClause(sql);
// params.addValue("outstandingAmountCondition", searchConditions.getOutstandingAmountCondition());
params.addValue("minOutstandingAmount", searchConditions.getMinOutstandingAmount());
params.addValue("maxOutstandingAmount", searchConditions.getMaxOutstandingAmount());
sql.append(" ( a.outstandingAmt between :minOutstandingAmount and :maxOutstandingAmount ) ");
} else {
checkAndUpdateWhereClause(sql);
// params.addValue("outstandingAmountCondition", searchConditions.getOutstandingAmountCondition());
params.addValue("outstandingAmount", searchConditions.getOutstandingAmount());
sql.append(" a.outstandingAmt ")
.append(searchConditions.getOutstandingAmountCondition())
.append(" :outstandingAmount ");
}
}
return sql.toString();
}
private void checkAndUpdateWhereClause(final StringBuffer sql){
if (isWhereClauseAdded) {
sql.append(" and ");
} else {
sql.append(" where ");
isWhereClauseAdded = true;
}
}
@Override
public AdHocSearchQueryData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final String officeName = rs.getString("officeName");
final String loanProductName = rs.getString("productName");
final Integer count = JdbcSupport.getInteger(rs, "count");
final BigDecimal loanOutStanding = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "outstanding").setScale(2, RoundingMode.HALF_UP);
final Double percentage = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "percentOut").setScale(2, RoundingMode.HALF_UP).doubleValue();
return AdHocSearchQueryData.matchedResult(officeName, loanProductName, count, loanOutStanding, percentage);
}
}
}