diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 4ead11adc..e381755b6 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -3872,6 +3872,14 @@ self/clients/{clientId}/obligeedetails Obligee list + + + + + Run Report + self/runreports/{reportName} + + Run a Report @@ -49299,6 +49307,7 @@ No Request Body: } +  
@@ -50554,6 +50563,134 @@ GET https://DomainName/api/v1/self/loanproducts/{productId}?clientId={clientId}
+   +
+
+

Running a Report

+

This resource allows you to run and receive output from pre-defined Apache Fineract reports.

+

+ The default output is a JSON formatted "Generic Resultset". The Generic Resultset contains + Column Heading as well as Data information. However, you can + export to CSV format by simply adding "&exportCSV=true" to the end + of your URL.

+

If Pentaho reports have been pre-defined, they can also be + run through this resource. Pentaho reports can return HTML, PDF or + CSV formats.

+

The Apache Fineract reference application uses a + JQuery plugin called stretchyreporting which, itself, uses this + reports resource to provide a pretty flexible reporting User + Interface (UI).

+
Arguments
+
+
R_'parameter names' ...
+
+ optional, No defaults +
+
The number and names of the parameters depend on the + specific report and how it has been configured. R_officeId is an + example parameter name.
+
Note: the prefix R_ stands for Reporting
+
genericResultSet
+
+ optional, defaults to true +
+
If 'true' an optimised JSON format is returned suitable for tabular display of data. +
If 'false' a simple JSON format is returned. +
parameterType
+
+ optional, The only valid value is 'true'. If any + other value is provided the argument will be ignored +
+
Determines whether the request looks in the list of + reports or the list of parameters for its data. Doesn't apply to + Pentaho reports.
+
exportCSV
+
+ optional, The only valid value is 'true'. If any + other value is provided the argument will be ignored +
+
Output will be delivered as a CSV file instead of JSON. + Doesn't apply to Pentaho reports.
+
output-type
+
+ optional, Defaults to HTML. +
+
Valid Values are HTML, XLS, XSLX, CSV and PDF for html, Excel, Excel 2007+, + CSV and PDF formats respectively.
+
Only applies to Pentaho reports.
+
locale
+
+ optional +
+
Any valid locale Ex: en_US, en_IN, fr_FR etc
+
Only applies to Pentaho reports.
+
+

Example Requests:

