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.
Unidirectional converter for EN 16931 invoices from CII to UBL, supporting both editions of the standard:
| Edition | Input | Output | Specification identifier (BT-24) |
|---|---|---|---|
| EN 16931:2017 | CII D16B | UBL 2.1 | urn:cen.eu:en16931:2017 |
| EN 16931:2026 | CII D25A | UBL 2.5 | urn:cen.eu:en16931:2026 |
Since v4.0.0 the UBL versions 2.2, 2.3 and 2.4 are no longer supported, because the two editions of EN 16931 prescribe exactly UBL 2.1 and UBL 2.5.
This is a Java 17+ library that converts a Cross Industry Invoice (CII) into a Universal Business Language (UBL) document following the rules of the European Norm (EN) 16931 that defines a common semantic data model for electronic invoices in Europe.
Special care was given to XRechnung invoices - all the CII examples of them translate to UBL that is valid according to the EN 16931 validation rules.
See https://peppol.helger.com/public/locale-en_US/menuitem-tools-rest-api#cii2ubl for a service implementation using this library.
This library is licensed under the Apache License Version 2.0.
The binary releases are available on Maven Central at https://repo1.maven.org/maven2/com/helger/en16931-cii2ubl/ and below.
This is a pure Java library and not a self-contained conversion tool. The entrance classes are:
- EN 16931:2017, CII D16B to UBL 2.1:
com.helger.en16931.cii2ubl.en2017.CIID16BToUBL21Converter - EN 16931:2026, CII D25A to UBL 2.5:
com.helger.en16931.cii2ubl.en2026.CIID25AToUBL25Converter(since v4.0.0)
If you do not know the edition of a document up front, use com.helger.en16931.cii2ubl.CIIToUBLDispatcher (since v4.0.0).
It determines the edition from BT-24 (Specification identifier) and routes to the matching converter:
final ErrorList aErrorList = new ErrorList ();
final Serializable aUBL = new CIIToUBLDispatcher ().convertCIItoUBL (aFile, aErrorList);Note that the edition cannot be determined from the XML namespaces, because CII D16B, D22B and D25A all use identical namespace URIs, and it cannot be determined from the XML Schema either, because a D16B instance also validates against the D25A XSD.
BT-24 is the only reliable discriminator.
For documents that carry no EN 16931 identifier - for example legacy ZUGFeRD files - set the edition explicitly with setEdition (EEN16931Edition.EN2017).
EEN16931Edition lives in en16931-basics, the artefact that holds the facts about EN 16931 that are shared between the conversion projects; since v4.0.0 it is a dependency of this library.
The main conversion method is called convertCIItoUBL and takes either a File as input or a pre-parsed CrossIndustryInvoiceType object.
Reading is done with com.helger.cii.d16b.CIID16BCrossIndustryInvoiceTypeMarshaller respectively com.helger.cii.d25a.CIID25ACrossIndustryInvoiceTypeMarshaller from ph-cii.
Additionally an ErrorList object must be provided as a container for all the errors that occur.
The conversion is deemed successful, if a non-null object is returned and if the error list contains no error (errorList.containsNoError ()).
The field mapping of both editions is documented in the docs folder:
docs/en16931-2017-syntax.md- UBL 2.1 and CII D16Bdocs/en16931-2026-syntax.md- UBL 2.5 and CII D25A
Replace x.y.z with the effective version you want to use:
<dependency>
<groupId>com.helger</groupId>
<artifactId>en16931-cii2ubl</artifactId>
<version>x.y.z</version>
</dependency>Call it via java -jar en16931-cii2ubl-cli-full.jar followed by the options and parameters.
Without --en-version the EN 16931 edition is determined from BT-24 of each source file.
[INFO] CII to UBL Converter v4.0.0-SNAPSHOT (build 2026-09-04T22:40:13Z)
Missing required parameter: 'source files'
Usage: CIItoUBLConverter [-hV] [--disable-wildcard-expansion]
[--swap-price-sign] [--swap-quantity-sign] [--verbose]
[--en-version edition] [--mode mode] [--output-suffix
filename part] [-t directory] [--ubl version]
[--ubl-cardaccountnetworkid ID] [--ubl-customizationid
ID] [--ubl-defaultorderrefid ID] [--ubl-profileid ID]
[--ubl-vatscheme vat scheme] source files...
CII to UBL Converter for EN 16931 invoices
source files... One or more CII file(s)
--disable-wildcard-expansion
Disable wildcard expansion of filenames
--en-version edition The EN 16931 edition to use: '2017' (CII D16B to
UBL 2.1) or '2026' (CII D25A to UBL 2.5). If
omitted, the edition is determined from BT-24 of
each source file.
-h, --help Show this help message and exit.
--mode mode Allowed values: AUTOMATIC, INVOICE, CREDIT_NOTE
(default: 'AUTOMATIC')
--output-suffix filename part
The suffix added to the output filename (default:
'-ubl')
--swap-price-sign Swap price sign if needed for credit notes
(default: 'true')
--swap-quantity-sign Swap quantity sign if needed for credit notes
(default: 'true')
-t, --target directory The target directory for result output (default:
'.')
--ubl version Deprecated alias for --en-version: '2.1' selects
EN 16931:2017 and '2.5' selects EN 16931:2026.
Prefer --en-version.
--ubl-cardaccountnetworkid ID
The UBL CardAccount network ID to be used
(default: 'mapped-from-cii')
--ubl-customizationid ID
The UBL customization ID to be used
--ubl-defaultorderrefid ID
The UBL default order reference ID to be used
(default: '')
--ubl-profileid ID The UBL profile ID to be used
--ubl-vatscheme vat scheme
The UBL VAT scheme to be used (default: 'VAT')
-V, --version Print version information and exit.
--verbose Enable debug logging (default: 'false')
- The migration of CII
NetPriceProductTradePrice/BasisQuantityto UBLPrice/BaseQuantityis not consistent for me- See example files 2, 8 and 9
- The UBL example files use a BaseQuanity of 1 in all cases
v4.0.1 - 2026-09-07
- Fixed a CII
ram:GlobalIDwith@schemeID="SEPA"being written as a party identifier BT-29/BT-46/BT-60/BT-71. In UBL that scheme identifier is reserved for BT-90, so the result carried two competing bank assigned creditor identifiers, and the scheme identifier is not an ISO 6523 ICD code either, which BR-CL-10 requires. Affects both editions - Now using
EEN16931TaxSchemeCode.LOCand the new discriminator constants of en16931-basics 1.0.1 instead of local copies.NATIONAL_TAX_SCHEME,NON_VAT_TAX_CODE_LIST_IDandSUPPORTING_DOCUMENT_TYPE_CODE_LIST_IDofCIID25AToUBL25Converterare deprecated and now delegate there - BT-32-2 is derived from
EN16931CodeLists.mapTaxSchemeCodeCIIToUBLrather than a hard coded"FC"to"LOC"special case - Fixed a schema invalid
cac:OrderLineReferencefor an invoice line that has a sales order reference (BT-200/BT-201) but no purchase order line reference (BT-132) - the mandatorycbc:LineIDwas left out entirely - The placeholder for a mandatory
cbc:LineIDwithout a business term value is now theNoneprescribed by the UBL binding instead of1, which is what en16931-ubl2cii reads back. Affects BT-132, BT-190, BT-192 and BT-199
v4.0.0 - 2026-09-05
- Added the EN 16931:2026 syntax binding:
com.helger.en16931.cii2ubl.en2026.CIID25AToUBL25Converterconverts CII D25A to UBL 2.5, covering all 284 rows of the mapping table including the 70 business terms and groups that are new in 2026 (BG-33 to BG-39, BT-166 to BT-220) - Removed the support for creating UBL 2.2, 2.3 and 2.4 - the two editions of EN 16931 prescribe exactly UBL 2.1 and UBL 2.5
- Moved the EN 16931:2017 conversion to the new package
com.helger.en16931.cii2ubl.en2017and renamedCIIToUBL21ConvertertoCIID16BToUBL21Converter. The UBL 2.1 output is unchanged - Split
AbstractCIIToUBLConverterinto the edition independentAbstractCIIToUBLConverterBaseand the CII release specificAbstractCIIToUBL2017ConverterandAbstractCIIToUBL2026Converter - Added
CIIToUBLDispatcherto determine the EN 16931 edition of a CII document from BT-24 and to route to the matching converter. The edition can neither be determined from the XML namespaces, which are identical across CII releases, nor from the XML Schema, because a D16B instance also validates against the D25A XSD - Added the dependency to
en16931-basics1.0.0 and tookEEN16931Edition, the code list knowledge and the UNTDID 2379 date formats from there instead of holding own copies.com.helger.en16931.cii2ubl.EEN16931Editionwas therefore replaced bycom.helger.en16931.basics.EEN16931Edition, and the BT-3, BT-8, BT-17/BT-18 and BT-81 classification now comes fromcom.helger.en16931.basics.codelist.EN16931CodeLists. TheAbstractCIIToUBLConverterBaseconstantsDEFAULT_DATE_TIME_FORMATandDATE_TIME_FORMAT_WITH_TIMEwere replaced bycom.helger.en16931.basics.EEN16931DateFormatCode. The converted output is unchanged - The CLI determines the edition per file from BT-24 by default; the new option
--en-version 2017|2026forces one instead.--ublis deprecated and now only accepts2.1and2.5 - Fixed a long standing CLI bug: without
--ubl-customizationidand--ubl-profileidthe conversion aborted with aNullPointerException - The BT-3 (Invoice type code) subset of UNTDID 1001 now follows the EN 16931 code list registry (the CEN/TC 434 section of the European Commission registry), with the values taken from "EN16931 code lists values v17b - used from 2026-05-15". The list is versioned by date and not by EN 16931 edition, so both editions share it
- Fixed the BT-3 classification of
81,502and503: all three were treated as Invoice codes, although the code list has them as Credit Note codes.502and503were added to the list in v15 (used from 2025-05-15) - Added the dependencies to
ph-cii-d25aandph-ubl25 - Added
docs/en16931-2026-syntax.mdwith the three-way field mapping (UBL 2.5 invoice / UBL 2.5 credit note / CII D25A) of EN 16931:2026 - Added
docs/plan-4.0.0.mdwith the implementation plan for v4.0.0
v3.1.7 - 2026-04-22
- Fixed BT-150 (Item price base quantity unit of measure): now falls back to the net price unitCode when no gross price is present, instead of losing it
- Fixed BT-61/BT-61-1 (Payee legal registration identifier):
_convertPartyLegalEntityis now called for the Payee party, mappingSpecifiedLegalOrganization/IDtoPartyLegalEntity/CompanyID - Fixed UBL-CR-275 compliance:
RegistrationNamefallback is no longer set on the PayeePartyLegalEntity, as the EN 16931 Schematron forbids it - Fixed BT-11 (Project reference) for UBL 2.1 CreditNote: now mapped to
AdditionalDocumentReferencesince UBL 2.1 CreditNote lacksProjectReference
v3.1.6 - 2026-04-22
- Added CLI options
--swap-quantity-signand--swap-price-signto expose the full converter API - Improved BT-90 mapping so that it is correctly set to the
PayeePartyif present - Improved BT-11 mapping for UBL CreditNote
v3.1.5 - 2026-04-13
- Fixed the "main class" attribute for the manifest
v3.1.4 - 2026-04-13
- No more OSGI bundle packaging
- Avoid creating an empty UBL Delivery Party Name element. See issue #47
- Fixed the Delivery element to also be created when only a delivery date is present but no ShipToParty
- Unified the CreditNote line-item price handling (BT-147, BT-148, BT-149, BT-150) to match the Invoice logic
v3.1.3 - 2026-02-02
- Fixed the BT-9 mapping of CreditNotes. See issue #45 - thx @AWickSimova
- The list of allowed Invoice Type Codes was extended: Added 71, 81, 102, 218, 219, 331, 382, 471, 472, 473, 500, 501, 502, 503, 553, 817 and 870
v3.1.2 - 2026-01-05
- Fixed a bug in the BG-17 conversion towards UBL 2.4. See issue #44 - thx @matmen
v3.1.1 - 2025-12-29
- Fixed invalid mapping to
BillingReference/CreditNoteDocumentReference. See issue #43 - thx @ycheuv-lgtm
v3.1.0 - 2025-11-16
- Updated to ph-commons 12.1.0
- Using JSpecify annotations
v3.0.1 - 2025-09-19
- Added the mapping of BT-86 for DirectDebit case
v3.0.0 - 2025-08-27
- Requires Java 17 as the minimum version
- Updated to ph-commons 12.0.0
v2.3.1 - 2025-06-26
- Avoid
NullPointerExceptionif a CII line item has no quantity
v2.3.0 - 2025-03-25
- Removed the default values for BT-24 (
CustomizationID) and BT-23 (ProfileID) - Emitting UBL 2.4 documents correctly
- Taking the values of BT-23 and BT-24 from the source CII fields. See issue #37 - thx @shenazz
- Handling BG-17 only if
PayeePartyCreditorFinancialAccountis present. See issue #38 - thx @msccip
v2.2.6 - 2024-12-23
- Supporting all values of the UNTDID 4461 code list. See issue #34 - thx @shenazz
v2.2.5 - 2024-10-22
- Fixed the automatic module name. See issue #32 - thx @toherrmann
v2.2.4 - 2024-06-17
- Fixed a regression from v2.2.3. See issue #31 - thx @msccip
v2.2.3 - 2024-06-12
- Added additional mapping of BT-41. See issue #28 - thx @bdewein
- Fixed the mapping of the Due Date Type Code for BT-8. See issue #29 - thx @bdewein
- Fixed the mapping of BT-27 and BT-28. See issue #26 - thx @bdewein
- Fixed the item price mapping around BT-146, BT-147, BT-148, BT-149 and BT-150. See issue #27 - thx @bdewein
v2.2.2 - 2024-04-12
- Added support for providing the default UBL order reference ID, in case the empty String is not good enough. See issue #23 - thx @lkumai
v2.2.1 - 2024-03-29
- Updated to ph-commons 11.1.5
- Ensured Java 21 compatibility
v2.2.0 - 2024-03-08
- Updated to ph-ubl 9.0.0
- Tested output against EN 16931 rules v1.3.10 and v1.3.11 - no changes necessary
- Updated to create UBL 2.4-CS01
- Added support for BT-8 mapping. See issue #22
v2.1.0 - 2023-04-28
- Updated to ph-ubl 8.0.1
- Added support for converting to UBL 2.4
v2.0.3 - 2023-04-20
- Improved mapping of references to external documents in additional document references. See issue #20 - thanks @msccip
v2.0.2 - 2023-03-30
- Changed the default mode of the CLI version from
INVOICEtoAUTOMATIC. See issue #19 - thanks @msccip
v2.0.1 - 2023-03-15
- Added manual wildcard expansion of filenames
- Added new CLI option
--disable-wildcard-expansionto disable the wildcard expansion and stick to the old resolution logic
v2.0.0 - 2023-02-20
- Using Java 11 as the baseline
- Updated to ph-commons 11
- Updated to JAXB 4.0
- Added the new CLI parameter
--verbosefor a few more details - Improved logging
- Successfully tested the CLI parameter with wildcard parameters (because the Java Windows Runtime performs automatic wildcard expansion)
v1.4.10 - 2022-12-16
- Fixed the conversion of the
TypeCodeelement inAdditionalReferencedDocument. See issue #18 - thanks @L3Mars
v1.4.9 - 2022-11-15
- Fixed an unnecessary division by 100 for creating
MultiplierFactorNumeric. See issue #17 - thanks @L3Mars - Tested output against EN 16931 rules v1.3.9 - no changes necessary
v1.4.8 - 2022-09-28
- Added new option
--output-suffixto customize the output file suffix, that is currently hard coded to-ubl - Fixed a problem with the mapping of BT-147, BT-148, BT-149 and BT-150. See issue #15. Thanks to @cambid for pointing that out
v1.4.7 - 2022-02-15
- Further improved
null/empty handling to avoid creation of empty elements
v1.4.6 - 2022-02-12
- Allowing the additional Payment Means Type Codes
1,42and68. See issue #13 - For decimal values, trailing zeroes are no longer emitted. See issue #13
- Improved not creating empty UBL elements. See issue #13
- The
SubjectCodeofIncludedNoteelements is copied over. See issue #13 - Improved the Party ID handling and allowing for multiple seller IDs. See issue #13
v1.4.5 - 2021-12-20
- Tested output against EN 16931 rules v1.3.7 - no changes necessary
- Fixed creating invalid UBL if
SellerAssignedIDis empty. See issue #12 - thanks @DerHamm
v1.4.4 - 2021-10-14
- Improved sign swapping of Quantity and Price to avoid negative prices (BT-146)
v1.4.3 - 2021-10-07
- Tested output against EN 16931 rules v1.3.6 - no changes necessary
- Changed determination if Invoice or CreditNote primarily to
rsm:ExchangedDocument/ram:TypeCodeinstead of the payable amount
v1.4.2 - 2021-06-10
- Changed the default customization ID to
urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0 - If the UBL
LineExtensionAmountis negative, but the lineQuantityis positive, theQuantitywill be changed to negative. Customizable viasetSwapQuantitySignIfNeeded(boolean)
v1.4.1 - 2021-05-02
- Updated to ph-commons 10.1
- Tested output against EN 16931 rules v1.3.5 - no changes necessary
v1.4.0 - 2021-03-22
- Updated to ph-commons 10
v1.3.0 - 2021-01-05
- Added support for different CII time formats (2, 3, 4, 101, 102, 103 and 105) where
102is the default - The error handling for the commandline client was improved (checking errors before writing UBL) (see issue #9)
- Added the possibility to create UBL 2.3
- Made the API more open to directly convert to Invoice or CreditNote
- Improved the handling of payment means to be more EN compliant. See issue #7.
- Added a possibility to retrieve the internal converter version number
v1.2.5 - 2020-11-30
- Added Jakarta Activation dependency to the standalone CLI version. See issue #6.
v1.2.4 - 2020-10-20
- Tested with EN 16031 validation rules 1.3.3
- Not emitting the
LegalMonetaryTotal/PayableRoundingAmountif the value is0as a work around for ConnectingEurope/eInvoicing-EN16931#242
v1.2.3 - 2020-09-17
- Updated to Jakarta JAXB 2.3.3
v1.2.2 - 2020-08-30
- Updated to ph-commons 9.4.7
- Updated to ph-cii 2.3.0
- Updated to ph-ubl 6.4.0
v1.2.1 - 2020-05-26
- Updated to new Maven groupIds
v1.2.0 - 2020-03-09
- Verified against EN 16931 validation artefacts 1.3.0 - no changes in the output
- Added commandline interface (CLI). See PR #3. Thanks to @rkottmann
- Fixed creating invalid UBL if
SellerOrderReferencedDocumentis present butBuyerOrderReferencedDocumentis not set (see issue #5) - Made default VAT scheme, UBL
CustomizationID, UBLProfileIDand thePaymentMeans/CardAccount/NetworkIDcustomizable. See issue #1 and issue #2. - Fixed embedded attachment mapping. See issue #4.
v1.1.5 - 2019-09-13
- Added possibility to enforce invoice creation
- Verified against EN 16931 validation artefacts 1.3.0
v1.1.4 - 2019-07-15
- Updated to EN 16931 validation artefacts 1.2.3
v1.1.3 - 2019-05-15
- Updated to EN 16931 validation artefacts 1.2.1
v1.1.2 - 2019-04-26
- Updated to EN 16931 validation artefacts 1.2.0
v1.1.1 - 2019-02-27
- Improved delivery date handling
- Improved price base quantity handling
v1.1.0 - 2019-02-26
- Added support to create UBL 2.1 Invoice and CreditNote
v1.0.0 - 2019-02-26
- Initial release creating UBL 2.2 Invoice and CreditNote
My personal Coding Styleguide | It is appreciated if you star the GitHub project if you like it.