Reports
The Axelor Open Platform integrates powerful BIRT reports for reporting.
Configuration
Following configuration settings should be provided via axelor-config.properties.
# The external report design directory
# ~~~~~
# this directory is searched for the rptdesign files
# (fallbacks to designs provided by modules)
reports.design-dir = {user.home}/axelor/reports
-
reports.design-dir- the external directory of design files and related resources
Directory Structure
The reports directory structure should be as follows:
-
reports- contains design files (.rptdesign) -
reports/lib- contains library files (.rptlibrary) -
reports/img- contains images -
reports/css- contains style sheets
The reports can also be provided from modules itself from the src/main/resources/reports
directory which follows same structure.
If reports.design-dir configuration is given, the resource locator first
searches design files and related resources from that directory and fallbacks
to the module provided resources.
Report Data Source
The reports should only use JDBC Data Source to create data sets for the report.
The Axelor Open Platform applications utilizes the internal hibernate connection to configure the report data sources.
Files storage
To support any file storage system (local, s3, …), a utility function has been created to deal with files included in reports (images, etc…).
Use dynamic images to allow blob images to be inserted into the report. With the onCreate event script, set the image data.
var imgData = com.axelor.report.tool.ImageTool.getImageBytes(row["<my_file_id>"], true);
this.altText = "";
if (imgData != null) {
this.data = imgData;
}
com.axelor.report.tool.ImageTool.getImageBytes(Long metaFileId, boolean cache) accept as first
parameter the id of the MetaFile and as second parameter whether to cache the file (see data.object-storage.cache.*
properties).
Action Report
Reports can be executed using a special xml action action-report.
The <action-report> is used to define the action reports.
| Name | Description |
|---|---|
|
name of the action |
|
the design file name (.rptdesign file) |
|
name of the output file (can use |
|
output format: |
|
boolean, whether to attach the generated report to current object |
The action report can have the following elements:
-
<param>- the report parameter-
name- name of the parameter -
expr- expression to provide parameter value -
if- condition to check before preparing the value
-
example:
<action-report name="action-report-invoice"
design="invoice.rptdesign"
output="invoice-${date}${time}"
format="pdf">
<param name="some" expr="eval: expression" if="confirmed"/>
<param name="thing" expr="constant" />
</action-report>
The action-report must be the last action in the group of actions. For example:
<button ... onClick="action-report-invoice" /> (1)
<button ... onClick="save,action-some,action-report-invoice" /> (2)
<button ... onClick="save,action-some,action-report-invoice,action-another" /> (3)
| 1 | valid, only one action |
| 2 | valid, action-report is last action |
| 3 | invalid, action-report is not last action |
Integration
The Axelor Open Platform provides easy to use API to handle report generation.
-
com.axelor.report.ReportGenerator- provides methods to generate reports -
com.axelor.report.ReportEngineProvider- provides a preconfigured singleton instance ofIReportEngine
You can generate reports from your code like this:
public class PrintService {
@Inject private ReportGenerator generator;
public void print(String design, String format, Map<String, Object> params) throws IOException, BirtException {
try (OutputStream output = new FileOutputStream(...)) {
generator.generate(output, design, format, params, Locale.getDefault());
}
}
}
To customize the generated reports, use com.axelor.report.ReportConfigurer. It is called for
every report generated by ReportGenerator:
-
validate(report, params)- called once the report design is opened, throw an exception to abort the report generation -
configure(task, options, params)- called before the report is run, to configure the task (timezone, render options…)
@Singleton
public class MyReportConfigurer extends ReportConfigurerImpl {
@Override
public void configure(IRunAndRenderTask task, IRenderOption options, Map<String, Object> params) {
task.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
}
}
public class MyModule extends AxelorModule {
@Override
protected void configure() {
bind(ReportConfigurerImpl.class).to(MyReportConfigurer.class);
}
}
You can also work with IReportEngine instance directly (not recommended though) like this:
public class PrintService {
@Inject private IReportEngine engine;
public void print(String design, Map<String, Object> params) throws IOException {
IResourceLocator locator = engine.getConfig().getResourceLocator();
URL found = locator.findResource(null, design, IResourceLocator.OTHERS);
if (found == null) {
return;
}
// TODO: open the design
// TODO: create render tasks
// TODO: provide task options
// TODO: run the task
}
}