peppol-reporting
June 3, 2026 · View on GitHub
If this project saved you some time or made your day a little easier, a star would mean a lot — it helps others find it too.
Peppol Reporting support library. Peppol Reporting is the process of collecting, aggregating and transmitting Peppol Reports to OpenPeppol.
peppol-reporting is part of my Peppol solution stack. See https://github.com/phax/peppol for other components and libraries in that area.
This library supports the following reports:
- Peppol Transaction Statistics Report 1.0.1 (March 2023)
- Specification link: https://docs.peppol.eu/edelivery/specs/reporting/tsr/
- Peppol End User Statistics Report 1.1.0 (June 2023)
- Specification link: https://docs.peppol.eu/edelivery/specs/reporting/eusr/
- OpenPeppol Operational Guideline for Reporting implementation: https://docs.peppol.eu/edelivery/
This library does not deal with the transmission of Reports. That needs to be done with phase4 or another AS4 solution. See the phase4 Wiki for detailed guidance on integration with this project.
This library requires Java 17 and Maven to build.
How to use it
This library offers a Java domain model for EUSR and TSR reports.
Note: phase4 v2.2.2 and onwards has direct support for this project.
Overview images



Data collection
Data collection needs to happen inside your Access Point instances.
The data for reporting needs to be collected in instances of class PeppolReportingItem.
For each sent or received Peppol transmission, such a PeppolReportingItem needs to be collected, and persisted.
Each PeppolReportingItem consists of the following elements:
OffsetDateTime m_aExchangeDTUTC- timing of the exchange in UTC; for TSR and EUSREReportingDirection m_eDirection- direction of the exchange; for TSR and EUSRString m_sC2ID- Peppol Seat ID of C2; for TSR onlyString m_sC3ID- Peppol Seat ID of C3; for TSR onlyString m_sDocTypeIDSchemeandString m_sDocTypeIDValue- Document Type ID of the exchange; for TSR and EUSRString m_sProcessIDSchemeandString m_sProcessIDValue- Process ID of the exchange; for TSR and EUSRString m_sTransportProtocol- the transport protocol used; for TSR onlyString m_sC1CountryCode- the country code of C1; for TSR and EUSRString m_sC4CountryCode- the country code of C4 - only required for received messages; for TSR and EUSRString m_sEndUserID- the end user ID to aggregate on - this ID is not part of any report; for EUSR only
To facilitate this collection, the submodule peppol-reporting-api exists.
Data storage
The created reporting item must be stored somewhere, to be able to retrieve them later.
This project comes with different backends for storing PeppolReportingItem objects, each in a separate submodule.
Each submodule is described below.
To choose a submodule, it needs to be added as a Maven dependency. The main logic is loaded via SPI. Please make sure to only use one submodule at a time - storing to multiple backends is currently not supported out of the box.
Alternatively you can implement your own Reporting backend implementation, by implementing the SPI interface
com.helper.peppol.reporting.api.backend.IPeppolReportingBackendSPI defined in the peppol-reporting-api submodule.
Storage in MongoDB
Submodule peppol-reporting-backend-mongodb stores data in a MongoDB.
This submodule was introduced in version 2.1.0.
It creates one collection called: reporting-items
It supports the following configuration properties:
peppol.reporting.mongodb.connectionstring: the connection string to use to connect to MongoDBpeppol.reporting.mongodb.dbname: the MongoDB database name to usepeppol.reporting.mongodb.collection(since v2.2.1): the MongoDB collection name to use. Defaults toreporting-items.
Storage in Redis
Submodule peppol-reporting-backend-redis stores data in Redis. Make sure you use persistent storage for this one.
This submodule was introduced in version 2.1.0.
The used Redis keys are:
peppol:reporting:itemidx- counter for unique IDspeppol:reporting:item:*- represents a single reporting item hash mappeppol:reporting:*- contains a list of reporting item keys of a single day
It supports the following configuration properties:
peppol.reporting.redis.host: the Redis host to connect topeppol.reporting.redis.port: the Redis port to connect topeppol.reporting.redis.user(since v2.2.3; optional): the username used to connect to the Redis serverpeppol.reporting.redis.password(since v2.2.3; optional): the password used to connect to the Redis server
Storage in CSV file
Submodule peppol-reporting-backend-csv stores data in a CSV file.
This submodule was introduced in version 2.2.4.
It supports the following configuration properties:
peppol.reporting.csv.filename: the CSV filename to store the entries inpeppol.reporting.csv.separator-char(optional): the CSV cell separator character to use. The default is,peppol.reporting.csv.quote-char(optional): the CSV quote character to use. The default is"peppol.reporting.csv.escape-char(optional): the CSV escape character to use. The default is\
Storage in SQL databases
Submodule peppol-reporting-backend-sql stores data in relational databases.
This submodule was introduced in version 3.0.1.
It supports the following configuration properties:
peppol.reporting.jdbc.database-type: the SQL database type to operate on. Currently supported arepostgresql,mysqlandsqlserver. The value is case-insensitive.peppol.reporting.jdbc.driver: contains the fully qualified class name of the JDBC driver to be used. E.g.org.postgresql.Driverfor PostgreSQL,com.mysql.cj.jdbc.Driverfor MySQL orcom.microsoft.sqlserver.jdbc.SQLServerDriverfor SQL Serverpeppol.reporting.jdbc.url: contains the full JDBC connection URL to connect to the databasepeppol.reporting.jdbc.user(optional): the database username to usepeppol.reporting.jdbc.password(optional): the database password to usepeppol.reporting.jdbc.schema(optional): the database schema to usepeppol.reporting.jdbc.execution-time-warning.enabled(optional): iftrueenables warning logging if an SQL command takes too long to execute. Defaults totrue.peppol.reporting.jdbc.execution-time-warning.ms(optional): the number of milliseconds after the which an SQL execution will trigger an execution time warning. Defaults to1000which is one second.peppol.reporting.jdbc.debug.connections(optional): iftrueenables logging of SQL connection handling. Defaults tofalse.peppol.reporting.jdbc.debug.transactions(optional): iftrueenables logging of SQL transactions. Defaults tofalse.peppol.reporting.jdbc.debug.sql(optional): iftrueenables logging of SQL statements. Defaults tofalse.
Database change management is done with the Open Source version of Flyway. All the Flyway DDL scripts are available in the folder https://github.com/phax/peppol-reporting/tree/main/peppol-reporting-backend-sql/src/main/resources/db
It can be configured as followed:
peppol.reporting.flyway.enabled:trueif Flyway should be enabled,falseif not. Defaults totrue.peppol.reporting.flyway.jdbc.url(optional): allows a specific JDBC URL for usage with Flyway. If none is provided, the value ofpeppol.reporting.jdbc.urlis used instead.peppol.reporting.flyway.jdbc.user(optional): allows a specific JDBC username for usage with Flyway. If none is provided, the value ofpeppol.reporting.jdbc.useris used instead.peppol.reporting.flyway.jdbc.password(optional): allows a specific JDBC password for usage with Flyway. If none is provided, the value ofpeppol.reporting.jdbc.passwordis used instead.peppol.reporting.flyway.jdbc.schema-create(optional):trueif the DB schema as defined inpeppol.reporting.jdbc.schemashould be automatically created by Flyway. Defaults tofalse.peppol.reporting.flyway.baseline.version(optional): the Flyway baseline version to use. Defaults to0.peppol.reporting.flyway.history-table(since v4.1.3; optional): the name of the Flyway history table. Defaults toflyway_schema_history.peppol.reporting.flyway.debug-mode(since v4.1.3; optional):trueto enable Flyway debug mode. Defaults tofalse.peppol.reporting.flyway.repair-mode(since v4.1.3; optional):trueto enable Flyway repair mode. Defaults tofalse.
By default it is not bound to any specific DB engine, so you need to provide the necessary driver dependency manually. PostgreSQL:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>x.y.z</version>
</dependency>
MySQL:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>x.y.z</version>
</dependency>
SQL Server:
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>x.y.z</version>
</dependency>
Storage in memory
Submodule peppol-reporting-backend-inmemory stores data in memory only and is not persistent.
This submodule was introduced in version 2.1.1.
This module is mainly meant for testing purposes.
Data aggregation
To aggregate data for a single Reporting Period, all the matching PeppolReportingItem objects need to be collected first.
All the matching items need to be fed into the respective report builder.
Via the builder EndUserStatisticsReport.builder (), the report of type EndUserStatisticsReportType (EUSR) can be created.
Via the builder TransactionStatisticsReport.builder (), the report of type TransactionStatisticsReportType (TSR) can be created.
Batched (streaming) aggregation to prevent Out-Of-Memory errors
When working with large datasets (e.g., millions of records stored in MongoDB or another database),
loading all PeppolReportingItem objects into memory at once can cause Out-Of-Memory errors.
To handle this, the library provides accumulator classes that let you feed items in batches:
EUSRReportingItemAccumulator– for EUSR reportsTSRReportingItemAccumulator– for TSR reports
Both classes are not thread-safe; do not share an instance across threads without external synchronisation.
Example: paged EUSR aggregation (e.g., from MongoDB)
// 1. Build the report header
EndUserStatisticsReportType aReport = EndUserStatisticsReport.builder ()
.monthOf (aNow)
.reportingServiceProviderID (MY_SPID)
.build (); // note: reportingItemList() is NOT called - we fill the report ourselves
// 2. Create the accumulator and feed pages of items
EUSRReportingItemAccumulator accumulator = new EUSRReportingItemAccumulator ();
int pageSize = 1000;
int pageIndex = 0;
List<PeppolReportingItem> page;
do {
page = myRepository.findByMonth (yearMonth, pageIndex, pageSize); // your query
for (PeppolReportingItem item : page)
accumulator.accept (item);
pageIndex++;
} while (!page.isEmpty ());
// 3. Write the aggregated data into the report
accumulator.fillReport (aReport);
Example: paged TSR aggregation
TransactionStatisticsReportType aReport = TransactionStatisticsReport.builder ()
.monthOf (aNow)
.reportingServiceProviderID (MY_SPID)
.build ();
TSRReportingItemAccumulator accumulator = new TSRReportingItemAccumulator ();
int pageSize = 1000;
int pageIndex = 0;
List<PeppolReportingItem> page;
do {
page = myRepository.findByMonth (yearMonth, pageIndex, pageSize);
for (PeppolReportingItem item : page)
accumulator.accept (item);
pageIndex++;
} while (!page.isEmpty ());
accumulator.fillReport (aReport);
The output produced by the accumulator-based approach is functionally identical to using
EndUserStatisticsReport.builder().reportingItemList(allItems).build() or the equivalent TSR builder —
all FullSet/Total counts and all Subset/Subtotal entries will match exactly.
Report XML Serialization
The JAXB generated domain model classes reside in the packages com.helger.peppol.reporting.jaxb.eusr.v110 and com.helger.peppol.reporting.jaxb.tsr.v101.
This domain model can be read from and written to XML documents via the marshaller classes EndUserStatisticsReport110Marshaller and TransactionStatisticsReport101Marshaller.
Report Validation
Additionally, the Schematron compatibility can be verified using the classes EndUserStatisticsReportValidator and TransactionStatisticsReportValidator.
All checks are performed against the default Schematrons provided by OpenPeppol.
Glossary
- EUSR - End User Statistics Report
- TSR - Transaction Statistics Report
- Report - Document containing OpenPeppol reporting information
- Reporting - The process of transmitting a Report to OpenPeppol
- Reporting Period - The period for which reporting data is to be collected and transmitted to OpenPeppol
Maven usage
Add the following to your pom.xml to use this artifact, replacing x.y.z with the real version:
<dependency>
<groupId>com.helger.peppol</groupId>
<artifactId>peppol-reporting</artifactId>
<version>x.y.z</version>
</dependency>
Usage as Maven BOM:
<dependency>
<groupId>com.helger.peppol</groupId>
<artifactId>peppol-reporting-parent-pom</artifactId>
<version>x.y.z</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Note: all v1.x releases used the group ID com.helger only.
News and Noteworthy
v4.1.4 - 2026-05-20
- Removed OSGI bundling
- Added
EUSRReportingItemAccumulatorandTSRReportingItemAccumulatorto support batched/streaming aggregation, preventing OOM errors when processing large datasets. See #18 - thx @jaskiratsingh1792
v4.1.3 - 2026-04-12
- Added new submodule
peppol-reporting-testcontaining shared SPI contract tests (AbstractPeppolReportingBackendSPITest) that all backend implementations can extend to ensure consistent behaviour - (InMemory)
isInitialized()now tracks real initialization state instead of always returningtrue;shutdownBackend()clears stored data and resets to uninitialized; store and query calls beforeinitBackend()now throwIllegalStateException, consistent with all other backends - (Redis) Fixed item ordering within a single day: changed
LPUSHtoRPUSHso thatLRANGE 0 -1returns items in insertion order instead of reverse insertion order - Replaced individual ad-hoc test classes with thin subclasses of the shared
AbstractPeppolReportingBackendSPITest, providing extensive contract tests per backend covering lifecycle, validation, date-range semantics, non-eligible doctype filtering, and multiset store/retrieve consistency - (SQL)
ReportingFlywayMigratornow honours theFlywayConfigurationhistory table setting - (SQL) Updated to ph-db 8.2.0, using the new shared
FlywayMigrationRunnerfromph-db-flyway
v4.1.2 - 2026-03-28
- (SQL) Updated Flyway to 12.2.0
- Added SQL Server to CI build pipeline
v4.1.1 - 2026-03-07
- (Redis) Updated to Jedis 7.2.0 and using
RedisClientinstead ofJedisandJedisPool - (SQL) Added support for SQL Server as a database backend
v4.1.0 - 2025-11-16
- Updated to ph-commons 12.1.0
- Using JSpecify annotations
v4.0.1 - 2025-09-19
- (SQL) Updated to ph-db 8.0.1 to fix an error with schema name masking for MySQL
- (SQL) Removed
PeppolReportingBackendSqlSPI.getTableNamePrefixin favour ofDBSystemHelper.getTableNamePrefix(incompatible change)
v4.0.0 - 2025-08-27
- Requires Java 17 as the minimum version
- Updated to ph-commons 12.0.0
- Removed all code marked as deprecated for removal
- (SQL) Updated to Flyway 11.x
v3.1.0 - 2025-04-11
- (SQL) Requires ph-db 7.1.0
- [MongoDB] Extended
PeppolReportingBackendMongoDBSPIAPI - (SQL) Renamed class
EDatabaseTypetoEReportingDatabaseType(internal backwards incompatible change) - (SQL) Renamed class
FlywayMigratortoReportingFlywayMigrator(internal backwards incompatible change) - (SQL) Removed class
ReportingFlywayConfigurationin favour ofReportingFlywayConfigurationBuilder - (SQL) Replaced class
EReportingDatabaseTypewithEDatabaseSystemTypefrom ph-db - (SQL) Reworked class
ReportingJdbcConfigurationto be based on a new shared class from ph-db
v3.0.3 - 2024-11-27
- Calling the
PeppolReportingHelper.isDocumentTypeEligableForReportingmethod in all backends to avoid the need for outside filtering
v3.0.2 - 2024-10-31
- Added new method
PeppolReportingBackend.setBackendService(IPeppolReportingBackendSPI)to explicitly set the backend - (CSV) Added missing write locking in CSV backend
v3.0.1 - 2024-08-12
- Added new submodule
peppol-reporting-backend-sqlto support PostgreSQL and MySQL
v3.0.0 - 2024-06-28
- Extracted
peppol-reporting-datatypessubmodule - Extracted
peppol-reporting-testfilessubmodule - Changed the Java package names from
com.helper.*tocom.helger.*- LOL
v2.2.5 - 2024-03-29
- Updated to ph-commons 11.1.5
- Ensured Java 21 compatibility
v2.2.4 - 2024-03-21
- Added new submodule
peppol-reporting-backend-csvthat uses a CSV file as the backend to store reporting items
v2.2.3 - 2024-03-05
- Added the possibility to provide username and password via configuration for the Redis backend. See PR #13 - thx @TaKO8Ki
v2.2.2 - 2024-01-29
- Moved the method
PeppolReportingItem.isValidCountryCode(String)to classPeppolReportingHelper - Added a constant
CPeppolReporting.REPLACEMENT_COUNTRY_CODEfor theZZcode for invalid incoming country codes - Added a constant
CPeppolReporting.OPENPEPPOL_PARTICIPANT_IDfor the default receiver PID
v2.2.1 - 2023-12-31
- Made the collection name customizable in the MongoDB backend
- Fixed an error in iterating in the "in-memory" backend when only entries from the last day of the period are present
v2.2.0 - 2023-12-07
- Modified classes
EUSRReportingItemListandTSRReportingItemListso that the list is only iterated once and is based onIterable. Backwards incompatible change. - Extended class
IPeppolReportingBackendSPIwith methoditerateReportingItemsto be able to lazily iterate over a data source. See #2 - thx @iansmirlis
v2.1.6 - 2023-11-10
- Updated EUSR Schematron to v1.1.4
v2.1.5 - 2023-11-02
- Updated EUSR Schematron to v1.1.3 and TSR Schematron to v1.0.4
v2.1.4 - 2023-10-12
- Updated EUSR Schematron to v1.1.2 and TSR Schematron to v1.0.3
v2.1.3 - 2023-09-21
- Updated EUSR Schematron to v1.1.1 and TSR Schematron to v1.0.2
v2.1.2 - 2023-09-12
- Added class
PeppolReportingHelperwith some generic helper methods
v2.1.1 - 2023-09-10
- Added new submodule
peppol-reporting-backend-inmemorythat uses memory persistence as the backend to store reporting items - Added third party module descriptors
- Fixed the date time offset when storing to MongoDB
v2.1.0 - 2023-09-10
- Added new API package
com.helper.peppol.reporting.api.backendto define a generic backend API - Added new submodule
peppol-reporting-backend-mongodbthat uses MongoDB as the backend to store reporting items - Added new submodule
peppol-reporting-backend-redisthat uses Redis as the backend to store reporting items
v2.0.0 - 2023-07-21
- Changed the Maven Group ID to be
com.helger.peppolinstead ofcom.helger - Introduced the new submodule
peppol-reporting-api - Changed some of the package names introduced in v1.2.0 to reflect the submodule name
- Using Maven Bundle plugin to create OSGI bundles
v1.2.0 - 2023-07-20
- Added data models to easily build End User Statistics Reports v1.1.0 in code
- Added data models to easily build Transaction Statistics Reports v1.0.1 in code
v1.1.0 - 2023-07-02
- Updated to support EUSR 1.1.0
v1.0.0 - 2023-04-26
- Initial Version
- Supports EUSR 1.0.0 and TSR 1.0.1
My personal Coding Styleguide | It is appreciated if you star the GitHub project if you like it.