From 2b27f5b574b91dd5b7f1b286f42dff0c81e6e6cb Mon Sep 17 00:00:00 2001 From: Madhukar Date: Tue, 28 Jan 2014 20:00:31 +0530 Subject: [PATCH] MIFOSX-871 Advanced search query with search operators --- api-docs/apiLive.htm | 213 ++++++++++++++ .../loanproduct/data/LoanProductData.java | 2 +- .../portfolio/search/SearchConstants.java | 30 ++ .../search/api/SearchApiResource.java | 37 ++- .../search/data/AdHocQueryDataValidator.java | 276 ++++++++++++++++++ .../data/AdHocQuerySearchConditions.java | 168 +++++++++++ .../data/AdHocQuerySearchConstants.java | 45 +++ .../search/data/AdHocSearchQueryData.java | 57 ++++ .../service/SearchReadPlatformService.java | 6 + .../SearchReadPlatformServiceImpl.java | 158 +++++++++- 10 files changed, 988 insertions(+), 4 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQueryDataValidator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConditions.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConstants.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocSearchQueryData.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 087d6eece..d8e4598af 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -1406,6 +1406,14 @@ + + AdHoc Search + search/advance + Advance Search + + + + @@ -20994,6 +21002,211 @@ GET https://DomainName/api/v1/search?query=000000111 +   +
+
+

Advance Search

+

AdHoc Query search supported parameters and field descriptions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field Descriptions
entities
Entities to be included for search.(presently support added only for loans)
loanStatus
Loan status values to be included for search.
+ Supported loan status:["all","active","overpaid","closed","writeoff"] +
loanProducts
Array of loanproduct ids. +
offices
Array of office ids. +
loanDateOption
Date condition name for search query.
+ Supported values "approvalDate" or "createdDate" or "disbursalDate". +
loanFromDate
Start date for 'loanDateOption' in 'yyyy-MM-dd' format.
loanToDate
End date for 'loanDateOption' in 'yyyy-MM-dd' format.
includeOutStandingAmountPercentage
It is a Boolean value, allows to search percentage of loan outstanding amount with in a given range
outStandingAmountPercentageCondition
Condition type for outstanding amount percentage. + Supported values: between or <= or >= or < or > or =
+ When 'outStandingAmountPercentageCondition' is between required fields minOutStandingAmountPercentage and maxOutStandingAmountPercentage +
minOutStandingAmountPercentage
Minimum percentage of outstanding amount.
Mandatory when 'outStandingAmountPercentageCondition' type is 'between'
maxOutStandingAmountPercentage
Maximum percentage of outstanding amount.
Mandatory when 'outStandingAmountPercentageCondition' type is 'between'
outStandingAmountPercentage
Percentage of outstanding amount for search.
Mandatory when 'outStandingAmountPercentageCondition' type is otherthan 'between'
includeOutstandingAmount
Boolean value, allows user to search loan outstanding amount with in a given range
outstandingAmountCondition
Condition type to search outstanding amount. + Supported values: between or <= or >= or < or > or =
+ When 'outstandingAmountCondition' is between required fields minOutStandingAmount and maxOutStandingAmount +
minOutStandingAmount
Minimum value of outstanding amount for outstandingAmountCondition type between.
Mandatory when 'outstandingAmountCondition' type is 'between'
maxOutStandingAmount
Maximum value of outstanding amount for outstandingAmountCondition type between.
Mandatory when 'outstandingAmountCondition' type is 'between'
outStandingAmount
Default value of outstanding amount for search.
Mandatory when 'outstandingAmountCondition' type is otherthan 'between'
+
+
+   +
+
+

AdHoc Query Search

+

+ AdHocQuery search has more search options, it is a POST request, it uses request body to send search parameters
+