+
self/runreports/Client%20Details?R_officeId=1
+
+
+
self/runreports/Client%20Details?R_officeId=1&exportCSV=true
+
+
+
+
+ +GET https://DomainName/api/v1/self/runreports/{reportName} + + +{ + "columnHeaders": [ + { + "columnName": "Office/Branch", + "columnType": "VARCHAR", + "isColumnNullable": false, + "isColumnPrimaryKey": false, + "columnValues": [] + }, + { + "columnName": "Client Account No.", + "columnType": "VARCHAR", + "isColumnNullable": false, + "isColumnPrimaryKey": false, + "columnValues": [] + }, + { + "columnName": "Name", + "columnType": "VARCHAR", + "isColumnNullable": false, + "isColumnPrimaryKey": false, + "columnValues": [] + }, + { + "columnName": "Joined", + "columnType": "DATE", + "isColumnNullable": false, + "isColumnPrimaryKey": false, + "columnValues": [] + }, + { + "columnName": "External Id", + "columnType": "VARCHAR", + "isColumnNullable": false, + "isColumnPrimaryKey": false, + "columnValues": [] + } + ], + "data": [ + { + "row": [ + "Head Office", + "000000001", + "John Doe", + "2017-03-04", + "786YYH7" + ] + } + ] +} + +
+
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java index ff83770e9..e13e65246 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java @@ -24,10 +24,12 @@ import java.util.Map; import java.util.Set; import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; @@ -82,9 +84,11 @@ public class RunreportsApiResource { @ApiOperation(value = "Running a Report", notes = "This resource allows you to run and receive output from pre-defined Apache Fineract reports.\n" + "\n" + "Reports can also be used to provide data for searching and workflow functionality.\n" + "\n" + "The default output is a JSON formatted \"Generic Resultset\". The Generic Resultset contains Column Heading as well as Data information. However, you can export to CSV format by simply adding \"&exportCSV=true\" to the end of your URL.\n" + "\n" + "If Pentaho reports have been pre-defined, they can also be run through this resource. Pentaho reports can return HTML, PDF or CSV formats.\n" + "\n" + "The Apache Fineract reference application uses a JQuery plugin called stretchy reporting which, itself, uses this reports resource to provide a pretty flexible reporting User Interface (UI).\n\n" + "\n" + "\n" + "Example Requests:\n" + "\n" + "runreports/Client%20Listing?R_officeId=1\n" + "\n" + "\n" + "runreports/Client%20Listing?R_officeId=1&exportCSV=true\n" + "\n" + "\n" + "runreports/OfficeIdSelectOne?R_officeId=1¶meterType=true\n" + "\n" + "\n" + "runreports/OfficeIdSelectOne?R_officeId=1¶meterType=true&exportCSV=true\n" + "\n" + "\n" + "runreports/Expected%20Payments%20By%20Date%20-%20Formatted?R_endDate=2013-04-30&R_loanOfficerId=-1&R_officeId=1&R_startDate=2013-04-16&output-type=HTML&R_officeId=1\n" + "\n" + "\n" + "runreports/Expected%20Payments%20By%20Date%20-%20Formatted?R_endDate=2013-04-30&R_loanOfficerId=-1&R_officeId=1&R_startDate=2013-04-16&output-type=XLS&R_officeId=1\n" + "\n" + "\n" + "runreports/Expected%20Payments%20By%20Date%20-%20Formatted?R_endDate=2013-04-30&R_loanOfficerId=-1&R_officeId=1&R_startDate=2013-04-16&output-type=CSV&R_officeId=1\n" + "\n" + "\n" + "runreports/Expected%20Payments%20By%20Date%20-%20Formatted?R_endDate=2013-04-30&R_loanOfficerId=-1&R_officeId=1&R_startDate=2013-04-16&output-type=PDF&R_officeId=1") @ApiResponses({@ApiResponse(code = 200, message = "", response = RunreportsApiResourceSwagger.GetReportNameResponse.class)}) - public Response runReport(@PathParam("reportName") @ApiParam(value = "reportName") final String reportName, @Context final UriInfo uriInfo) { + public Response runReport(@PathParam("reportName") @ApiParam(value = "reportName") final String reportName, + @Context final UriInfo uriInfo, + @DefaultValue("false") @QueryParam("isSelfServiceUserReport") @ApiParam(value = "isSelfServiceUserReport") final boolean isSelfServiceUserReport) { - final MultivaluedMap queryParams = uriInfo.getQueryParameters(); + final MultivaluedMap queryParams = uriInfo.getQueryParameters(); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean exportCsv = ApiParameterHelper.exportCsv(uriInfo.getQueryParameters()); @@ -96,7 +100,7 @@ public class RunreportsApiResource { String parameterTypeValue = null; if (!parameterType) { parameterTypeValue = "report"; - String reportType = this.readExtraDataAndReportingService.getReportType(reportName); + String reportType = this.readExtraDataAndReportingService.getReportType(reportName, isSelfServiceUserReport); ReportingProcessService reportingProcessService = this.reportingProcessServiceProvider.findReportingProcessService(reportType); if (reportingProcessService != null) { return reportingProcessService.processRequest(reportName, queryParams); } } else { @@ -108,7 +112,7 @@ public class RunreportsApiResource { if (exportPdf) { final Map reportParams = getReportParams(queryParams); final String pdfFileName = this.readExtraDataAndReportingService - .retrieveReportPDF(reportName, parameterTypeValue, reportParams); + .retrieveReportPDF(reportName, parameterTypeValue, reportParams, isSelfServiceUserReport); final File file = new File(pdfFileName); @@ -124,7 +128,7 @@ public class RunreportsApiResource { final Map reportParams = getReportParams(queryParams); final GenericResultsetData result = this.readExtraDataAndReportingService.retrieveGenericResultset(reportName, - parameterTypeValue, reportParams); + parameterTypeValue, reportParams, isSelfServiceUserReport); String json = ""; final boolean genericResultSetIsPassed = ApiParameterHelper.genericResultSetPassed(uriInfo.getQueryParameters()); @@ -145,7 +149,7 @@ public class RunreportsApiResource { // CSV Export final Map reportParams = getReportParams(queryParams); final StreamingOutput result = this.readExtraDataAndReportingService - .retrieveReportCSV(reportName, parameterTypeValue, reportParams); + .retrieveReportCSV(reportName, parameterTypeValue, reportParams, isSelfServiceUserReport); return Response.ok().entity(result).type("text/csv") .header("Content-Disposition", "attachment;filename=" + reportName.replaceAll(" ", "") + ".csv").build(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java index f909c33b6..95965ab79 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java @@ -75,6 +75,9 @@ public final class Report extends AbstractPersistableCustom { @OneToMany(cascade = CascadeType.ALL, mappedBy = "report", orphanRemoval = true, fetch=FetchType.EAGER) private Set reportParameterUsages = new HashSet<>(); + + @Column(name = "self_service_user_report") + private boolean isSelfServiceUserReport; public static Report fromJson(final JsonCommand command, final Collection reportTypes) { @@ -278,6 +281,7 @@ public final class Report extends AbstractPersistableCustom { } public Set getReportParameterUsages() { - return this.reportParameterUsages; - } + return this.reportParameterUsages; + } + } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java index 952a36a42..bc8b9a68b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java @@ -32,13 +32,13 @@ import org.apache.fineract.useradministration.domain.AppUser; public interface ReadReportingService { - StreamingOutput retrieveReportCSV(String name, String type, Map extractedQueryParams); + StreamingOutput retrieveReportCSV(String name, String type, Map extractedQueryParams, boolean isSelfServiceUserReport); - GenericResultsetData retrieveGenericResultset(String name, String type, Map extractedQueryParams); + GenericResultsetData retrieveGenericResultset(String name, String type, Map extractedQueryParams, boolean isSelfServiceUserReport); - String retrieveReportPDF(String name, String type, Map extractedQueryParams); + String retrieveReportPDF(String name, String type, Map extractedQueryParams, boolean isSelfServiceUserReport); - String getReportType(String reportName); + String getReportType(String reportName, boolean isSelfServiceUserReport); Collection retrieveReportList(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java index c732f0d1e..2d9d271fa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java @@ -90,15 +90,16 @@ public class ReadReportingServiceImpl implements ReadReportingService { } @Override - public StreamingOutput retrieveReportCSV(final String name, final String type, final Map queryParams) { + public StreamingOutput retrieveReportCSV(final String name, final String type, + final Map queryParams, final boolean isSelfServiceUserReport) { - return new StreamingOutput() { + return new StreamingOutput() { @Override public void write(final OutputStream out) { try { - final GenericResultsetData result = retrieveGenericResultset(name, type, queryParams); + final GenericResultsetData result = retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport); final StringBuffer sb = generateCsvFileBuffer(result); final InputStream in = new ByteArrayInputStream(sb.toString().getBytes("UTF-8")); @@ -171,12 +172,13 @@ public class ReadReportingServiceImpl implements ReadReportingService { } @Override - public GenericResultsetData retrieveGenericResultset(final String name, final String type, final Map queryParams) { + public GenericResultsetData retrieveGenericResultset(final String name, final String type, + final Map queryParams, final boolean isSelfServiceUserReport) { final long startTime = System.currentTimeMillis(); logger.info("STARTING REPORT: " + name + " Type: " + type); - final String sql = getSQLtoRun(name, type, queryParams); + final String sql = getSQLtoRun(name, type, queryParams, isSelfServiceUserReport); final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql); @@ -185,7 +187,8 @@ public class ReadReportingServiceImpl implements ReadReportingService { return result; } - private String getSQLtoRun(final String name, final String type, final Map queryParams) { + private String getSQLtoRun(final String name, final String type, final Map queryParams, + final boolean isSelfServiceUserReport) { String sql = getSql(name, type); @@ -203,18 +206,22 @@ public class ReadReportingServiceImpl implements ReadReportingService { // (typically used to return report lists containing only reports // permitted to be run by the user sql = this.genericDataService.replace(sql, "${currentUserId}", currentUser.getId().toString()); + + sql = this.genericDataService.replace(sql, "${isSelfServiceUser}", + Integer.toString(isSelfServiceUserReport ? 1 : 0)); - sql = this.genericDataService.wrapSQL(sql); + sql = this.genericDataService.wrapSQL(sql); - return sql; + return sql; - } + } private String getSql(final String name, final String type) { - final String inputSql = "select " + type + "_sql as the_sql from stretchy_" + type + " where " + type + "_name = '" + name + "'"; + final String inputSql = "select " + type + "_sql as the_sql from stretchy_" + type + " where " + type + "_name = '" + name + "'" ; final String inputSqlWrapped = this.genericDataService.wrapSQL(inputSql); + // the return statement contains the exact sql required final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(inputSqlWrapped); if (rs.next() && rs.getString("the_sql") != null) { return rs.getString("the_sql"); } @@ -222,21 +229,22 @@ public class ReadReportingServiceImpl implements ReadReportingService { } @Override - public String getReportType(final String reportName) { + public String getReportType(final String reportName, final boolean isSelfServiceUserReport) { - final String sql = "SELECT ifnull(report_type,'') as report_type FROM `stretchy_report` where report_name = '" + reportName + "'"; + final String sql = "SELECT ifnull(report_type,'') as report_type FROM `stretchy_report` where report_name = '" + reportName + "' and self_service_user_report = ?"; this.columnValidator.validateSqlInjection(sql, reportName); final String sqlWrapped = this.genericDataService.wrapSQL(sql); - final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped); + final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped, new Object [] {isSelfServiceUserReport}); if (rs.next()) { return rs.getString("report_type"); } throw new ReportNotFoundException(sql); } @Override - public String retrieveReportPDF(final String reportName, final String type, final Map queryParams) { + public String retrieveReportPDF(final String reportName, final String type, final Map queryParams, + final boolean isSelfServiceUserReport) { final String fileLocation = FileSystemContentRepository.FINERACT_BASE_DIR + File.separator + ""; if (!new File(fileLocation).isDirectory()) { @@ -246,7 +254,7 @@ public class ReadReportingServiceImpl implements ReadReportingService { final String genaratePdf = fileLocation + File.separator + reportName + ".pdf"; try { - final GenericResultsetData result = retrieveGenericResultset(reportName, type, queryParams); + final GenericResultsetData result = retrieveGenericResultset(reportName, type, queryParams, isSelfServiceUserReport); final List columnHeaders = result.getColumnHeaders(); final List data = result.getData(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java index 6e9f5e381..338303f04 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java @@ -422,7 +422,8 @@ public class ReportMailingJobWritePlatformServiceImpl implements ReportMailingJo final MultivaluedMap reportParams, final String reportName, final StringBuilder errorLog) { try { - final String reportType = this.readReportingService.getReportType(reportName); + final boolean isSelfServiceUserReport = false; + final String reportType = this.readReportingService.getReportType(reportName, isSelfServiceUserReport); final ReportingProcessService reportingProcessService = this.reportingProcessServiceProvider.findReportingProcessService(reportType); if (reportingProcessService != null) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/runreport/SelfRunReportApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/runreport/SelfRunReportApiResource.java index b86d176a6..941aff04f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/runreport/SelfRunReportApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/runreport/SelfRunReportApiResource.java @@ -54,7 +54,8 @@ public class SelfRunReportApiResource { @Produces({ MediaType.APPLICATION_JSON, "text/csv", "application/vnd.ms-excel", "application/pdf", "text/html" }) public Response runReport(@PathParam("reportName") final String reportName, @Context final UriInfo uriInfo) { this.context.authenticatedUser(); - return this.runreportsApiResource.runReport(reportName, uriInfo); + final boolean isSelfServiceUserReport = true; + return this.runreportsApiResource.runReport(reportName, uriInfo, isSelfServiceUserReport); } } diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V345__reports_for_self_service_user.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V345__reports_for_self_service_user.sql new file mode 100644 index 000000000..92c4283db --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V345__reports_for_self_service_user.sql @@ -0,0 +1,29 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you under the Apache License, Version 2.0 (the +-- "License"); you may not use this file except in compliance +-- with the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, +-- software distributed under the License is distributed on an +-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +-- KIND, either express or implied. See the License for the +-- specific language governing permissions and limitations +-- under the License. +-- + +ALTER TABLE `stretchy_report` + ADD COLUMN `self_service_user_report` TINYINT(1) NOT NULL DEFAULT '0' AFTER `use_report`; + + +UPDATE `stretchy_parameter` SET `parameter_sql`='select sp.parameter_name, sp.parameter_variable, sp.parameter_label, sp.parameter_displayType, \r sp.parameter_FormatType, sp.parameter_default, sp.selectOne, sp.selectAll, spp.parameter_name as parentParameterName\r from stretchy_parameter sp\r left join stretchy_parameter spp on spp.id = sp.parent_id\r where sp.special is null\r and exists \r (select \'f\' \r from stretchy_report sr\r join stretchy_report_parameter srp on srp.report_id = sr.id and sr.self_service_user_report = \'${isSelfServiceUser}\'\r where sr.report_name in(${reportListing})\r and srp.parameter_id = sp.id\r )\r order by sp.id' WHERE `parameter_name` = 'FullParameterList'; + +UPDATE `stretchy_parameter` SET `parameter_sql`='select r.id as report_id, r.report_name, r.report_type, r.report_subtype, r.report_category,\nrp.id as parameter_id, rp.report_parameter_name, p.parameter_name\n from stretchy_report r\n left join stretchy_report_parameter rp on rp.report_id = r.id \n left join stretchy_parameter p on p.id = rp.parameter_id\n where r.use_report is true and r.self_service_user_report = \'${isSelfServiceUser}\'\n and exists\n ( select \'f\'\n from m_appuser_role ur \n join m_role r on r.id = ur.role_id\n join m_role_permission rp on rp.role_id = r.id\n join m_permission p on p.id = rp.permission_id\n where ur.appuser_id = ${currentUserId}\n and (p.code in (\'ALL_FUNCTIONS_READ\', \'ALL_FUNCTIONS\') or p.code = concat("READ_", r.report_name)) )\n order by r.report_category, r.report_name, rp.id' WHERE `parameter_name` = 'FullReportList'; + +UPDATE `stretchy_parameter` SET `parameter_sql`='select r.id as report_id, r.report_name, r.report_type, r.report_subtype, r.report_category,\n rp.id as parameter_id, rp.report_parameter_name, p.parameter_name\n from stretchy_report r\n left join stretchy_report_parameter rp on rp.report_id = r.id\n left join stretchy_parameter p on p.id = rp.parameter_id\n where r.report_category = \'${reportCategory}\'\n and r.use_report is true and r.self_service_user_report = \'${isSelfServiceUser}\' \n and exists\n (select \'f\'\n from m_appuser_role ur \n join m_role r on r.id = ur.role_id\n join m_role_permission rp on rp.role_id = r.id\n join m_permission p on p.id = rp.permission_id\n where ur.appuser_id = ${currentUserId}\n and (p.code in (\'ALL_FUNCTIONS_READ\', \'ALL_FUNCTIONS\') or p.code = concat("READ_", r.report_name)) )\n order by r.report_category, r.report_name, rp.id' WHERE `parameter_name`='reportCategoryList'; +