<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.idempiere.org/w-en/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mbozem</id>
	<title>iDempiere en - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.idempiere.org/w-en/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mbozem"/>
	<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/en/Special:Contributions/Mbozem"/>
	<updated>2026-09-30T22:14:03Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.2</generator>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=NF14_Extensible_Report_Content_Generation&amp;diff=24045</id>
		<title>NF14 Extensible Report Content Generation</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=NF14_Extensible_Report_Content_Generation&amp;diff=24045"/>
		<updated>2026-09-12T20:48:52Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IDEMPIERE-7070 introduces UI-independent and report-engine-independent extension points for generating and post-processing report content.&lt;br /&gt;
&lt;br /&gt;
The feature allows plug-ins to provide alternative report renderers and to enhance generated documents without depending on the ZK report viewer or modifying iDempiere core classes.&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
Before this change, the report extension mechanism was located in the ZK report viewer and exposed ZK- and Jasper-specific types.&lt;br /&gt;
&lt;br /&gt;
This caused several limitations:&lt;br /&gt;
&lt;br /&gt;
* The extension was not available to headless or server-side consumers.&lt;br /&gt;
* Report-generation paths calling &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; directly bypassed plug-in-provided output.&lt;br /&gt;
* The mechanism was effectively tied to Jasper Reports.&lt;br /&gt;
* Multiple independent document enhancements could not be combined cleanly because only one renderer could handle the request.&lt;br /&gt;
* Preview and download names could expose the random suffix of an internal temporary Jasper file.&lt;br /&gt;
&lt;br /&gt;
A report may need several consecutive operations, for example:&lt;br /&gt;
&lt;br /&gt;
# Generate the initial PDF.&lt;br /&gt;
# Add ZUGFeRD/Factur-X data.&lt;br /&gt;
# Convert the document to PDF/A.&lt;br /&gt;
# Add PDF attachments.&lt;br /&gt;
# Add a watermark.&lt;br /&gt;
# Digitally sign or encrypt the final document.&lt;br /&gt;
&lt;br /&gt;
IDEMPIERE-7070 separates initial report rendering from subsequent content processing, allowing these operations to be implemented by independent OSGi services and executed in a defined order.&lt;br /&gt;
&lt;br /&gt;
== Architecture ==&lt;br /&gt;
&lt;br /&gt;
Report content generation consists of two extension stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ReportContentRequest&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
ranked IReportContentRendererFactory services&lt;br /&gt;
        |&lt;br /&gt;
        +-- first applicable renderer&lt;br /&gt;
        |&lt;br /&gt;
        +-- ReportEngine fallback&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
initial report content&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
all applicable IReportContentProcessor services&lt;br /&gt;
in descending OSGi service-ranking order&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
final processed content&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Report content request ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ReportContentRequest&amp;lt;/code&amp;gt; is the common input for rendering and post-processing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public record ReportContentRequest(&lt;br /&gt;
        ReportEngine reportEngine,&lt;br /&gt;
        ProcessInfo processInfo,&lt;br /&gt;
        String title,&lt;br /&gt;
        boolean applyPostProcessing) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The three-argument constructor enables post-processing by default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ReportContentRequest request =&lt;br /&gt;
        new ReportContentRequest(reportEngine, processInfo, reportTitle);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Post-processing can be disabled explicitly when required:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ReportContentRequest request =&lt;br /&gt;
        new ReportContentRequest(reportEngine, processInfo, reportTitle, false);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The report context preserves the relevant process information, including:&lt;br /&gt;