+ + + + + + + +
Mandatory Fields
entities
+
+ + + + + + + +
Optional Fields
+ loanStatus, loanProducts, offices, loanDateOption, loanFromDate, loanToDate,
includeOutStandingAmountPercentage, outStandingAmountPercentageCondition,
+ minOutStandingAmountPercentage and maxOutStandingAmountPercentage OR outStandingAmountPercentage,
includeOutstandingAmount, outstandingAmountCondition,
+ minOutstandingAmount and maxOutstandingAmount OR outstandingAmount
+
+
+ +POST https://DomainName/api/v1/search/advance + + +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" +} + + +[{ + "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 +}] + +
+
diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index 644d9a773..7028badd7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -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; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/SearchConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/SearchConstants.java index b76e42180..18d809552 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/SearchConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/SearchConstants.java @@ -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 values = new HashSet(); + static { + for (final SEARCH_LOAN_DATE param : SEARCH_LOAN_DATE.values()) { + values.add(param.value); + } + } + + public static Set getAllValues() { + return values; + } + + @Override + public String toString() { + return name().toString().replaceAll("_", " "); + } + + public String getValue() { + return this.value; + } + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/api/SearchApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/api/SearchApiResource.java index da4e57a74..fc21af0ff 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/api/SearchApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/api/SearchApiResource.java @@ -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 searchResponseParameters = SEARCH_RESPONSE_PARAMETERS.getAllValues(); private final SearchReadPlatformService searchReadPlatformService; - private final ToApiJsonSerializer toApiJsonSerializer; + private final ToApiJsonSerializer toApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; + private final AdHocQueryDataValidator fromApiJsonDeserializer; @Autowired public SearchApiResource(final SearchReadPlatformService searchReadPlatformService, - final ToApiJsonSerializer toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper) { + final ToApiJsonSerializer 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 searchResults = this.searchReadPlatformService.retrieveAdHocQueryMatchingData(searchConditions); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serialize(settings, searchResults); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQueryDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQueryDataValidator.java new file mode 100644 index 000000000..8c6c217f2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQueryDataValidator.java @@ -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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, + AdHocQuerySearchConstants.AD_HOC_SEARCH_QUERY_REQUEST_DATA_PARAMETERS); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + 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 dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + public AdHocQuerySearchConditions retrieveSearchConditions(String json) { + + validateAdHocQueryParameters(json); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + List loanStatus = null; + if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanStatusParamName, element)) { + loanStatus = Arrays.asList(this.fromApiJsonHelper.extractArrayNamed(AdHocQuerySearchConstants.loanStatusParamName, element)); + } + + List loanProducts = null; + if (this.fromApiJsonHelper.parameterExists(AdHocQuerySearchConstants.loanProductsParamName, element)) { + loanProducts = extractLongValuesList(Arrays.asList(this.fromApiJsonHelper.extractArrayNamed( + AdHocQuerySearchConstants.loanProductsParamName, element))); + } + + List 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 extractLongValuesList(List listTobeConverted) { + List tempList = new ArrayList(); + for (String temp : listTobeConverted) { + tempList.add(Long.valueOf(temp)); + } + return tempList; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConditions.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConditions.java new file mode 100644 index 000000000..d9077c76d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConditions.java @@ -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 loanStatus; + private final List loanProducts; + private final List 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 loanStatus, + final List loanProducts, final List 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 loanStatus, final List loanProducts, + final List 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 getLoanStatus() { + return getStatusVluesFromStatusCodes(); + } + + private List getStatusVluesFromStatusCodes() { + List loanStatusValues = new ArrayList(); + 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 getLoanProducts() { + return this.loanProducts; + } + + public List 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; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConstants.java new file mode 100644 index 000000000..90e0212fb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocQuerySearchConstants.java @@ -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 AD_HOC_SEARCH_QUERY_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(loanStatusParamName, + loanProductsParamName, officesParamName, loanDateOptionParamName, loanFromDateParamName, loanToDateParamName, + includeOutStandingAmountPercentageParamName, outStandingAmountPercentageConditionParamName, + minOutStandingAmountPercentageParamName, maxOutStandingAmountPercentageParamName, outStandingAmountPercentageParamName, + includeOutstandingAmountParamName, outstandingAmountConditionParamName, minOutstandingAmountParamName, + maxOutstandingAmountParamName, outstandingAmountParamName, localeParamName, dateFormatParamName)); + + public static final Set AD_HOC_SEARCH_QUERY_CONDITIONS = new HashSet( + 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"}; + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocSearchQueryData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocSearchQueryData.java new file mode 100644 index 000000000..f7241774e --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/data/AdHocSearchQueryData.java @@ -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 loanProducts; + @SuppressWarnings("unused") + private final Collection offices; + + public static AdHocSearchQueryData template(final Collection loanProducts, final Collection 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 loanProducts = null; + final Collection 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 loanProducts, + final Collection offices) { + + this.officeName = officeName; + this.loanProductName = loanProductName; + this.count = count; + this.loanOutStanding = loanOutStanding; + this.percentage = percentage; + + this.loanProducts = loanProducts; + this.offices = offices; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformService.java index 80d72c165..3a48ef875 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformService.java @@ -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 retriveMatchingData(SearchConditions searchConditions); + + AdHocSearchQueryData retrieveAdHocQueryTemplate(); + + Collection retrieveAdHocQueryMatchingData(AdHocQuerySearchConditions searchConditions); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java index 768e4bfba..97c4014f4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/search/service/SearchReadPlatformServiceImpl.java @@ -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 loanProducts = this.loanProductReadPlatformService.retrieveAllLoanProductsForLookup(); + final Collection offices = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); + + return AdHocSearchQueryData.template(loanProducts, offices); + } + + @Override + public Collection 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 { + + 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); + } + + } + } \ No newline at end of file