&lt;br /&gt;
* process parameters&lt;br /&gt;
* record ID and record UUID&lt;br /&gt;
* record selections&lt;br /&gt;
* language&lt;br /&gt;
* user and tenant context&lt;br /&gt;
* transaction context&lt;br /&gt;
* requested PDF filename&lt;br /&gt;
&lt;br /&gt;
=== Stage 1: report content rendering ===&lt;br /&gt;
&lt;br /&gt;
A plug-in can register an &amp;lt;code&amp;gt;IReportContentRendererFactory&amp;lt;/code&amp;gt; OSGi service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentRendererFactory {&lt;br /&gt;
    IReportContentRenderer createRenderer(ReportContentRequest request);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Factories are evaluated in descending OSGi &amp;lt;code&amp;gt;service.ranking&amp;lt;/code&amp;gt; order. A factory returns:&lt;br /&gt;
&lt;br /&gt;
* an &amp;lt;code&amp;gt;IReportContentRenderer&amp;lt;/code&amp;gt; when it supports the request, or&lt;br /&gt;
* &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; when it is not applicable.&lt;br /&gt;
&lt;br /&gt;
The first applicable renderer is selected.&lt;br /&gt;
&lt;br /&gt;
A renderer implements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentRenderer {&lt;br /&gt;
    File getContent(String contentType, String fileExtension);&lt;br /&gt;
&lt;br /&gt;
    ReportContentType[] getSupportedContentTypes();&lt;br /&gt;
&lt;br /&gt;
    default int getRowCount() {&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Supported output types are described without any dependency on a UI toolkit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public record ReportContentType(&lt;br /&gt;
        String name,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        String contentType) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example Declarative Services registration is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@Component(&lt;br /&gt;
    service = IReportContentRendererFactory.class,&lt;br /&gt;
    immediate = true,&lt;br /&gt;
    property = &amp;quot;service.ranking:Integer=100&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
public class CustomReportContentRendererFactory&lt;br /&gt;
        implements IReportContentRendererFactory {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public IReportContentRenderer createRenderer(&lt;br /&gt;
            ReportContentRequest request) {&lt;br /&gt;
        if (!supports(request))&lt;br /&gt;
            return null;&lt;br /&gt;
&lt;br /&gt;
        return new CustomReportContentRenderer(request);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The default Jasper renderer is supplied by the &amp;lt;code&amp;gt;org.adempiere.report.jasper&amp;lt;/code&amp;gt; bundle with service ranking &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. It is one provider of the generic API; the extension point is not limited to Jasper Reports.&lt;br /&gt;
&lt;br /&gt;
The Jasper renderer supports the existing report viewer formats, including:&lt;br /&gt;
&lt;br /&gt;
* PDF&lt;br /&gt;
* HTML&lt;br /&gt;
* CSV&lt;br /&gt;
* XLS&lt;br /&gt;
* XLSX&lt;br /&gt;
* SSV&lt;br /&gt;
&lt;br /&gt;
If no registered factory handles the request, Core uses the standard &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; as a fallback. The fallback supports PDF, HTML, CSV, XLS and XLSX.&lt;br /&gt;
&lt;br /&gt;
=== Stage 2: report content post-processing ===&lt;br /&gt;
&lt;br /&gt;
After rendering, the generated content is passed through every applicable &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt; service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentProcessor {&lt;br /&gt;
&lt;br /&gt;
    boolean isApplicable(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension);&lt;br /&gt;
&lt;br /&gt;
    File process(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension,&lt;br /&gt;
            File input);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Processors are executed in descending OSGi &amp;lt;code&amp;gt;service.ranking&amp;lt;/code&amp;gt; order. Each processor receives the result of the previous processor.&lt;br /&gt;
&lt;br /&gt;
A processor may:&lt;br /&gt;
&lt;br /&gt;
* modify the input file and return the same file, or&lt;br /&gt;
* create and return a new file.&lt;br /&gt;
&lt;br /&gt;
It must not return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. A null result is treated as an error.&lt;br /&gt;
&lt;br /&gt;
Typical processor use cases include:&lt;br /&gt;
&lt;br /&gt;
* ZUGFeRD/Factur-X data&lt;br /&gt;
* PDF/A conversion&lt;br /&gt;
* PDF attachments&lt;br /&gt;
* watermarks&lt;br /&gt;
* digital signatures&lt;br /&gt;
* encryption&lt;br /&gt;
* other document transformations&lt;br /&gt;
&lt;br /&gt;
An example registration is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@Component(&lt;br /&gt;
    service = IReportContentProcessor.class,&lt;br /&gt;
    immediate = true,&lt;br /&gt;
    property = &amp;quot;service.ranking:Integer=100&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
public class CustomPDFProcessor&lt;br /&gt;
        implements IReportContentProcessor {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public boolean isApplicable(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension) {&lt;br /&gt;
        return &amp;quot;application/pdf&amp;quot;.equals(contentType)&lt;br /&gt;
                || &amp;quot;pdf&amp;quot;.equalsIgnoreCase(fileExtension);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public File process(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension,&lt;br /&gt;
            File input) {&lt;br /&gt;
        // Modify input or create a new output file.&lt;br /&gt;
        return input;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The processor chain is applied both to custom renderer output and to content generated by the standard &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; fallback.&lt;br /&gt;
&lt;br /&gt;
== Central Core API ==&lt;br /&gt;
&lt;br /&gt;
The common API is provided through &amp;lt;code&amp;gt;org.adempiere.base.Core&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The main entry points are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
IReportContentRenderer getReportContentRenderer(&lt;br /&gt;
        ReportContentRequest request);&lt;br /&gt;
&lt;br /&gt;
File getReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension);&lt;br /&gt;
&lt;br /&gt;
File getReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        File outputFile);&lt;br /&gt;
&lt;br /&gt;
File processReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        File content);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When an output file is supplied, only the final processed content is copied to that target. Intermediate renderer or processor files are not copied to it.&lt;br /&gt;
&lt;br /&gt;
Failures during report generation are propagated instead of silently returning an incomplete result.&lt;br /&gt;
&lt;br /&gt;
== Report-generation paths using the API ==&lt;br /&gt;
&lt;br /&gt;
The pull request routes the following report-generation paths through the common API:&lt;br /&gt;
&lt;br /&gt;
* ZK report preview and export&lt;br /&gt;
* the legacy direct Jasper viewer&lt;br /&gt;
* report email attachments&lt;br /&gt;
* report archiving&lt;br /&gt;
* workflow reports&lt;br /&gt;
* server-side exports&lt;br /&gt;
* document PDF generation&lt;br /&gt;
* batch-print processes&lt;br /&gt;
* payment and remittance printing&lt;br /&gt;
* dunning and invoice printing&lt;br /&gt;
* relevant ZK report and print processes&lt;br /&gt;
&lt;br /&gt;
Document generation for orders, invoices, shipments, distribution orders and RFQ responses is included in the affected server-side paths.&lt;br /&gt;
&lt;br /&gt;
The existing direct-printer path remains unchanged.&lt;br /&gt;
&lt;br /&gt;
== Report viewer behavior ==&lt;br /&gt;
&lt;br /&gt;
The ZK report viewer uses the common content renderer and processor chain.&lt;br /&gt;
&lt;br /&gt;
Processed content is cached for each combination of MIME type and file extension. Preview, export, email and archive actions can therefore reuse the same final result.&lt;br /&gt;
&lt;br /&gt;
This is important for processors such as:&lt;br /&gt;
&lt;br /&gt;
* document signing&lt;br /&gt;
* attachment generation&lt;br /&gt;
* ZUGFeRD/Factur-X generation&lt;br /&gt;
* PDF/A conversion&lt;br /&gt;
&lt;br /&gt;
Without caching, such processors could be executed more than once for the same viewer output.&lt;br /&gt;
&lt;br /&gt;
The report viewer also:&lt;br /&gt;
&lt;br /&gt;
* shows only formats supported by the active renderer or report engine;&lt;br /&gt;
* preserves the original &amp;lt;code&amp;gt;ProcessInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
* preserves record UUIDs;&lt;br /&gt;
* refreshes native print data after changing the print format;&lt;br /&gt;
* uses the logical report name for preview and download media;&lt;br /&gt;
* retains unique filenames for internal temporary files.&lt;br /&gt;
&lt;br /&gt;
Consequently, users no longer see the random suffix generated for an internal Jasper temporary file in the preview or download filename.&lt;br /&gt;
&lt;br /&gt;
== Compatibility ==&lt;br /&gt;
&lt;br /&gt;
* Existing Jasper reports are handled by the default renderer in the Jasper bundle.&lt;br /&gt;
* The API is not limited to Jasper Reports.&lt;br /&gt;
* Other report engines can register their own renderer factory.&lt;br /&gt;
* Requests without a matching renderer fall back to &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Installations without an &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt; receive the original generated content unchanged.&lt;br /&gt;
* Headless installations can use the renderer and processor APIs without the ZK bundle.&lt;br /&gt;
* Existing report viewer export formats are retained.&lt;br /&gt;
* Legacy &amp;lt;code&amp;gt;ZkJRViewer&amp;lt;/code&amp;gt; adapters are retained.&lt;br /&gt;
* Direct-printer behavior is unchanged.&lt;br /&gt;
* No database migration is required.&lt;br /&gt;
&lt;br /&gt;
== Example use cases ==&lt;br /&gt;
&lt;br /&gt;
=== ZUGFeRD/Factur-X ===&lt;br /&gt;
&lt;br /&gt;
A processor can embed ZUGFeRD/Factur-X XML into a PDF produced by any renderer.&lt;br /&gt;
&lt;br /&gt;
This integrates electronic invoice generation into the normal workflow:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Print button&lt;br /&gt;
    -&amp;gt; Preview&lt;br /&gt;
    -&amp;gt; E-mail&lt;br /&gt;
    -&amp;gt; Archive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The same processor can be applied to a PDF produced by:&lt;br /&gt;
&lt;br /&gt;
* the native report engine&lt;br /&gt;
* Jasper Reports&lt;br /&gt;
* a FreeMarker-based renderer&lt;br /&gt;
* another custom report engine&lt;br /&gt;
&lt;br /&gt;
This complements the existing [[Plugin:_ZUGFeRDXInvoice|ZUGFeRD/XInvoice plug-in]] by allowing its result to participate in the standard report workflow.&lt;br /&gt;
&lt;br /&gt;
=== PDF attachments ===&lt;br /&gt;
&lt;br /&gt;
A processor can attach additional PDF documents to the generated report. For example, a drawing attached to an order can be included in the final order PDF.&lt;br /&gt;
&lt;br /&gt;
=== Alternative report engines ===&lt;br /&gt;
&lt;br /&gt;
The API was tested conceptually and practically with a FreeMarker-based print-template engine. Such a plug-in can examine the print format in &amp;lt;code&amp;gt;ReportContentRequest&amp;lt;/code&amp;gt;, return its own renderer and use the same post-processing pipeline as Jasper or the native report engine.&lt;br /&gt;
&lt;br /&gt;
== Validation ==&lt;br /&gt;
&lt;br /&gt;
=== Automated validation ===&lt;br /&gt;
&lt;br /&gt;
The pull request contains integration coverage for:&lt;br /&gt;
&lt;br /&gt;
* renderer selection by OSGi service ranking&lt;br /&gt;
* fallback to the next renderer factory&lt;br /&gt;
* fallback to the standard report engine&lt;br /&gt;
* default Jasper renderer registration&lt;br /&gt;
* supported output formats&lt;br /&gt;
* processor ordering&lt;br /&gt;
* processor applicability&lt;br /&gt;
* invalid null processor results&lt;br /&gt;
* enabling and disabling post-processing&lt;br /&gt;
* processed-content caching&lt;br /&gt;
* propagation of report-generation failures&lt;br /&gt;
&lt;br /&gt;
After fixes made during review, the reported validation results were:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ServerReportCtlTest&amp;lt;/code&amp;gt;: 1 test, 0 failures, 0 errors&lt;br /&gt;
* &amp;lt;code&amp;gt;ReportViewerContentRendererFactoryTest&amp;lt;/code&amp;gt;: 9 tests, 0 failures, 0 errors&lt;br /&gt;
* complete 63-module Maven &amp;lt;code&amp;gt;verify&amp;lt;/code&amp;gt; reactor: '''BUILD SUCCESS'''&lt;br /&gt;
* &amp;lt;code&amp;gt;git diff --check&amp;lt;/code&amp;gt;: passed&lt;br /&gt;
&lt;br /&gt;
Earlier failures concerning the requested PDF filename and missing server-side print data were fixed before the successful final validation.&lt;br /&gt;
&lt;br /&gt;
=== Integration testing ===&lt;br /&gt;
&lt;br /&gt;
The reported integration-test environment was:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| iDempiere&lt;br /&gt;
| Version 14 development workspace&lt;br /&gt;
|-&lt;br /&gt;
| Java&lt;br /&gt;
| OpenJDK 17.0.19&lt;br /&gt;
|-&lt;br /&gt;
| Runtime&lt;br /&gt;
| Eclipse PDE&lt;br /&gt;
|-&lt;br /&gt;
| Database&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
|-&lt;br /&gt;
| Database migration level&lt;br /&gt;
| &amp;lt;code&amp;gt;202607261257_IDEMPIERE-6552.sql&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ZK&lt;br /&gt;
| 10.3.0.1&lt;br /&gt;
|-&lt;br /&gt;
| Locale&lt;br /&gt;
| &amp;lt;code&amp;gt;de_DE&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The integration test used:&lt;br /&gt;
&lt;br /&gt;
* a FreeMarker report renderer;&lt;br /&gt;
* a ZUGFeRD/Factur-X content processor;&lt;br /&gt;
* invoice &amp;lt;code&amp;gt;C_Invoice_ID=103&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The following flow was tested:&lt;br /&gt;
&lt;br /&gt;
# Generate an invoice using the normal Print button.&lt;br /&gt;
# Render the invoice through the new report content pipeline.&lt;br /&gt;
# Post-process the generated PDF through &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Embed Factur-X/ZUGFeRD XML into the PDF.&lt;br /&gt;
# Combine the FreeMarker renderer with the ZUGFeRD processor.&lt;br /&gt;
&lt;br /&gt;
Reported result:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Invoice_Header.pdf&amp;lt;/code&amp;gt; was generated successfully.&lt;br /&gt;
* The PDF contained an embedded &amp;lt;code&amp;gt;factur-x.xml&amp;lt;/code&amp;gt; attachment.&lt;br /&gt;
* The embedded XML was 9,711 bytes and began with a valid &amp;lt;code&amp;gt;CrossIndustryInvoice&amp;lt;/code&amp;gt; document.&lt;br /&gt;
* The ZUGFeRD processor worked independently of the selected renderer.&lt;br /&gt;
* No remaining ZUGFeRD, PostgreSQL or PDFBox errors were reported in the server log after the final test.&lt;br /&gt;
&lt;br /&gt;
Further review and broader testing of the affected report-generation paths and representative processor combinations are still recommended before merge.&lt;br /&gt;
&lt;br /&gt;
== Known limitation ==&lt;br /&gt;
&lt;br /&gt;
Multi-record Jasper report handling is intentionally outside the scope of this change and is tracked separately in [https://idempiere.atlassian.net/browse/IDEMPIERE-7073 IDEMPIERE-7073].&lt;br /&gt;
&lt;br /&gt;
== Related work and examples ==&lt;br /&gt;
&lt;br /&gt;
* [https://idempiere.atlassian.net/browse/IDEMPIERE-7070 IDEMPIERE-7070 – Make report content generation extensible through OSGi]&lt;br /&gt;
* [https://github.com/idempiere/idempiere/pull/3318 Pull request #3318]&lt;br /&gt;
* [https://idempiere.atlassian.net/browse/IDEMPIERE-6069 IDEMPIERE-6069 – related composable report-processing requirement]&lt;br /&gt;
* [[Plugin:_ZUGFeRDXInvoice|ZUGFeRD/XInvoice plug-in]]&lt;br /&gt;
* [https://github.com/mbozem/idempiere-print-template/tree/IDEMPIERE-7070 FreeMarker renderer integration branch]&lt;br /&gt;
* [https://github.com/mbozem/auler.gmbh.zugferdxinvoice/tree/agent/idempiere-14-compatibility ZUGFeRD/Factur-X integration branch]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:New Features]]&lt;br /&gt;
[[Category:New Features w14]]&lt;br /&gt;
[[Category:New Features Development]]&lt;br /&gt;
[[Category:New Features Technical]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=NF14_Extensible_Report_Content_Generation&amp;diff=24044</id>
		<title>NF14 Extensible Report Content Generation</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=NF14_Extensible_Report_Content_Generation&amp;diff=24044"/>
		<updated>2026-09-12T20:46:38Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IDEMPIERE-7070 introduces UI-independent and report-engine-independent extension points for generating and post-processing report content.&lt;br /&gt;
&lt;br /&gt;
The feature allows plug-ins to provide alternative report renderers and to enhance generated documents without depending on the ZK report viewer or modifying iDempiere core classes.&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
Before this change, the report extension mechanism was located in the ZK report viewer and exposed ZK- and Jasper-specific types.&lt;br /&gt;
&lt;br /&gt;
This caused several limitations:&lt;br /&gt;
&lt;br /&gt;
* The extension was not available to headless or server-side consumers.&lt;br /&gt;
* Report-generation paths calling &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; directly bypassed plug-in-provided output.&lt;br /&gt;
* The mechanism was effectively tied to Jasper Reports.&lt;br /&gt;
* Multiple independent document enhancements could not be combined cleanly because only one renderer could handle the request.&lt;br /&gt;
* Preview and download names could expose the random suffix of an internal temporary Jasper file.&lt;br /&gt;
&lt;br /&gt;
A report may need several consecutive operations, for example:&lt;br /&gt;
&lt;br /&gt;
# Generate the initial PDF.&lt;br /&gt;
# Add ZUGFeRD/Factur-X data.&lt;br /&gt;
# Convert the document to PDF/A.&lt;br /&gt;
# Add PDF attachments.&lt;br /&gt;
# Add a watermark.&lt;br /&gt;
# Digitally sign or encrypt the final document.&lt;br /&gt;
&lt;br /&gt;
IDEMPIERE-7070 separates initial report rendering from subsequent content processing, allowing these operations to be implemented by independent OSGi services and executed in a defined order.&lt;br /&gt;
&lt;br /&gt;
== Architecture ==&lt;br /&gt;
&lt;br /&gt;
Report content generation consists of two extension stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ReportContentRequest&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
ranked IReportContentRendererFactory services&lt;br /&gt;
        |&lt;br /&gt;
        +-- first applicable renderer&lt;br /&gt;
        |&lt;br /&gt;
        +-- ReportEngine fallback&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
initial report content&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
all applicable IReportContentProcessor services&lt;br /&gt;
in descending OSGi service-ranking order&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
final processed content&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Report content request ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ReportContentRequest&amp;lt;/code&amp;gt; is the common input for rendering and post-processing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public record ReportContentRequest(&lt;br /&gt;
        ReportEngine reportEngine,&lt;br /&gt;
        ProcessInfo processInfo,&lt;br /&gt;
        String title,&lt;br /&gt;
        boolean applyPostProcessing) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The three-argument constructor enables post-processing by default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ReportContentRequest request =&lt;br /&gt;
        new ReportContentRequest(reportEngine, processInfo, reportTitle);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Post-processing can be disabled explicitly when required:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ReportContentRequest request =&lt;br /&gt;
        new ReportContentRequest(reportEngine, processInfo, reportTitle, false);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The report context preserves the relevant process information, including:&lt;br /&gt;
&lt;br /&gt;
* process parameters&lt;br /&gt;
* record ID and record UUID&lt;br /&gt;
* record selections&lt;br /&gt;
* language&lt;br /&gt;
* user and tenant context&lt;br /&gt;
* transaction context&lt;br /&gt;
* requested PDF filename&lt;br /&gt;
&lt;br /&gt;
=== Stage 1: report content rendering ===&lt;br /&gt;
&lt;br /&gt;
A plug-in can register an &amp;lt;code&amp;gt;IReportContentRendererFactory&amp;lt;/code&amp;gt; OSGi service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentRendererFactory {&lt;br /&gt;
    IReportContentRenderer createRenderer(ReportContentRequest request);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Factories are evaluated in descending OSGi &amp;lt;code&amp;gt;service.ranking&amp;lt;/code&amp;gt; order. A factory returns:&lt;br /&gt;
&lt;br /&gt;
* an &amp;lt;code&amp;gt;IReportContentRenderer&amp;lt;/code&amp;gt; when it supports the request, or&lt;br /&gt;
* &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; when it is not applicable.&lt;br /&gt;
&lt;br /&gt;
The first applicable renderer is selected.&lt;br /&gt;
&lt;br /&gt;
A renderer implements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentRenderer {&lt;br /&gt;
    File getContent(String contentType, String fileExtension);&lt;br /&gt;
&lt;br /&gt;
    ReportContentType[] getSupportedContentTypes();&lt;br /&gt;
&lt;br /&gt;
    default int getRowCount() {&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Supported output types are described without any dependency on a UI toolkit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public record ReportContentType(&lt;br /&gt;
        String name,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        String contentType) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example Declarative Services registration is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@Component(&lt;br /&gt;
    service = IReportContentRendererFactory.class,&lt;br /&gt;
    immediate = true,&lt;br /&gt;
    property = &amp;quot;service.ranking:Integer=100&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
public class CustomReportContentRendererFactory&lt;br /&gt;
        implements IReportContentRendererFactory {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public IReportContentRenderer createRenderer(&lt;br /&gt;
            ReportContentRequest request) {&lt;br /&gt;
        if (!supports(request))&lt;br /&gt;
            return null;&lt;br /&gt;
&lt;br /&gt;
        return new CustomReportContentRenderer(request);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The default Jasper renderer is supplied by the &amp;lt;code&amp;gt;org.adempiere.report.jasper&amp;lt;/code&amp;gt; bundle with service ranking &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. It is one provider of the generic API; the extension point is not limited to Jasper Reports.&lt;br /&gt;
&lt;br /&gt;
The Jasper renderer supports the existing report viewer formats, including:&lt;br /&gt;
&lt;br /&gt;
* PDF&lt;br /&gt;
* HTML&lt;br /&gt;
* CSV&lt;br /&gt;
* XLS&lt;br /&gt;
* XLSX&lt;br /&gt;
* SSV&lt;br /&gt;
&lt;br /&gt;
If no registered factory handles the request, Core uses the standard &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; as a fallback. The fallback supports PDF, HTML, CSV, XLS and XLSX.&lt;br /&gt;
&lt;br /&gt;
=== Stage 2: report content post-processing ===&lt;br /&gt;
&lt;br /&gt;
After rendering, the generated content is passed through every applicable &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt; service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentProcessor {&lt;br /&gt;
&lt;br /&gt;
    boolean isApplicable(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension);&lt;br /&gt;
&lt;br /&gt;
    File process(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension,&lt;br /&gt;
            File input);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Processors are executed in descending OSGi &amp;lt;code&amp;gt;service.ranking&amp;lt;/code&amp;gt; order. Each processor receives the result of the previous processor.&lt;br /&gt;
&lt;br /&gt;
A processor may:&lt;br /&gt;
&lt;br /&gt;
* modify the input file and return the same file, or&lt;br /&gt;
* create and return a new file.&lt;br /&gt;
&lt;br /&gt;
It must not return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. A null result is treated as an error.&lt;br /&gt;
&lt;br /&gt;
Typical processor use cases include:&lt;br /&gt;
&lt;br /&gt;
* ZUGFeRD/Factur-X data&lt;br /&gt;
* PDF/A conversion&lt;br /&gt;
* PDF attachments&lt;br /&gt;
* watermarks&lt;br /&gt;
* digital signatures&lt;br /&gt;
* encryption&lt;br /&gt;
* other document transformations&lt;br /&gt;
&lt;br /&gt;
An example registration is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@Component(&lt;br /&gt;
    service = IReportContentProcessor.class,&lt;br /&gt;
    immediate = true,&lt;br /&gt;
    property = &amp;quot;service.ranking:Integer=100&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
public class CustomPDFProcessor&lt;br /&gt;
        implements IReportContentProcessor {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public boolean isApplicable(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension) {&lt;br /&gt;
        return &amp;quot;application/pdf&amp;quot;.equals(contentType)&lt;br /&gt;
                || &amp;quot;pdf&amp;quot;.equalsIgnoreCase(fileExtension);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public File process(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension,&lt;br /&gt;
            File input) {&lt;br /&gt;
        // Modify input or create a new output file.&lt;br /&gt;
        return input;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The processor chain is applied both to custom renderer output and to content generated by the standard &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; fallback.&lt;br /&gt;
&lt;br /&gt;
== Central Core API ==&lt;br /&gt;
&lt;br /&gt;
The common API is provided through &amp;lt;code&amp;gt;org.adempiere.base.Core&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The main entry points are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
IReportContentRenderer getReportContentRenderer(&lt;br /&gt;
        ReportContentRequest request);&lt;br /&gt;
&lt;br /&gt;
File getReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension);&lt;br /&gt;
&lt;br /&gt;
File getReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        File outputFile);&lt;br /&gt;
&lt;br /&gt;
File processReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        File content);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When an output file is supplied, only the final processed content is copied to that target. Intermediate renderer or processor files are not copied to it.&lt;br /&gt;
&lt;br /&gt;
Failures during report generation are propagated instead of silently returning an incomplete result.&lt;br /&gt;
&lt;br /&gt;
== Report-generation paths using the API ==&lt;br /&gt;
&lt;br /&gt;
The pull request routes the following report-generation paths through the common API:&lt;br /&gt;
&lt;br /&gt;
* ZK report preview and export&lt;br /&gt;
* the legacy direct Jasper viewer&lt;br /&gt;
* report email attachments&lt;br /&gt;
* report archiving&lt;br /&gt;
* workflow reports&lt;br /&gt;
* server-side exports&lt;br /&gt;
* document PDF generation&lt;br /&gt;
* batch-print processes&lt;br /&gt;
* payment and remittance printing&lt;br /&gt;
* dunning and invoice printing&lt;br /&gt;
* relevant ZK report and print processes&lt;br /&gt;
&lt;br /&gt;
Document generation for orders, invoices, shipments, distribution orders and RFQ responses is included in the affected server-side paths.&lt;br /&gt;
&lt;br /&gt;
The existing direct-printer path remains unchanged.&lt;br /&gt;
&lt;br /&gt;
== Report viewer behavior ==&lt;br /&gt;
&lt;br /&gt;
The ZK report viewer uses the common content renderer and processor chain.&lt;br /&gt;
&lt;br /&gt;
Processed content is cached for each combination of MIME type and file extension. Preview, export, email and archive actions can therefore reuse the same final result.&lt;br /&gt;
&lt;br /&gt;
This is important for processors such as:&lt;br /&gt;
&lt;br /&gt;
* document signing&lt;br /&gt;
* attachment generation&lt;br /&gt;
* ZUGFeRD/Factur-X generation&lt;br /&gt;
* PDF/A conversion&lt;br /&gt;
&lt;br /&gt;
Without caching, such processors could be executed more than once for the same viewer output.&lt;br /&gt;
&lt;br /&gt;
The report viewer also:&lt;br /&gt;
&lt;br /&gt;
* shows only formats supported by the active renderer or report engine;&lt;br /&gt;
* preserves the original &amp;lt;code&amp;gt;ProcessInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
* preserves record UUIDs;&lt;br /&gt;
* refreshes native print data after changing the print format;&lt;br /&gt;
* uses the logical report name for preview and download media;&lt;br /&gt;
* retains unique filenames for internal temporary files.&lt;br /&gt;
&lt;br /&gt;
Consequently, users no longer see the random suffix generated for an internal Jasper temporary file in the preview or download filename.&lt;br /&gt;
&lt;br /&gt;
== Compatibility ==&lt;br /&gt;
&lt;br /&gt;
* Existing Jasper reports are handled by the default renderer in the Jasper bundle.&lt;br /&gt;
* The API is not limited to Jasper Reports.&lt;br /&gt;
* Other report engines can register their own renderer factory.&lt;br /&gt;
* Requests without a matching renderer fall back to &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Installations without an &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt; receive the original generated content unchanged.&lt;br /&gt;
* Headless installations can use the renderer and processor APIs without the ZK bundle.&lt;br /&gt;
* Existing report viewer export formats are retained.&lt;br /&gt;
* Legacy &amp;lt;code&amp;gt;ZkJRViewer&amp;lt;/code&amp;gt; adapters are retained.&lt;br /&gt;
* Direct-printer behavior is unchanged.&lt;br /&gt;
* No database migration is required.&lt;br /&gt;
&lt;br /&gt;
== Example use cases ==&lt;br /&gt;
&lt;br /&gt;
=== ZUGFeRD/Factur-X ===&lt;br /&gt;
&lt;br /&gt;
A processor can embed ZUGFeRD/Factur-X XML into a PDF produced by any renderer.&lt;br /&gt;
&lt;br /&gt;
This integrates electronic invoice generation into the normal workflow:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Print button&lt;br /&gt;
    -&amp;gt; Preview&lt;br /&gt;
    -&amp;gt; E-mail&lt;br /&gt;
    -&amp;gt; Archive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The same processor can be applied to a PDF produced by:&lt;br /&gt;
&lt;br /&gt;
* the native report engine&lt;br /&gt;
* Jasper Reports&lt;br /&gt;
* a FreeMarker-based renderer&lt;br /&gt;
* another custom report engine&lt;br /&gt;
&lt;br /&gt;
This complements the existing [[Plugin:_ZUGFeRDXInvoice|ZUGFeRD/XInvoice plug-in]] by allowing its result to participate in the standard report workflow.&lt;br /&gt;
&lt;br /&gt;
=== PDF attachments ===&lt;br /&gt;
&lt;br /&gt;
A processor can attach additional PDF documents to the generated report. For example, a drawing attached to an order can be included in the final order PDF.&lt;br /&gt;
&lt;br /&gt;
=== Alternative report engines ===&lt;br /&gt;
&lt;br /&gt;
The API was tested conceptually and practically with a FreeMarker-based print-template engine. Such a plug-in can examine the print format in &amp;lt;code&amp;gt;ReportContentRequest&amp;lt;/code&amp;gt;, return its own renderer and use the same post-processing pipeline as Jasper or the native report engine.&lt;br /&gt;
&lt;br /&gt;
== Validation ==&lt;br /&gt;
&lt;br /&gt;
=== Automated validation ===&lt;br /&gt;
&lt;br /&gt;
The pull request contains integration coverage for:&lt;br /&gt;
&lt;br /&gt;
* renderer selection by OSGi service ranking&lt;br /&gt;
* fallback to the next renderer factory&lt;br /&gt;
* fallback to the standard report engine&lt;br /&gt;
* default Jasper renderer registration&lt;br /&gt;
* supported output formats&lt;br /&gt;
* processor ordering&lt;br /&gt;
* processor applicability&lt;br /&gt;
* invalid null processor results&lt;br /&gt;
* enabling and disabling post-processing&lt;br /&gt;
* processed-content caching&lt;br /&gt;
* propagation of report-generation failures&lt;br /&gt;
&lt;br /&gt;
After fixes made during review, the reported validation results were:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ServerReportCtlTest&amp;lt;/code&amp;gt;: 1 test, 0 failures, 0 errors&lt;br /&gt;
* &amp;lt;code&amp;gt;ReportViewerContentRendererFactoryTest&amp;lt;/code&amp;gt;: 9 tests, 0 failures, 0 errors&lt;br /&gt;
* complete 63-module Maven &amp;lt;code&amp;gt;verify&amp;lt;/code&amp;gt; reactor: '''BUILD SUCCESS'''&lt;br /&gt;
* &amp;lt;code&amp;gt;git diff --check&amp;lt;/code&amp;gt;: passed&lt;br /&gt;
&lt;br /&gt;
Earlier failures concerning the requested PDF filename and missing server-side print data were fixed before the successful final validation.&lt;br /&gt;
&lt;br /&gt;
=== Integration testing ===&lt;br /&gt;
&lt;br /&gt;
The reported integration-test environment was:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| iDempiere&lt;br /&gt;
| Version 14 development workspace&lt;br /&gt;
|-&lt;br /&gt;
| Java&lt;br /&gt;
| OpenJDK 17.0.19&lt;br /&gt;
|-&lt;br /&gt;
| Runtime&lt;br /&gt;
| Eclipse PDE&lt;br /&gt;
|-&lt;br /&gt;
| Database&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
|-&lt;br /&gt;
| Database migration level&lt;br /&gt;
| &amp;lt;code&amp;gt;202607261257_IDEMPIERE-6552.sql&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ZK&lt;br /&gt;
| 10.3.0.1&lt;br /&gt;
|-&lt;br /&gt;
| Locale&lt;br /&gt;
| &amp;lt;code&amp;gt;de_DE&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The integration test used:&lt;br /&gt;
&lt;br /&gt;
* a FreeMarker report renderer;&lt;br /&gt;
* a ZUGFeRD/Factur-X content processor;&lt;br /&gt;
* invoice &amp;lt;code&amp;gt;C_Invoice_ID=103&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The following flow was tested:&lt;br /&gt;
&lt;br /&gt;
# Generate an invoice using the normal Print button.&lt;br /&gt;
# Render the invoice through the new report content pipeline.&lt;br /&gt;
# Post-process the generated PDF through &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Embed Factur-X/ZUGFeRD XML into the PDF.&lt;br /&gt;
# Combine the FreeMarker renderer with the ZUGFeRD processor.&lt;br /&gt;
&lt;br /&gt;
Reported result:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Invoice_Header.pdf&amp;lt;/code&amp;gt; was generated successfully.&lt;br /&gt;
* The PDF contained an embedded &amp;lt;code&amp;gt;factur-x.xml&amp;lt;/code&amp;gt; attachment.&lt;br /&gt;
* The embedded XML was 9,711 bytes and began with a valid &amp;lt;code&amp;gt;CrossIndustryInvoice&amp;lt;/code&amp;gt; document.&lt;br /&gt;
* The ZUGFeRD processor worked independently of the selected renderer.&lt;br /&gt;
* No remaining ZUGFeRD, PostgreSQL or PDFBox errors were reported in the server log after the final test.&lt;br /&gt;
&lt;br /&gt;
Further review and broader testing of the affected report-generation paths and representative processor combinations are still recommended before merge.&lt;br /&gt;
&lt;br /&gt;
== Known limitation ==&lt;br /&gt;
&lt;br /&gt;
Multi-record Jasper report handling is intentionally outside the scope of this change and is tracked separately in [https://idempiere.atlassian.net/browse/IDEMPIERE-7073 IDEMPIERE-7073].&lt;br /&gt;
&lt;br /&gt;
== Related work and examples ==&lt;br /&gt;
&lt;br /&gt;
* [https://idempiere.atlassian.net/browse/IDEMPIERE-7070 IDEMPIERE-7070 – Make report content generation extensible through OSGi]&lt;br /&gt;
* [https://github.com/idempiere/idempiere/pull/3318 Pull request #3318]&lt;br /&gt;
* [https://idempiere.atlassian.net/browse/IDEMPIERE-6069 IDEMPIERE-6069 – related composable report-processing requirement]&lt;br /&gt;
* [[Plugin:_ZUGFeRDXInvoice|ZUGFeRD/XInvoice plug-in]]&lt;br /&gt;
* [https://github.com/mbozem/idempiere-print-template/tree/IDEMPIERE-7070 FreeMarker renderer integration branch]&lt;br /&gt;
* [https://github.com/mbozem/auler.gmbh.zugferdxinvoice/tree/agent/idempiere-14-compatibility ZUGFeRD/Factur-X integration branch]&lt;br /&gt;
&lt;br /&gt;
== Tracking information ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Item&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| Jira issue&lt;br /&gt;
| [https://idempiere.atlassian.net/browse/IDEMPIERE-7070 IDEMPIERE-7070]&lt;br /&gt;
|-&lt;br /&gt;
| Issue type&lt;br /&gt;
| Improvement&lt;br /&gt;
|-&lt;br /&gt;
| Priority&lt;br /&gt;
| Minor&lt;br /&gt;
|-&lt;br /&gt;
| Jira status&lt;br /&gt;
| Peer Review Queue&lt;br /&gt;
|-&lt;br /&gt;
| Reporter / assignee&lt;br /&gt;
| Markus Bozem&lt;br /&gt;
|-&lt;br /&gt;
| Created&lt;br /&gt;
| 29 July 2026&lt;br /&gt;
|-&lt;br /&gt;
| Pull request&lt;br /&gt;
| [https://github.com/idempiere/idempiere/pull/3318 #3318]&lt;br /&gt;
|-&lt;br /&gt;
| Pull request target&lt;br /&gt;
| &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Pull request status&lt;br /&gt;
| Open — not merged&lt;br /&gt;
|-&lt;br /&gt;
| Pull request scope as of 16 August 2026&lt;br /&gt;
| 37 changed files, 1,526 additions and 495 deletions&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Category:New Features]]&lt;br /&gt;
[[Category:New Features w14]]&lt;br /&gt;
[[Category:New Features Development]]&lt;br /&gt;
[[Category:New Features Technical]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=24043</id>
		<title>User:Mbozem</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=24043"/>
		<updated>2026-09-11T16:51:36Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: /* Kontaktdaten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Userboxtop}}&lt;br /&gt;
{{User real name|Markus Bozem}}&lt;br /&gt;
{{User Living In Germany}}&lt;br /&gt;
{{User en}}&lt;br /&gt;
{{User de}}&lt;br /&gt;
{{User developer}}&lt;br /&gt;
{{User GitHub|https://github.com/mbozem}}&lt;br /&gt;
{{Userboxbottom}}&lt;br /&gt;
&lt;br /&gt;
== Contact ==&lt;br /&gt;
&lt;br /&gt;
[mailto:mbozem@bozem.de E-Mail]&amp;lt;br&amp;gt;&lt;br /&gt;
[http://sps-schutzplanken.de SPS Schutzplanken GmbH]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=24042</id>
		<title>User:Mbozem</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=24042"/>
		<updated>2026-09-11T16:37:19Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: /* Kontaktdaten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Kontaktdaten ==&lt;br /&gt;
&lt;br /&gt;
Markus Bozem&amp;lt;br&amp;gt;&lt;br /&gt;
Ulmenstraße 6&amp;lt;br&amp;gt;&lt;br /&gt;
63808 Haibach&lt;br /&gt;
&lt;br /&gt;
[mailto:mbozem@bozem.de Email]&lt;br /&gt;
[http://www.markus-bozem.de Homepage]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=Creating_reports_using_JasperReports&amp;diff=23979</id>
		<title>Creating reports using JasperReports</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=Creating_reports_using_JasperReports&amp;diff=23979"/>
		<updated>2026-08-16T17:27:08Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is a guide to the JasperReports library that is included with iDempiere. The focus is on the standard reporting mechanism, but you can also design more complex reports using JasperSoft Studio as described [[Create reports using JasperSoft Studio|here]].&lt;br /&gt;
=Introduction=&lt;br /&gt;
iDempiere includes the Jaspersoft reporting software library as standard to produce reports in a simple format from many Windows, such as Business Partner Details, Bank/Cash, and so on. &lt;br /&gt;
&lt;br /&gt;
You can also use the embedded JasperReports engine to generate reports in your own customized format by using JasperSoft Studio to design them.&lt;br /&gt;
&lt;br /&gt;
=Standard Reporting=&lt;br /&gt;
==Ready-made reports==&lt;br /&gt;
Many iDempiere Windows include standard, ready-made tabular reports that can be generated from the Report button on the Toolbar (or type Alt-R). Some Windows, for example ''Invoices (Customer)'', also have a Print button (Alt-P) that produces a formatted page.&lt;br /&gt;
&lt;br /&gt;
By default the standard report format includes features like:&lt;br /&gt;
* Zoom across to related documents (e.g. zoom to a Business Partner from the report);&lt;br /&gt;
* Drill down using hyperlinks in HTML reports;&lt;br /&gt;
* Export to HTML, PDF, and Excel.&lt;br /&gt;
&lt;br /&gt;
You can see the full list of ready-made reports by logging in as System/System role, then select ''Report&amp;amp;Process'' and execute an advanced query with the filter ''Report=Yes'' to select the reports. &lt;br /&gt;
&lt;br /&gt;
Use these ready-made reports as a guide to create your own.&lt;br /&gt;
==Customise a standard report==&lt;br /&gt;
You can customise these reports and prints using the Toolbox icon from the print-preview window to change:&lt;br /&gt;
* Add and remove columns;&lt;br /&gt;
* Page format and orientation: A4 Landscape for example;&lt;br /&gt;
* Font: serif or sans serif;&lt;br /&gt;
* Display order of the items on the page;&lt;br /&gt;
* Sort order of the rows or pages;&lt;br /&gt;
* Format of the columns: alignment, width, colour, and so on.&lt;br /&gt;
&lt;br /&gt;
Once you have made your changes, save the new print format and re-create the print-out.&lt;br /&gt;
==Report Wizard==&lt;br /&gt;
iDempiere also provides a Report Wizard using the Wizard icon which provides more functionality to customise the standard reports, in particular:&lt;br /&gt;
* Grouping criteria: group by date or by customer, for example;&lt;br /&gt;
* Summary Fields: provides aggregate functions like sum, count, minimum, maximum, mean, variance, and standard deviation;&lt;br /&gt;
* Copy Print Format: make a copy of the standard format of the ready-made reports and then change the format to suit your requirements, leaving the original print format intact.&lt;br /&gt;
&lt;br /&gt;
==Reporting selected records==&lt;br /&gt;
&lt;br /&gt;
JasperReports can access multiple records selected in an iDempiere window through the T_Selection table. The current process instance available through the AD_PINSTANCE_ID Jasper parameter.&lt;br /&gt;
&lt;br /&gt;
For queries supporting both individual records and multiple selections, use RECORD_ID for the individual record and T_Selection for the record collection.&lt;br /&gt;
&lt;br /&gt;
See [[Create reports using JasperSoft Studio#Filtering by records selected in iDempiere|Filtering by records selected in iDempiere]] for parameter declarations and SQL examples.&lt;br /&gt;
&lt;br /&gt;
==Create new reports using built-in reporting functionality==&lt;br /&gt;
You can also create your own reports using iDempiere's built-in reporting functionality by creating a Report View for a database table or a more complex database. Follow the [[Report View|guidelines here]].&lt;br /&gt;
&lt;br /&gt;
=Customised reporting=&lt;br /&gt;
==Create new reports using JasperSoft Studio==&lt;br /&gt;
The standard reporting mechanism is suitable for simple reports, but using the built-in Print Format tools to customise layouts and create new reports can be cumbersome and time-consuming.&lt;br /&gt;
&lt;br /&gt;
If you want to create more sophisticated reports and print-outs with professionally designed layouts, fonts, colours and logos, you should rather use JasperSoft Studio to do the design, then load the JRXML report definition into iDempiere for execution by the JasperReports engine. &lt;br /&gt;
&lt;br /&gt;
Follow these guides:&lt;br /&gt;
* [[Create reports using JasperSoft Studio]]&lt;br /&gt;
* [https://www.youtube.com/watch?v=qupDLprUoJE Video about JasperSoft Studio by Evenos]&lt;br /&gt;
* [https://www.youtube.com/watch?v=B9mtzNm7CRc Short video about JasperSoft Studio].&lt;br /&gt;
&lt;br /&gt;
==JasperReports plug-in by Niemcor==&lt;br /&gt;
See this [http://youtu.be/JW7WmuknBUo video by Red1] demonstrating a useful JasperReports plug-in by Niemcor, with ready made sample reports. Follow the [[Plugin:_JasperReports|instructions here]].&lt;br /&gt;
&lt;br /&gt;
=See also=&lt;br /&gt;
* [[Making fonts available to your JasperReports]]&lt;br /&gt;
* [[Finding resources in JasperReports|Making resources such as images and language files available to your JasperReports]]&lt;br /&gt;
* [[Multi-Lingual Jasper]]&lt;br /&gt;
* [http://www.adempiere.com/ADempiere/Compiere_JasperReports_Integration_HowTo Link to historical Compiere documentation on JasperReports]&lt;br /&gt;
* [http://www.adempiere.com/HOWTO_Use_Jasper_On_Financial_Reports Another link to historical Compiere documentation about Financial Reports]&lt;br /&gt;
&lt;br /&gt;
[[Category: JasperReports]]&lt;br /&gt;
[[Category: Development|P]]&lt;br /&gt;
[[Category: Developer documentation‏‎]]&lt;br /&gt;
[[Category:Reference]]&lt;br /&gt;
[[Category: NeedsToBeUpdated]]&lt;br /&gt;
&lt;br /&gt;
[[en:Creating reports using JasperReports]] &lt;br /&gt;
[[es:Crear reportes usando JasperReports]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=Create_reports_using_JasperSoft_Studio&amp;diff=23978</id>
		<title>Create reports using JasperSoft Studio</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=Create_reports_using_JasperSoft_Studio&amp;diff=23978"/>
		<updated>2026-08-16T17:23:45Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This guide explains how to use JasperSoft Studio to create complex reports and print-outs for use by the JasperReports engine embedded in iDempiere. This is an alternative to using the standard built-in reports [[Creating reports using JasperReports| described here]].&lt;br /&gt;
&lt;br /&gt;
=Introduction=&lt;br /&gt;
The standard reporting mechanism is suitable for simple reports, but creating complex reports with sophisticated layouts can be difficult and time-consuming using the built-in Print Format tools, so rather use JasperSoft Studio to do the design of sophisticated reports and print-outs with professionally designed layouts, fonts, colours and logos, then load the JRXML report definition into iDempiere for execution by the JasperReports engine.&lt;br /&gt;
&lt;br /&gt;
The guide is based on [https://www.youtube.com/watch?v=qupDLprUoJE this video] by Evenos.&lt;br /&gt;
=How to do it=&lt;br /&gt;
==JasperSoft Studio==&lt;br /&gt;
If you have not already done so, install JasperSoft Studio:&lt;br /&gt;
* Download and install the Community Edition of JasperSoft Studio from the [https://community.jaspersoft.com/project/jaspersoft-studio download page] ('''note:''' don't download the older iReport Designer as this has been deprecated in favour of JasperSoft Studio);&lt;br /&gt;
* Follow the installation instructions [https://community.jaspersoft.com/documentation/tibco-jaspersoft-studio-user-guide/v640/installing-jaspersoft-studio here]. JasperSoft Studio is built on the same framework as the Eclipse IDE so the [https://community.jaspersoft.com/documentation/v640/tibco-jaspersoft-studio-user-guide installation and usage is the same].&lt;br /&gt;
&lt;br /&gt;
==Create a report==&lt;br /&gt;
===Database Adapter===&lt;br /&gt;
The first step is to create a database adapter to connect JasperSoft Studio to your instance of the iDempiere database server:&lt;br /&gt;
&lt;br /&gt;
* Start up JasperSoft Studio&lt;br /&gt;
* Delete the two unnecessary Data Adapters (called ''One Empty Record'' and ''Sample DB'')&lt;br /&gt;
* Create a new Data Adapter to connect to the iDempiere database: '''Create Data Adapter &amp;gt; Database JDBC Connection &amp;gt; Next &amp;gt; '''&lt;br /&gt;
** '''Name:''' iDempiere Data Adapter&lt;br /&gt;
** '''JDBC Driver:''' org.postgresql.Driver&lt;br /&gt;
** '''JDBC URL:''' jdbc:postgresql://localhost:5432/idempiere (use ''localhost'' if the database is on the same machine as JasperSoft Studio, otherwise replace ''localhost'' with the name of the database server)&lt;br /&gt;
** '''Username:''' adempiere (the same username that was used when iDempiere was installed)&lt;br /&gt;
** '''Password:''' *********&lt;br /&gt;
* Click on '''Test''' to test the connection parameters&lt;br /&gt;
* Click '''Finish''' if all is well&lt;br /&gt;
&lt;br /&gt;
This only has to be done once. Thereafter you will use the same adapter to develop your reports.&lt;br /&gt;
===Design the report===&lt;br /&gt;
Now you can create the JasperReport:&lt;br /&gt;
* Click on the ''New JasperReport'' icon&lt;br /&gt;
* Select a Report Template (e.g. Blank Format A4)&lt;br /&gt;
** '''Parent directory:''' MyReports (in the JaspersoftWorkspace), or create a new JasperReports Project&lt;br /&gt;
** '''File name:''' Invoice.jrxml (JRXML is the XML format used by JasperReports)&lt;br /&gt;
* Open the ''adempiere (CURRENT)'' schema to see all the tables and views that can be used in the report (note that you can also create additional database views in the schema for your own reports)&lt;br /&gt;
* In the Texts box (to the right of the schemas), write a SQL SELECT statement to assemble the items for the report, for example:&lt;br /&gt;
  SELECT a.documentno, a.docstatus, b.name, b.logo_id FROM adempiere.c_invoice a, adempiere.c_bpartner b  WHERE a.c_bpartner_id=b.c_bpartner_id&lt;br /&gt;
* The SELECT can be as detailed and as complex as you require&lt;br /&gt;
* Click ''Next &amp;gt;'' to choose the fields from the SELECT that you want to see in the report&lt;br /&gt;
* Click ''Next &amp;gt;'' and add any GROUP BY clauses if required&lt;br /&gt;
* Click ''Finish'' to generate the basic report definition&lt;br /&gt;
* Use the Preview tab to run the report, then Save it regularly while you work on it.&lt;br /&gt;
&lt;br /&gt;
Follow [https://www.youtube.com/watch?v=qupDLprUoJE the video] to complete the design of the report, and see the [https://community.jaspersoft.com/documentation/tibco-jaspersoft-studio-user-guide/v640/getting-started-jaspersoft-studio JasperSoft Studio documentation] for more details. &lt;br /&gt;
===Compile the report===&lt;br /&gt;
When you are done, compile the report for use by iDempiere:&lt;br /&gt;
* Select the Design Tab&lt;br /&gt;
* Click on the ''Compile Report'' button (above the report design)&lt;br /&gt;
&lt;br /&gt;
This will generate the report binary file ''Invoice.jasper'' from the resource ''Invoice.jrxml''. This is the file that you will upload to iDempiere (see below). Note that you can upload either the binary ''.jasper'' or the XML description ''.JRXML'' because iDempiere understands both, but the binary may be more efficient, and it may avoid version inconsistencies between the JasperReports compiler in iDempiere and the one in JasperSoft Studio.&lt;br /&gt;
&lt;br /&gt;
==Installing the report==&lt;br /&gt;
Once you are happy with the report, you can install it in iDempiere as follows:&lt;br /&gt;
* Login as System/System&lt;br /&gt;
* Select ''Report &amp;amp; Process'' and create a new entry:&lt;br /&gt;
** '''Search Key:''' InvoiceJasperReport&lt;br /&gt;
** '''Name:''' InvoiceJasperReport&lt;br /&gt;
** '''Data Access Level:''' Client+Organisation (it is not useful to the System user)&lt;br /&gt;
** '''Report:''' check the box for completeness&lt;br /&gt;
** '''Class Name:''' org.compiere.report.ReportStarter (note that this is case sensitive)&lt;br /&gt;
** '''Jasper Report:''' attachment:Invoice.jasper (this is the binary that was created above)&lt;br /&gt;
* Save the record&lt;br /&gt;
* Now click on the Attachment icon (the paperclip on the toolbar) and load the binary from the JaspersoftWorkspace. This ensures that the binary is stored in the database with the Report Process record and will get backed up with the database.&lt;br /&gt;
&lt;br /&gt;
To try out the report:&lt;br /&gt;
* Logout of System and into the client (e.g. GardenWorld Admin)&lt;br /&gt;
* Select Invoice and find an invoice that will use the new report format&lt;br /&gt;
* Click on the Print button (or Alt-P)&lt;br /&gt;
* When the preview opens, click on ''Customize Report'' to open the Print Format Window&lt;br /&gt;
* Find the field ''Jasper Report'' and use the dropdown to select the Report that you created above&lt;br /&gt;
* Save the updated Print Format and reprint the document with the new report layout.&lt;br /&gt;
&lt;br /&gt;
==Filtering by records selected in iDempiere==&lt;br /&gt;
&lt;br /&gt;
When a report or process is started for multiple records selected in an iDempiere window, the selected record keys are stored in T_Selection. Each selection is associated with the current process instance through AD_PInstance_ID.&lt;br /&gt;
&lt;br /&gt;
The process instance ID is passed to JasperReports as the integer parameter AD_PINSTANCE_ID. Declare this parameter in the JRXML file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;parameter name=&amp;quot;AD_PINSTANCE_ID&amp;quot; class=&amp;quot;java.lang.Integer&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For tables with a numeric primary key, join the report query to T_Selection.T_Selection_ID:&lt;br /&gt;
&lt;br /&gt;
  SELECT bp.*&lt;br /&gt;
  FROM C_BPartner bp&lt;br /&gt;
  INNER JOIN T_Selection sel&lt;br /&gt;
          ON sel.T_Selection_ID = bp.C_BPartner_ID&lt;br /&gt;
         AND sel.AD_PInstance_ID = $P{AD_PINSTANCE_ID}&lt;br /&gt;
  ORDER BY bp.Value&lt;br /&gt;
&lt;br /&gt;
Always restrict T_Selection by AD_PInstance_ID. Otherwise, the query could include selections belonging to other process executions.&lt;br /&gt;
&lt;br /&gt;
For tables using a UUID key, use T_Selection_UU instead:&lt;br /&gt;
&lt;br /&gt;
  SELECT t.*&lt;br /&gt;
  FROM MyTable t&lt;br /&gt;
  INNER JOIN T_Selection sel&lt;br /&gt;
          ON sel.T_Selection_UU = t.MyTable_UU&lt;br /&gt;
         AND sel.AD_PInstance_ID = $P{AD_PINSTANCE_ID}&lt;br /&gt;
&lt;br /&gt;
T_Selection is normally populated when a collection of selected records is passed to the report. A report opened for one individual record normally receives the record through RECORD_ID instead.&lt;br /&gt;
&lt;br /&gt;
To support both a single record and multiple selected records, declare both parameters:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;parameter name=&amp;quot;RECORD_ID&amp;quot; class=&amp;quot;java.lang.Integer&amp;quot;/&amp;gt;&lt;br /&gt;
  &amp;lt;parameter name=&amp;quot;AD_PINSTANCE_ID&amp;quot; class=&amp;quot;java.lang.Integer&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The query can then use both selection mechanisms:&lt;br /&gt;
&lt;br /&gt;
  SELECT bp.*&lt;br /&gt;
  FROM C_BPartner bp&lt;br /&gt;
  WHERE bp.C_BPartner_ID = $P{RECORD_ID}&lt;br /&gt;
     OR EXISTS (&lt;br /&gt;
         SELECT 1&lt;br /&gt;
         FROM T_Selection sel&lt;br /&gt;
         WHERE sel.AD_PInstance_ID = $P{AD_PINSTANCE_ID}&lt;br /&gt;
           AND sel.T_Selection_ID = bp.C_BPartner_ID&lt;br /&gt;
     )&lt;br /&gt;
  ORDER BY bp.Value&lt;br /&gt;
&lt;br /&gt;
This form supports reports started for a single record as well as reports started for multiple selected records.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=References=&lt;br /&gt;
* [[Creating reports using JasperReports]]&lt;br /&gt;
* [https://www.youtube.com/watch?v=qupDLprUoJE Evenos video about JasperSoft Studio]&lt;br /&gt;
* [https://community.jaspersoft.com/documentation/v640/tibco-jaspersoft-studio-user-guide JasperSoft Studio Guide]&lt;br /&gt;
* [[Making fonts available to your JasperReports]]&lt;br /&gt;
&lt;br /&gt;
[[Category: JasperReports]]&lt;br /&gt;
[[Category: Development|P]]&lt;br /&gt;
[[Category: Developer documentation‏‎]]&lt;br /&gt;
[[Category: Updated2022]]&lt;br /&gt;
[[Category: How-To]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=NF14_Extensible_Report_Content_Generation&amp;diff=23977</id>
		<title>NF14 Extensible Report Content Generation</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=NF14_Extensible_Report_Content_Generation&amp;diff=23977"/>
		<updated>2026-08-16T11:12:24Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: Created page with &amp;quot;&amp;lt;div style=&amp;quot;border:2px solid #d6a100; background:#fff8d8; padding:1em; margin-bottom:1em;&amp;quot;&amp;gt; '''Work in progress – not yet merged'''  This feature is proposed for iDempiere 1...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;border:2px solid #d6a100; background:#fff8d8; padding:1em; margin-bottom:1em;&amp;quot;&amp;gt;&lt;br /&gt;
'''Work in progress – not yet merged'''&lt;br /&gt;
&lt;br /&gt;
This feature is proposed for iDempiere 14 but has '''not yet been merged into the master branch'''.&lt;br /&gt;
&lt;br /&gt;
As of 16 August 2026:&lt;br /&gt;
&lt;br /&gt;
* Jira status: '''Peer Review Queue'''&lt;br /&gt;
* Pull request: '''Open'''&lt;br /&gt;
* GitHub review decision: '''Changes requested'''&lt;br /&gt;
* Merge status: '''Blocked'''&lt;br /&gt;
&lt;br /&gt;
The APIs and behavior described on this page may still change during review.&lt;br /&gt;
Do not depend on them in production plug-ins until the pull request has been merged.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
IDEMPIERE-7070 introduces UI-independent and report-engine-independent extension points for generating and post-processing report content.&lt;br /&gt;
&lt;br /&gt;
The feature allows plug-ins to provide alternative report renderers and to enhance generated documents without depending on the ZK report viewer or modifying iDempiere core classes.&lt;br /&gt;
&lt;br /&gt;
== Purpose ==&lt;br /&gt;
&lt;br /&gt;
Before this change, the report extension mechanism was located in the ZK report viewer and exposed ZK- and Jasper-specific types.&lt;br /&gt;
&lt;br /&gt;
This caused several limitations:&lt;br /&gt;
&lt;br /&gt;
* The extension was not available to headless or server-side consumers.&lt;br /&gt;
* Report-generation paths calling &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; directly bypassed plug-in-provided output.&lt;br /&gt;
* The mechanism was effectively tied to Jasper Reports.&lt;br /&gt;
* Multiple independent document enhancements could not be combined cleanly because only one renderer could handle the request.&lt;br /&gt;
* Preview and download names could expose the random suffix of an internal temporary Jasper file.&lt;br /&gt;
&lt;br /&gt;
A report may need several consecutive operations, for example:&lt;br /&gt;
&lt;br /&gt;
# Generate the initial PDF.&lt;br /&gt;
# Add ZUGFeRD/Factur-X data.&lt;br /&gt;
# Convert the document to PDF/A.&lt;br /&gt;
# Add PDF attachments.&lt;br /&gt;
# Add a watermark.&lt;br /&gt;
# Digitally sign or encrypt the final document.&lt;br /&gt;
&lt;br /&gt;
IDEMPIERE-7070 separates initial report rendering from subsequent content processing, allowing these operations to be implemented by independent OSGi services and executed in a defined order.&lt;br /&gt;
&lt;br /&gt;
== Architecture ==&lt;br /&gt;
&lt;br /&gt;
Report content generation consists of two extension stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ReportContentRequest&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
ranked IReportContentRendererFactory services&lt;br /&gt;
        |&lt;br /&gt;
        +-- first applicable renderer&lt;br /&gt;
        |&lt;br /&gt;
        +-- ReportEngine fallback&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
initial report content&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
all applicable IReportContentProcessor services&lt;br /&gt;
in descending OSGi service-ranking order&lt;br /&gt;
        |&lt;br /&gt;
        v&lt;br /&gt;
final processed content&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Report content request ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;ReportContentRequest&amp;lt;/code&amp;gt; is the common input for rendering and post-processing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public record ReportContentRequest(&lt;br /&gt;
        ReportEngine reportEngine,&lt;br /&gt;
        ProcessInfo processInfo,&lt;br /&gt;
        String title,&lt;br /&gt;
        boolean applyPostProcessing) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The three-argument constructor enables post-processing by default:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ReportContentRequest request =&lt;br /&gt;
        new ReportContentRequest(reportEngine, processInfo, reportTitle);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Post-processing can be disabled explicitly when required:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
ReportContentRequest request =&lt;br /&gt;
        new ReportContentRequest(reportEngine, processInfo, reportTitle, false);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The report context preserves the relevant process information, including:&lt;br /&gt;
&lt;br /&gt;
* process parameters&lt;br /&gt;
* record ID and record UUID&lt;br /&gt;
* record selections&lt;br /&gt;
* language&lt;br /&gt;
* user and tenant context&lt;br /&gt;
* transaction context&lt;br /&gt;
* requested PDF filename&lt;br /&gt;
&lt;br /&gt;
=== Stage 1: report content rendering ===&lt;br /&gt;
&lt;br /&gt;
A plug-in can register an &amp;lt;code&amp;gt;IReportContentRendererFactory&amp;lt;/code&amp;gt; OSGi service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentRendererFactory {&lt;br /&gt;
    IReportContentRenderer createRenderer(ReportContentRequest request);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Factories are evaluated in descending OSGi &amp;lt;code&amp;gt;service.ranking&amp;lt;/code&amp;gt; order. A factory returns:&lt;br /&gt;
&lt;br /&gt;
* an &amp;lt;code&amp;gt;IReportContentRenderer&amp;lt;/code&amp;gt; when it supports the request, or&lt;br /&gt;
* &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; when it is not applicable.&lt;br /&gt;
&lt;br /&gt;
The first applicable renderer is selected.&lt;br /&gt;
&lt;br /&gt;
A renderer implements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentRenderer {&lt;br /&gt;
    File getContent(String contentType, String fileExtension);&lt;br /&gt;
&lt;br /&gt;
    ReportContentType[] getSupportedContentTypes();&lt;br /&gt;
&lt;br /&gt;
    default int getRowCount() {&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Supported output types are described without any dependency on a UI toolkit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public record ReportContentType(&lt;br /&gt;
        String name,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        String contentType) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example Declarative Services registration is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@Component(&lt;br /&gt;
    service = IReportContentRendererFactory.class,&lt;br /&gt;
    immediate = true,&lt;br /&gt;
    property = &amp;quot;service.ranking:Integer=100&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
public class CustomReportContentRendererFactory&lt;br /&gt;
        implements IReportContentRendererFactory {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public IReportContentRenderer createRenderer(&lt;br /&gt;
            ReportContentRequest request) {&lt;br /&gt;
        if (!supports(request))&lt;br /&gt;
            return null;&lt;br /&gt;
&lt;br /&gt;
        return new CustomReportContentRenderer(request);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The default Jasper renderer is supplied by the &amp;lt;code&amp;gt;org.adempiere.report.jasper&amp;lt;/code&amp;gt; bundle with service ranking &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. It is one provider of the generic API; the extension point is not limited to Jasper Reports.&lt;br /&gt;
&lt;br /&gt;
The Jasper renderer supports the existing report viewer formats, including:&lt;br /&gt;
&lt;br /&gt;
* PDF&lt;br /&gt;
* HTML&lt;br /&gt;
* CSV&lt;br /&gt;
* XLS&lt;br /&gt;
* XLSX&lt;br /&gt;
* SSV&lt;br /&gt;
&lt;br /&gt;
If no registered factory handles the request, Core uses the standard &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; as a fallback. The fallback supports PDF, HTML, CSV, XLS and XLSX.&lt;br /&gt;
&lt;br /&gt;
=== Stage 2: report content post-processing ===&lt;br /&gt;
&lt;br /&gt;
After rendering, the generated content is passed through every applicable &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt; service:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
public interface IReportContentProcessor {&lt;br /&gt;
&lt;br /&gt;
    boolean isApplicable(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension);&lt;br /&gt;
&lt;br /&gt;
    File process(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension,&lt;br /&gt;
            File input);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Processors are executed in descending OSGi &amp;lt;code&amp;gt;service.ranking&amp;lt;/code&amp;gt; order. Each processor receives the result of the previous processor.&lt;br /&gt;
&lt;br /&gt;
A processor may:&lt;br /&gt;
&lt;br /&gt;
* modify the input file and return the same file, or&lt;br /&gt;
* create and return a new file.&lt;br /&gt;
&lt;br /&gt;
It must not return &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. A null result is treated as an error.&lt;br /&gt;
&lt;br /&gt;
Typical processor use cases include:&lt;br /&gt;
&lt;br /&gt;
* ZUGFeRD/Factur-X data&lt;br /&gt;
* PDF/A conversion&lt;br /&gt;
* PDF attachments&lt;br /&gt;
* watermarks&lt;br /&gt;
* digital signatures&lt;br /&gt;
* encryption&lt;br /&gt;
* other document transformations&lt;br /&gt;
&lt;br /&gt;
An example registration is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@Component(&lt;br /&gt;
    service = IReportContentProcessor.class,&lt;br /&gt;
    immediate = true,&lt;br /&gt;
    property = &amp;quot;service.ranking:Integer=100&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
public class CustomPDFProcessor&lt;br /&gt;
        implements IReportContentProcessor {&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public boolean isApplicable(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension) {&lt;br /&gt;
        return &amp;quot;application/pdf&amp;quot;.equals(contentType)&lt;br /&gt;
                || &amp;quot;pdf&amp;quot;.equalsIgnoreCase(fileExtension);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public File process(&lt;br /&gt;
            ReportContentRequest request,&lt;br /&gt;
            String contentType,&lt;br /&gt;
            String fileExtension,&lt;br /&gt;
            File input) {&lt;br /&gt;
        // Modify input or create a new output file.&lt;br /&gt;
        return input;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The processor chain is applied both to custom renderer output and to content generated by the standard &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt; fallback.&lt;br /&gt;
&lt;br /&gt;
== Central Core API ==&lt;br /&gt;
&lt;br /&gt;
The common API is provided through &amp;lt;code&amp;gt;org.adempiere.base.Core&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The main entry points are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
IReportContentRenderer getReportContentRenderer(&lt;br /&gt;
        ReportContentRequest request);&lt;br /&gt;
&lt;br /&gt;
File getReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension);&lt;br /&gt;
&lt;br /&gt;
File getReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        File outputFile);&lt;br /&gt;
&lt;br /&gt;
File processReportContent(&lt;br /&gt;
        ReportContentRequest request,&lt;br /&gt;
        String contentType,&lt;br /&gt;
        String fileExtension,&lt;br /&gt;
        File content);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When an output file is supplied, only the final processed content is copied to that target. Intermediate renderer or processor files are not copied to it.&lt;br /&gt;
&lt;br /&gt;
Failures during report generation are propagated instead of silently returning an incomplete result.&lt;br /&gt;
&lt;br /&gt;
== Report-generation paths using the API ==&lt;br /&gt;
&lt;br /&gt;
The pull request routes the following report-generation paths through the common API:&lt;br /&gt;
&lt;br /&gt;
* ZK report preview and export&lt;br /&gt;
* the legacy direct Jasper viewer&lt;br /&gt;
* report email attachments&lt;br /&gt;
* report archiving&lt;br /&gt;
* workflow reports&lt;br /&gt;
* server-side exports&lt;br /&gt;
* document PDF generation&lt;br /&gt;
* batch-print processes&lt;br /&gt;
* payment and remittance printing&lt;br /&gt;
* dunning and invoice printing&lt;br /&gt;
* relevant ZK report and print processes&lt;br /&gt;
&lt;br /&gt;
Document generation for orders, invoices, shipments, distribution orders and RFQ responses is included in the affected server-side paths.&lt;br /&gt;
&lt;br /&gt;
The existing direct-printer path remains unchanged.&lt;br /&gt;
&lt;br /&gt;
== Report viewer behavior ==&lt;br /&gt;
&lt;br /&gt;
The ZK report viewer uses the common content renderer and processor chain.&lt;br /&gt;
&lt;br /&gt;
Processed content is cached for each combination of MIME type and file extension. Preview, export, email and archive actions can therefore reuse the same final result.&lt;br /&gt;
&lt;br /&gt;
This is important for processors such as:&lt;br /&gt;
&lt;br /&gt;
* document signing&lt;br /&gt;
* attachment generation&lt;br /&gt;
* ZUGFeRD/Factur-X generation&lt;br /&gt;
* PDF/A conversion&lt;br /&gt;
&lt;br /&gt;
Without caching, such processors could be executed more than once for the same viewer output.&lt;br /&gt;
&lt;br /&gt;
The report viewer also:&lt;br /&gt;
&lt;br /&gt;
* shows only formats supported by the active renderer or report engine;&lt;br /&gt;
* preserves the original &amp;lt;code&amp;gt;ProcessInfo&amp;lt;/code&amp;gt;;&lt;br /&gt;
* preserves record UUIDs;&lt;br /&gt;
* refreshes native print data after changing the print format;&lt;br /&gt;
* uses the logical report name for preview and download media;&lt;br /&gt;
* retains unique filenames for internal temporary files.&lt;br /&gt;
&lt;br /&gt;
Consequently, users no longer see the random suffix generated for an internal Jasper temporary file in the preview or download filename.&lt;br /&gt;
&lt;br /&gt;
== Compatibility ==&lt;br /&gt;
&lt;br /&gt;
* Existing Jasper reports are handled by the default renderer in the Jasper bundle.&lt;br /&gt;
* The API is not limited to Jasper Reports.&lt;br /&gt;
* Other report engines can register their own renderer factory.&lt;br /&gt;
* Requests without a matching renderer fall back to &amp;lt;code&amp;gt;ReportEngine&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Installations without an &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt; receive the original generated content unchanged.&lt;br /&gt;
* Headless installations can use the renderer and processor APIs without the ZK bundle.&lt;br /&gt;
* Existing report viewer export formats are retained.&lt;br /&gt;
* Legacy &amp;lt;code&amp;gt;ZkJRViewer&amp;lt;/code&amp;gt; adapters are retained.&lt;br /&gt;
* Direct-printer behavior is unchanged.&lt;br /&gt;
* No database migration is required.&lt;br /&gt;
&lt;br /&gt;
== Example use cases ==&lt;br /&gt;
&lt;br /&gt;
=== ZUGFeRD/Factur-X ===&lt;br /&gt;
&lt;br /&gt;
A processor can embed ZUGFeRD/Factur-X XML into a PDF produced by any renderer.&lt;br /&gt;
&lt;br /&gt;
This integrates electronic invoice generation into the normal workflow:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Print button&lt;br /&gt;
    -&amp;gt; Preview&lt;br /&gt;
    -&amp;gt; E-mail&lt;br /&gt;
    -&amp;gt; Archive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The same processor can be applied to a PDF produced by:&lt;br /&gt;
&lt;br /&gt;
* the native report engine&lt;br /&gt;
* Jasper Reports&lt;br /&gt;
* a FreeMarker-based renderer&lt;br /&gt;
* another custom report engine&lt;br /&gt;
&lt;br /&gt;
This complements the existing [[Plugin:_ZUGFeRDXInvoice|ZUGFeRD/XInvoice plug-in]] by allowing its result to participate in the standard report workflow.&lt;br /&gt;
&lt;br /&gt;
=== PDF attachments ===&lt;br /&gt;
&lt;br /&gt;
A processor can attach additional PDF documents to the generated report. For example, a drawing attached to an order can be included in the final order PDF.&lt;br /&gt;
&lt;br /&gt;
=== Alternative report engines ===&lt;br /&gt;
&lt;br /&gt;
The API was tested conceptually and practically with a FreeMarker-based print-template engine. Such a plug-in can examine the print format in &amp;lt;code&amp;gt;ReportContentRequest&amp;lt;/code&amp;gt;, return its own renderer and use the same post-processing pipeline as Jasper or the native report engine.&lt;br /&gt;
&lt;br /&gt;
== Validation ==&lt;br /&gt;
&lt;br /&gt;
=== Automated validation ===&lt;br /&gt;
&lt;br /&gt;
The pull request contains integration coverage for:&lt;br /&gt;
&lt;br /&gt;
* renderer selection by OSGi service ranking&lt;br /&gt;
* fallback to the next renderer factory&lt;br /&gt;
* fallback to the standard report engine&lt;br /&gt;
* default Jasper renderer registration&lt;br /&gt;
* supported output formats&lt;br /&gt;
* processor ordering&lt;br /&gt;
* processor applicability&lt;br /&gt;
* invalid null processor results&lt;br /&gt;
* enabling and disabling post-processing&lt;br /&gt;
* processed-content caching&lt;br /&gt;
* propagation of report-generation failures&lt;br /&gt;
&lt;br /&gt;
After fixes made during review, the reported validation results were:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ServerReportCtlTest&amp;lt;/code&amp;gt;: 1 test, 0 failures, 0 errors&lt;br /&gt;
* &amp;lt;code&amp;gt;ReportViewerContentRendererFactoryTest&amp;lt;/code&amp;gt;: 9 tests, 0 failures, 0 errors&lt;br /&gt;
* complete 63-module Maven &amp;lt;code&amp;gt;verify&amp;lt;/code&amp;gt; reactor: '''BUILD SUCCESS'''&lt;br /&gt;
* &amp;lt;code&amp;gt;git diff --check&amp;lt;/code&amp;gt;: passed&lt;br /&gt;
&lt;br /&gt;
Earlier failures concerning the requested PDF filename and missing server-side print data were fixed before the successful final validation.&lt;br /&gt;
&lt;br /&gt;
=== Integration testing ===&lt;br /&gt;
&lt;br /&gt;
The reported integration-test environment was:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Component&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| iDempiere&lt;br /&gt;
| Version 14 development workspace&lt;br /&gt;
|-&lt;br /&gt;
| Java&lt;br /&gt;
| OpenJDK 17.0.19&lt;br /&gt;
|-&lt;br /&gt;
| Runtime&lt;br /&gt;
| Eclipse PDE&lt;br /&gt;
|-&lt;br /&gt;
| Database&lt;br /&gt;
| PostgreSQL&lt;br /&gt;
|-&lt;br /&gt;
| Database migration level&lt;br /&gt;
| &amp;lt;code&amp;gt;202607261257_IDEMPIERE-6552.sql&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ZK&lt;br /&gt;
| 10.3.0.1&lt;br /&gt;
|-&lt;br /&gt;
| Locale&lt;br /&gt;
| &amp;lt;code&amp;gt;de_DE&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The integration test used:&lt;br /&gt;
&lt;br /&gt;
* a FreeMarker report renderer;&lt;br /&gt;
* a ZUGFeRD/Factur-X content processor;&lt;br /&gt;
* invoice &amp;lt;code&amp;gt;C_Invoice_ID=103&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The following flow was tested:&lt;br /&gt;
&lt;br /&gt;
# Generate an invoice using the normal Print button.&lt;br /&gt;
# Render the invoice through the new report content pipeline.&lt;br /&gt;
# Post-process the generated PDF through &amp;lt;code&amp;gt;IReportContentProcessor&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Embed Factur-X/ZUGFeRD XML into the PDF.&lt;br /&gt;
# Combine the FreeMarker renderer with the ZUGFeRD processor.&lt;br /&gt;
&lt;br /&gt;
Reported result:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Invoice_Header.pdf&amp;lt;/code&amp;gt; was generated successfully.&lt;br /&gt;
* The PDF contained an embedded &amp;lt;code&amp;gt;factur-x.xml&amp;lt;/code&amp;gt; attachment.&lt;br /&gt;
* The embedded XML was 9,711 bytes and began with a valid &amp;lt;code&amp;gt;CrossIndustryInvoice&amp;lt;/code&amp;gt; document.&lt;br /&gt;
* The ZUGFeRD processor worked independently of the selected renderer.&lt;br /&gt;
* No remaining ZUGFeRD, PostgreSQL or PDFBox errors were reported in the server log after the final test.&lt;br /&gt;
&lt;br /&gt;
Further review and broader testing of the affected report-generation paths and representative processor combinations are still recommended before merge.&lt;br /&gt;
&lt;br /&gt;
== Known limitation ==&lt;br /&gt;
&lt;br /&gt;
Multi-record Jasper report handling is intentionally outside the scope of this change and is tracked separately in [https://idempiere.atlassian.net/browse/IDEMPIERE-7073 IDEMPIERE-7073].&lt;br /&gt;
&lt;br /&gt;
== Related work and examples ==&lt;br /&gt;
&lt;br /&gt;
* [https://idempiere.atlassian.net/browse/IDEMPIERE-7070 IDEMPIERE-7070 – Make report content generation extensible through OSGi]&lt;br /&gt;
* [https://github.com/idempiere/idempiere/pull/3318 Pull request #3318]&lt;br /&gt;
* [https://idempiere.atlassian.net/browse/IDEMPIERE-6069 IDEMPIERE-6069 – related composable report-processing requirement]&lt;br /&gt;
* [[Plugin:_ZUGFeRDXInvoice|ZUGFeRD/XInvoice plug-in]]&lt;br /&gt;
* [https://github.com/mbozem/idempiere-print-template/tree/IDEMPIERE-7070 FreeMarker renderer integration branch]&lt;br /&gt;
* [https://github.com/mbozem/auler.gmbh.zugferdxinvoice/tree/agent/idempiere-14-compatibility ZUGFeRD/Factur-X integration branch]&lt;br /&gt;
&lt;br /&gt;
== Tracking information ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Item&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| Jira issue&lt;br /&gt;
| [https://idempiere.atlassian.net/browse/IDEMPIERE-7070 IDEMPIERE-7070]&lt;br /&gt;
|-&lt;br /&gt;
| Issue type&lt;br /&gt;
| Improvement&lt;br /&gt;
|-&lt;br /&gt;
| Priority&lt;br /&gt;
| Minor&lt;br /&gt;
|-&lt;br /&gt;
| Jira status&lt;br /&gt;
| Peer Review Queue&lt;br /&gt;
|-&lt;br /&gt;
| Reporter / assignee&lt;br /&gt;
| Markus Bozem&lt;br /&gt;
|-&lt;br /&gt;
| Created&lt;br /&gt;
| 29 July 2026&lt;br /&gt;
|-&lt;br /&gt;
| Pull request&lt;br /&gt;
| [https://github.com/idempiere/idempiere/pull/3318 #3318]&lt;br /&gt;
|-&lt;br /&gt;
| Pull request target&lt;br /&gt;
| &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Pull request status&lt;br /&gt;
| Open — not merged&lt;br /&gt;
|-&lt;br /&gt;
| Pull request scope as of 16 August 2026&lt;br /&gt;
| 37 changed files, 1,526 additions and 495 deletions&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Category:New Features]]&lt;br /&gt;
[[Category:New Features w14]]&lt;br /&gt;
[[Category:New Features Development]]&lt;br /&gt;
[[Category:New Features Technical]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=Category:New_Features_w13&amp;diff=23976</id>
		<title>Category:New Features w13</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=Category:New_Features_w13&amp;diff=23976"/>
		<updated>2026-08-16T10:50:24Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Features in version 13 Orion ==&lt;br /&gt;
&lt;br /&gt;
* [[:Category:New Features Architecture|'''A''' → Architecture]]&lt;br /&gt;
* [[:Category:New Features Development|'''D''' → Development]]&lt;br /&gt;
* [[:Category:New Features Functional‏‎|'''F''' → Functional]]&lt;br /&gt;
* [[:Category:New Features Security|'''S''' → Security]]&lt;br /&gt;
* [[:Category:New Features Technical‏‎|'''T''' → Technical]]&lt;br /&gt;
* [[:Category:New Features User Experience (Usability)|'''U''' → User Experience (Usability)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:New Features]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=Category:New_Features_w14&amp;diff=23975</id>
		<title>Category:New Features w14</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=Category:New_Features_w14&amp;diff=23975"/>
		<updated>2026-08-16T10:49:56Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: Created page with &amp;quot;== Features arriving in version 14 Dev Build ==  NOTE: Version 14 is still unreleased - it is available in the default branch of the source code repository and it can be teste...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Features arriving in version 14 Dev Build ==&lt;br /&gt;
&lt;br /&gt;
NOTE: Version 14 is still unreleased - it is available in the default branch of the source code repository and it can be tested in https://test.idempiere.org and https://test-oracle.idempiere.org&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* [[:Category:New Features Architecture|'''A''' → Architecture]]&lt;br /&gt;
* [[:Category:New Features Development|'''D''' → Development]]&lt;br /&gt;
* [[:Category:New Features Functional‏‎|'''F''' → Functional]]&lt;br /&gt;
* [[:Category:New Features Security|'''S''' → Security]]&lt;br /&gt;
* [[:Category:New Features Technical‏‎|'''T''' → Technical]]&lt;br /&gt;
* [[:Category:New Features User Experience (Usability)|'''U''' → User Experience (Usability)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:New Features]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=Setting_up_Eclipse&amp;diff=15574</id>
		<title>Setting up Eclipse</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=Setting_up_Eclipse&amp;diff=15574"/>
		<updated>2019-09-09T07:07:42Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- breadcrumb --&amp;gt;&lt;br /&gt;
&amp;lt;font size=-2&amp;gt;&lt;br /&gt;
&amp;amp;lArr;&lt;br /&gt;
[[Installing_iDempiere|Table of Contents]] |&lt;br /&gt;
[[Download the Code]] |&lt;br /&gt;
Setting up Eclipse |&lt;br /&gt;
[[Importing DB Seed Manually]]&lt;br /&gt;
&amp;amp;rArr;&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Materialize and build ==&lt;br /&gt;
&lt;br /&gt;
On the operating system navigate to the folder where you cloned the code, named above as $IDEMPIERE_REPOSITORY, f.e. /home/carlos/sources/myexperiment), and run the next command:&lt;br /&gt;
 cd $HOME/sources/myexperiment&lt;br /&gt;
 mvn verify -U&lt;br /&gt;
This command download all libraries on Bundle-ClassPath, build all projects and output binaries to org.idempiere.p2/target/products&lt;br /&gt;
&lt;br /&gt;
== Import projects in Eclipse ==&lt;br /&gt;
&lt;br /&gt;
* Open again the eclipse you configured with the workspace pointing to the folder where you cloned the code&lt;br /&gt;
* Turn off Project &amp;gt; Build automatically&lt;br /&gt;
* Navigate to File &amp;gt; Import&lt;br /&gt;
* Navigate to Maven &amp;gt; Existing Maven Projects&lt;br /&gt;
* Click Next button and Browse to $IDEMPIERE_REPOSITORY (must appear there by default)&lt;br /&gt;
* All projects must appear selected, click Finish to complete the import&lt;br /&gt;
* At the end all projects are loaded into workspace&lt;br /&gt;
&lt;br /&gt;
== Workspace settings ==&lt;br /&gt;
&lt;br /&gt;
* In eclipse, navigate to Window &amp;gt; Preferences &amp;gt; General &amp;gt; Workspace&lt;br /&gt;
* set &amp;quot;Text file encoding&amp;quot; to &amp;quot;UTF-8&amp;quot; (if default is different)&lt;br /&gt;
* set &amp;quot;New text file line delimiter&amp;quot; to &amp;quot;Unix&amp;quot; (if default is different)&lt;br /&gt;
* navigate to Window &amp;gt; Preferences &amp;gt; Java &amp;gt; Compiler&lt;br /&gt;
* set Java &amp;gt; Compiler &amp;gt; Compiler compliance level to &amp;quot;11&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Set target platform ==&lt;br /&gt;
&lt;br /&gt;
* Still in eclipse&lt;br /&gt;
* Navigate to File &amp;gt; Import&lt;br /&gt;
* Navigate to General &amp;gt; Existing Projects into Workspace&lt;br /&gt;
* Click Next button and Browse to $IDEMPIERE_REPOSITORY/org.idempiere.p2.targetplatform&lt;br /&gt;
* Click Finish to complete the import&lt;br /&gt;
&lt;br /&gt;
* Now open the file org.idempiere.p2.targetplatform.target within the project just imported org.idempiere.p2.targetplatform&lt;br /&gt;
** This is the default target platform definition with remote url&lt;br /&gt;
** Here eclipse takes some time downloading the required dependencies, you can prepare some Colombian coffee and wait patiently  :)&lt;br /&gt;
* At Target Editor, click the &amp;quot;Set as Active Target Platform&amp;quot; link&lt;br /&gt;
* Wait for Eclipse to finish downloading bundles onto target platform&lt;br /&gt;
** Click the &amp;quot;Reload&amp;quot; button if some download fail, until there are not red errors on the Locations list&lt;br /&gt;
&lt;br /&gt;
* At the end you can enabled back Project &amp;gt; Build automatically&lt;br /&gt;
* Restart eclipse in order to do a clean build&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
''This tutorial is brought to you by [[User:CarlosRuiz|Carlos Ruiz]] from [http://globalqss.com GlobalQSS].  Feel free to improve directly or suggest using the Discussion tab.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:User Manual|I]]&lt;br /&gt;
[[Category:Installation|E]] &lt;br /&gt;
[[Category:Installation Eclipse]] &lt;br /&gt;
[[Category:Development‏‎]]&lt;br /&gt;
[[Category:Developer documentation‏‎]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=Importing_DB_Seed_Manually&amp;diff=11269</id>
		<title>Importing DB Seed Manually</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=Importing_DB_Seed_Manually&amp;diff=11269"/>
		<updated>2017-01-02T11:48:20Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: Added link to modified script to apply patches from directory&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- breadcrumb --&amp;gt;&lt;br /&gt;
&amp;lt;font size=-2&amp;gt;&lt;br /&gt;
&amp;amp;lArr;&lt;br /&gt;
[[Installing_iDempiere|Table of Contents]] |&lt;br /&gt;
[[Setting up Eclipse]] |&lt;br /&gt;
Importing DB Seed Manually |&lt;br /&gt;
[[Running iDempiere within Eclipse]]&lt;br /&gt;
&amp;amp;rArr;&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Please note this procedure is intended for technical people wanting to configure iDempiere to run in Eclipse, there is an easier mechanism on the installers to create the database.&lt;br /&gt;
&lt;br /&gt;
Let's check the steps to create an idempiere postgres database:&lt;br /&gt;
&lt;br /&gt;
== Create adempiere user ==&lt;br /&gt;
&lt;br /&gt;
 sudo su - postgres&lt;br /&gt;
 psql -U postgres -c &amp;quot;CREATE ROLE adempiere SUPERUSER LOGIN PASSWORD 'adempiere'&amp;quot;&lt;br /&gt;
 logout&lt;br /&gt;
&lt;br /&gt;
'''WARNING:''' Of course this is an insecure password, it is recommended that you set up a proper password for this user account&lt;br /&gt;
&lt;br /&gt;
== Create the idempiere database ==&lt;br /&gt;
&lt;br /&gt;
 createdb  --template=template0 -E UNICODE -O adempiere -U adempiere idempiere&lt;br /&gt;
 psql -d idempiere -U adempiere -c &amp;quot;ALTER ROLE adempiere SET search_path TO adempiere, pg_catalog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
When asked the password please provide the adempiere password that you set up in the previous step.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' It is possible that you will confront problems related to pg_hba.conf configuration (/etc/postgresql/{version}/main/pg_hba.conf), it's beyond the scope of this tutorial to solve those problems at this moment, please check and configure proper permissions.  Also, this tutorial is intended to work on a postgres installed locally, if your postgres is elsewhere you must use corresponding -h (host) and -p (port) parameters for createdb and psql. If you receive the error '''createdb: could not connect to database template1: FATAL:  Peer authentication failed for user &amp;quot;adempiere&amp;quot;''' you might be able to add '''-h localhost''' to resolve quickly. For a more permanent solution visit [http://stackoverflow.com/questions/18664074/getting-error-peer-authentication-failed-for-user-postgres-when-trying-to-ge stackoverflow].&lt;br /&gt;
&lt;br /&gt;
== Installing UUID (required to apply some migration scripts) ==&lt;br /&gt;
&lt;br /&gt;
Some migration scripts references the function generate_uuid, unfortunately the way to install this is different on different postgres versions, so we were not able to include it by default on the DB seed, next are the instructions per version:&lt;br /&gt;
&lt;br /&gt;
* Remember from the prerequisites you must have installed postgresql-contrib to make the generate_uuid function to work&lt;br /&gt;
&lt;br /&gt;
=== For postgresql versions 8.X, 9.0 ===&lt;br /&gt;
&lt;br /&gt;
* Run via psql the script provided at db/ddlutils/postgresql/uuid-ossp.sql&lt;br /&gt;
** psql -d idempiere -U adempiere -f db/ddlutils/postgresql/uuid-ossp.sql&lt;br /&gt;
&lt;br /&gt;
=== For postgresql 9.1 and above ===&lt;br /&gt;
&lt;br /&gt;
* Run the command CREATE EXTENSION &amp;quot;uuid-ossp&amp;quot; using psql&lt;br /&gt;
** psql -d idempiere -U adempiere -c 'CREATE EXTENSION &amp;quot;uuid-ossp&amp;quot;'&lt;br /&gt;
&lt;br /&gt;
== Import the seed ==&lt;br /&gt;
&lt;br /&gt;
== PostgreSQL 8.4 or 9.0 ==&lt;br /&gt;
&lt;br /&gt;
This tutorial uses postgres 8.4 - please note the following instructions are applicable if you use postgresql 8.4 or 9.0, for 9.1 or higher see below.&lt;br /&gt;
&lt;br /&gt;
 cd /tmp&lt;br /&gt;
 jar xvf $IDEMPIERE_REPOSITORY/org.adempiere.server-feature/data/seed/Adempiere_pg84.jar&lt;br /&gt;
 psql -d idempiere -U adempiere -f Adempiere_pg84.dmp&lt;br /&gt;
&lt;br /&gt;
You must replace $IDEMPIERE_REPOSITORY with the folder of your cloned repository.&lt;br /&gt;
&lt;br /&gt;
== PostgreSQL 9.1 or higher ==&lt;br /&gt;
&lt;br /&gt;
 cd /tmp&lt;br /&gt;
 jar xvf $IDEMPIERE_REPOSITORY/org.adempiere.server-feature/data/seed/Adempiere_pg.jar&lt;br /&gt;
 psql -d idempiere -U adempiere -f Adempiere_pg.dmp&lt;br /&gt;
&lt;br /&gt;
== Applying additional migration scripts ==&lt;br /&gt;
&lt;br /&gt;
See also [http://wiki.idempiere.org/en/Upgrade_from_1.0c_to_2.0#Periodic_upgrade_within_same_version Periodic upgrade within same version]&lt;br /&gt;
&lt;br /&gt;
=== Manual way ===&lt;br /&gt;
&lt;br /&gt;
Now, you must check if there are new migration scripts after the last applied, you can check the migration scripts this way:&lt;br /&gt;
&lt;br /&gt;
 ls $IDEMPIERE_REPOSITORY/migration/i1.0c-release/postgresql&lt;br /&gt;
&lt;br /&gt;
And compare the list of migration scripts with the contents of your AD_MigrationScript table, execute this in postgres:&lt;br /&gt;
&lt;br /&gt;
 select name from ad_migrationscript order by 1&lt;br /&gt;
&lt;br /&gt;
Please note that you must do this check every time you upgrade your version.&lt;br /&gt;
&lt;br /&gt;
If you find there are unapplied migration scripts then you can apply them with:&lt;br /&gt;
&lt;br /&gt;
 psql -d idempiere -U adempiere -f $IDEMPIERE_REPOSITORY/migration/i1.0c-release/postgresql/20130627011956_ProbableFutureMigrationScript.sql&lt;br /&gt;
&lt;br /&gt;
Please check carefully the output of the migration scripts, if you find errors please ask for help on the [http://groups.google.com/group/idempiere support forums]&lt;br /&gt;
&lt;br /&gt;
You must repeat the previous for each unapplied migration scripts, it's recommended also to take note of the last applied for future reference.&lt;br /&gt;
&lt;br /&gt;
At the end you can run the post-migration processes like this:&lt;br /&gt;
&lt;br /&gt;
 cat $IDEMPIERE_REPOSITORY/migration/processes_post_migration/postgresql/*.sql | psql -d idempiere -U adempiere&lt;br /&gt;
&lt;br /&gt;
=== Automated way (linux) ===&lt;br /&gt;
&lt;br /&gt;
There are two scripts contributed that help with this task:&lt;br /&gt;
&lt;br /&gt;
* [http://bitbucket.org/CarlosRuiz_globalqss/idempiere-stuff/raw/tip/script_to_sync_db/syncApplied.sh syncApplied.sh for PostgreSQL] or [http://markus-bozem.de/?p=1 modified syncApplied.sh for PostgreSQL] &lt;br /&gt;
* [http://bitbucket.org/CarlosRuiz_globalqss/idempiere-stuff/raw/tip/script_to_sync_db/syncApplied_ora.sh syncApplied_ora.sh for Oracle]&lt;br /&gt;
&lt;br /&gt;
You can download the corresponding file, edit the variables on top to fit your configuration and then you can run periodically the script every time you update source code.&lt;br /&gt;
&lt;br /&gt;
''This tutorial is brought to you by [[User:CarlosRuiz|Carlos Ruiz]] from [http://globalqss.com GlobalQSS].  Feel free to improve directly or suggest using the Discussion tab.&lt;br /&gt;
&lt;br /&gt;
[[Category:User Manual|I]]&lt;br /&gt;
[[Category:Installation|E]] &lt;br /&gt;
[[Category:Installation Eclipse]] &lt;br /&gt;
[[Category:Development‏‎]]&lt;br /&gt;
[[Category:Developer documentation‏‎]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8690</id>
		<title>User:Mbozem</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8690"/>
		<updated>2015-09-07T15:04:41Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: /* Kontaktdaten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Kontaktdaten ==&lt;br /&gt;
&lt;br /&gt;
Markus Bozem&lt;br /&gt;
&lt;br /&gt;
Geschwister-Scholl-Platz 3&lt;br /&gt;
&lt;br /&gt;
63741 Aschaffenburg&lt;br /&gt;
&lt;br /&gt;
[mailto:mbozem@bozem.de Email]&lt;br /&gt;
[http://www.markus-bozem.de Homepage]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8689</id>
		<title>User:Mbozem</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8689"/>
		<updated>2015-09-07T15:03:35Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Kontaktdaten ==&lt;br /&gt;
&lt;br /&gt;
Markus Bozem&lt;br /&gt;
Geschwister-Scholl-Platz 3&lt;br /&gt;
63741 Aschaffenburg&lt;br /&gt;
&lt;br /&gt;
[mailto:mbozem@bozem.de Email]&lt;br /&gt;
[http://www.markus-bozem.de Homepage]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8688</id>
		<title>User:Mbozem</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8688"/>
		<updated>2015-09-07T15:01:57Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Homepage&lt;br /&gt;
&lt;br /&gt;
[http://www.markus-bozem.de Homepage]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8687</id>
		<title>User:Mbozem</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=User:Mbozem&amp;diff=8687"/>
		<updated>2015-09-07T15:01:34Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: Created page with &amp;quot;Homepage  [http://www.markus-bozem.de]&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Homepage&lt;br /&gt;
&lt;br /&gt;
[http://www.markus-bozem.de]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
	<entry>
		<id>https://wiki.idempiere.org/w-en/index.php?title=IDempiere_World_Conference_2015&amp;diff=8596</id>
		<title>IDempiere World Conference 2015</title>
		<link rel="alternate" type="text/html" href="https://wiki.idempiere.org/w-en/index.php?title=IDempiere_World_Conference_2015&amp;diff=8596"/>
		<updated>2015-08-31T12:08:15Z</updated>

		<summary type="html">&lt;p&gt;Mbozem: /* Attending Conference */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Time=&lt;br /&gt;
&lt;br /&gt;
* September 3/4 (thursday/friday)&lt;br /&gt;
* best practices for community plugins&lt;br /&gt;
&lt;br /&gt;
* Paid Developer [[iDempiere workshop 2015|Workshop]] next week after the conference at the Bischofstein Castle near Koblenz (september 7-11)&lt;br /&gt;
&lt;br /&gt;
=Venue=&lt;br /&gt;
&lt;br /&gt;
The conference is in [http://en.wikipedia.org/wiki/Krefeld Krefeld], Germany. As the location I booked the same room where we held the successful [http://wiki.idempiere.org/en/IDempiere_World_Conference_2013 IDempiere World Conference 2013], [http://diekulisse.eu/ Die Kulisse].&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Die Kulisse&amp;quot; is a big, nice restaurant that lies in opposite to the back entrance of the main train station of Krefeld. You can reach it easily per Train (Krefeld Hbf), per S-Bahn (City-Train) from Düsseldorf/Düsseldorf Airport (U76) or use the big parking site behind the &amp;quot;Fabrik Heeder&amp;quot; Theater.&lt;br /&gt;
&lt;br /&gt;
The workshop is 170 kilometers away from Krefeld near Hatzenport (a small village near [https://en.wikipedia.org/wiki/Koblenz Koblenz]) in the the[https://en.wikipedia.org/wiki/Bischofstein_Castle_(Germany) Bischofstein Castle]. That is in the Moselle Valley which is one of the most lovely landscapes in germany (and not far away from the [https://en.wikipedia.org/wiki/Deutsches_Eck Deutsches Eck] and the [https://en.wikipedia.org/wiki/Middle_Rhine Middle Rhine Valley]). This location allows at the same time to relax and to focus on our software. Most of us will sleep at the castle and be together for the evening to become acquainted with each other and share more knowledge and stories.&lt;br /&gt;
&lt;br /&gt;
The castle is normally used as a youth hostel. You can see some of the rooms [http://www.gruppenhaus.de/burg-bischofstein-hatzenport-hs2843.html here].&lt;br /&gt;
&lt;br /&gt;
= Planned talks =&lt;br /&gt;
&lt;br /&gt;
There are 11 hours of talks and discussions during both conference days. We have a call for papers here: https://groups.google.com/forum/#!topic/idempiere/l0wQxo-EUx8&lt;br /&gt;
&lt;br /&gt;
[[File:Tentative_schedule.png]]&lt;br /&gt;
&lt;br /&gt;
= Attending Conference=&lt;br /&gt;
# [[User:TBayen|Thomas Bayen]], BX Service GmbH &amp;amp; Jakob Bayen KG, Germany&lt;br /&gt;
# [[User:CarlosRuiz|Carlos Ruiz]], Colombia&lt;br /&gt;
# [[User:Hriess|Hans Riess]], Germany&lt;br /&gt;
# [[Usuario:luisamesty|Luis Amesty]], Venezuela, España&lt;br /&gt;
# [[User:Chuckboecking|Chuck Boecking]], Austin, TX, United States&lt;br /&gt;
# [[User:HenrietteBaumann|Henriette Baumann]], Switzerland&lt;br /&gt;
# [[User:gabrielfranh|Gabriel F. Habermann]], devCoffee, Brazil&lt;br /&gt;
# [[User:muriloht|Murilo H. Torquato]], devCoffee, Brazil&lt;br /&gt;
# [[User:smartjsp|Pedro Rozo]], SmartJSP, Colombia&lt;br /&gt;
# [[User:Nbe222|Norbert Bede]], MULTIMAGE, Slovakia&lt;br /&gt;
# Philippe Baumann, Switzerland&lt;br /&gt;
# [[User:Diego.ruiz|Diego Ruiz]], Bx Service GmbH, Germany, Colombia&lt;br /&gt;
# [[User:Pshepetko|Peter Shepetko]], Ukraine&lt;br /&gt;
# [[User:a42niem|Dirk Niemeyer]], Germany&lt;br /&gt;
# [[User:a42jannis|Jannis Edzards]], Germany&lt;br /&gt;
# [[User:Eugene Barg|Eugene Barg]], Russia&lt;br /&gt;
# [[User:afildan|Anton Fildan]], Austria&lt;br /&gt;
# [[User:Dpansheriya|Deepak Pansheriya]], India&lt;br /&gt;
# [[User:dantam|Daniel Tamm]], Sweden&lt;br /&gt;
# [[User:spok|Sergey Polyarus]], Blue Water Parts, France&lt;br /&gt;
# [[User:Aranvier|Alain Ranvier]], Blue Water Parts, France&lt;br /&gt;
#  Hervé Bernard, Positiv Buildings (Pool Repair Center), France&lt;br /&gt;
# Ignacio Soriano, Capristo Automotive, Germany&lt;br /&gt;
# Longo Marco, Italy&lt;br /&gt;
# Tomáš Švikruha, MULTIMAGE, Slovakia&lt;br /&gt;
# Raouf Mazouz, France&lt;br /&gt;
# [[User:Hideaki_Hagiwara|Hideaki Hagiwara]] , Japan&lt;br /&gt;
# Peter Springborn, Berlin, Germany&lt;br /&gt;
# [[User:Nilskm|Nils Kohlström]], Sweden&lt;br /&gt;
# Shaun Melville - South Africa&lt;br /&gt;
# Honoré Tiako – Germany&lt;br /&gt;
# Mirko Trinajstić - BX Service GmbH, Germany&lt;br /&gt;
# Steven Sackett - Adaxa, Australia&lt;br /&gt;
# Lukas Nosalek, Czech Republic&lt;br /&gt;
# Michal Žilinčár, Zitec s.r.o., Slovakia&lt;br /&gt;
# Ladislav Nagy, Bigware, Slovakia&lt;br /&gt;
# [[User:isener|Ismet Sener]], Apeldoorn, Netherlands&lt;br /&gt;
# [[User:mbozem|Markus Bozem]], Aschaffenburg, Germany&lt;br /&gt;
&lt;br /&gt;
** please add your name here if you plan to attend&lt;br /&gt;
&lt;br /&gt;
=Accommodation=&lt;br /&gt;
&lt;br /&gt;
Krefeld is about 20 km from the District Capital and fair location Düsseldorf (International Airport DUS), about 50km away from Cologne and 20km to the Netherland border. It is well equiped with Hotels of different categories. Ask me (Thomas Bayen) for help, I know many hotels through my business. Or just have a look at this list:&lt;br /&gt;
&lt;br /&gt;
The conference location is direct to the Krefeld Hauptbahnhof (Main Train Station). Next to it is the Ibis Hotel, which has small and cheap rooms:&lt;br /&gt;
* http://www.accorhotels.com/de/hotel-3083-ibis-budget-krefeld-messe-duesseldorf/index.shtml&lt;br /&gt;
&lt;br /&gt;
* if you like it small and beautiful&lt;br /&gt;
** http://www.cafe-saint-denis.de/&lt;br /&gt;
** http://www.montenegrokrefeld.de/&lt;br /&gt;
* If you like unpersonal but big franchise hotels...&lt;br /&gt;
** http://www.accorhotels.com/de/hotel-5404-mercure-parkhotel-krefelder-hof/index.shtml&lt;br /&gt;
** http://www.accorhotels.com/de/hotel-5402-mercure-tagungs-landhotel-krefeld/index.shtml&lt;br /&gt;
&lt;br /&gt;
The workshop/software training is not in Krefeld but about 170 km away at the Bischofstein Castle. Please contact Thomas Bayen for accomodation there. Most of us will sleep at the castle and be together for the evening to become acquainted with each other and share more knowledge and stories.&lt;br /&gt;
&lt;br /&gt;
The castle is normally used as a youth hostel. You can see some of the rooms [http://www.gruppenhaus.de/burg-bischofstein-hatzenport-hs2843.html here]. As an alternative we can help you book a bed in a nearby hotel (for example [http://www.winzerhof-gietzen.com/ Winzerhof Gietzen]).&lt;br /&gt;
&lt;br /&gt;
=Presenters=&lt;br /&gt;
* [[User:CarlosRuiz|Carlos Ruiz]]&lt;br /&gt;
* [[User:Diego.ruiz|Diego Ruiz]]&lt;br /&gt;
* [[User:Chuckboecking|Chuck Boecking]]&lt;br /&gt;
* [[User:TBayen|Thomas Bayen]]&lt;br /&gt;
* Steven Sackett&lt;br /&gt;
* [[User:Dpansheriya|Deepak Pansheriya]]&lt;br /&gt;
* Pedro Rozo&lt;br /&gt;
* [[User:muriloht|Murilo H. Torquato]]&lt;br /&gt;
* [[User:Hriess|Hans Riess]]&lt;br /&gt;
* [[User:a42niem|Dirk Niemeyer]]&lt;br /&gt;
* [[User:Nbe222|Norbert Bede]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
** please contact coordination team if you have interesting applications, plugins, visions and/or plans to present&lt;br /&gt;
&lt;br /&gt;
= Social Events =&lt;br /&gt;
&lt;br /&gt;
For the evening there is a big Dinner together with all conference participants. That will be in the same location as the conference (or outside if weather allows that).&lt;br /&gt;
&lt;br /&gt;
There may be some social events for the weekend. When you come a long way to visit germany you might be interested to spend the weekend together with other community members. It may be we visit the [https://en.wikipedia.org/wiki/Altstadt_(D%C3%BCsseldorf) Düsseldorf Altstadt] or we repeat the famous community visit in the [http://jazzkeller.info/ Jazzkeller Krefeld]. These events are private organized and you are free to join. As this is of most interest for the workshop participants see [[iDempiere workshop 2015]] and/or ask Thomas Bayen about that.&lt;br /&gt;
&lt;br /&gt;
= Developer Workshop Plan =&lt;br /&gt;
&lt;br /&gt;
See [[iDempiere workshop 2015|Workshop]] for more information about the workshop agenda.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Costs =&lt;br /&gt;
&lt;br /&gt;
The '''fee for the conference''' is 30 € for each day and 30 € for the evening event, that are all together '''90,- €''' for the conference.&lt;br /&gt;
&lt;br /&gt;
The '''weekend events''' are private organized. You can join us '''for free'''. It might be there is some entrance fee or beer to pay. ;-)&lt;br /&gt;
&lt;br /&gt;
The '''user workshop''' and the '''developer workshop with Carlos Ruiz''' costs '''1.000,- € per five days''' per person. Shorter bookings may be possible - depending on your interest. Please ask Carlos Ruiz or Thomas Bayen.&lt;br /&gt;
&lt;br /&gt;
=Coordination Team=&lt;br /&gt;
* [[User:Diego.ruiz|Diego Ruiz]]&lt;br /&gt;
* [[User:TBayen|Thomas Bayen]]&lt;br /&gt;
* [[User:CarlosRuiz|Carlos Ruiz]]&lt;br /&gt;
&lt;br /&gt;
=Evening and weekend events=&lt;br /&gt;
&lt;br /&gt;
Events will be fixed if enough people are on the list. We have some possibilities like:&lt;br /&gt;
* visit in the [http://de.wikipedia.org/wiki/Burg_Linn Linn Castle] and its surrounding park&lt;br /&gt;
* Krefeld has a [http://de.wikipedia.org/wiki/Flugplatz_Krefeld-Egelsberg Airport] for Gliders/Sailplanes. If weather is good we could arrange some flights&lt;br /&gt;
* We have a germany-wide famous location for Jazz music, the [http://www.jazzkeller.info/ Jazz-Cellar]&lt;br /&gt;
* real linuxers will like the [http://portal.krefeld-pinguine.de/ Krefeld Penguins] or the [http://www.krefelderzoo.com/bereiche/zoofreunde-ev/pinguinart.html Penguins in Krefeld] :-)&lt;br /&gt;
* [http://www.kulturfabrik-krefeld.de/ Party]&lt;br /&gt;
* [http://www.theater-kr-mg.de/ Culture]&lt;br /&gt;
* see here for more info: [[iDempiere workshop 2015|Workshop]]&lt;br /&gt;
&lt;br /&gt;
=Previous conferences=&lt;br /&gt;
* [[IDempiere_World_Conference_2013]]&lt;br /&gt;
* [[IDempiere_Workshop_2014]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Community|M]]&lt;br /&gt;
[[Category:Meetings|5]]&lt;br /&gt;
[[Category:Meetings 2015]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[en:IDempiere World Conference 2015]]&lt;br /&gt;
[[de:Weltkonferenz 2015]]&lt;/div&gt;</summary>
		<author><name>Mbozem</name></author>
	</entry>
</feed>