@stackforge-eu/factur-x 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/dist/index.cjs +17 -6
- package/dist/index.js +17 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.7] — 2026-05-27
|
|
11
|
+
|
|
12
|
+
- **`fx:ConformanceLevel` in the XMP metadata used the raw `Profile` enum
|
|
13
|
+
identifier instead of the Factur-X / ZUGFeRD spec label.** The XMP packet
|
|
14
|
+
emitted `EN16931` / `BASIC_WL` where the spec requires `EN 16931` /
|
|
15
|
+
`BASIC WL`, so strict ZUGFeRD / Factur-X validators (Mustangproject, the
|
|
16
|
+
FNFE-MPE check tool, veraPDF's Factur-X profile) rejected every PDF this
|
|
17
|
+
library produced even though the embedded CII XML was correct. Added a
|
|
18
|
+
`Profile` → XMP-label mapping and routed `buildXmpMetadata` through it in
|
|
19
|
+
`src/core/embed.ts` (thanks @sco-indy, #2).
|
|
20
|
+
- **`flavor: Flavor.XRECHNUNG` still advertised `EN 16931` in the XMP packet.**
|
|
21
|
+
Hybrid XRechnung PDFs carry the EN16931 profile internally but per ZUGFeRD
|
|
22
|
+
2.x §2.4.4 must declare `XRECHNUNG` as the conformance level so the KoSIT /
|
|
23
|
+
ZUGFeRD validators route them through the XRechnung schematron set rather
|
|
24
|
+
than the vanilla Factur-X one. `resolveXmpConformanceLevel` in
|
|
25
|
+
`src/core/embed.ts` now special-cases the XRechnung flavor; the existing
|
|
26
|
+
`toXRechnung` → XML flow is unaffected. Regression test added in
|
|
27
|
+
`tests/embed.test.ts` covering all five profiles and the XRechnung flavor.
|
|
28
|
+
|
|
29
|
+
## [1.0.6] — 2026-05-23
|
|
30
|
+
|
|
31
|
+
- **`SpecifiedTradeSettlementPaymentMeans` emitted its child elements in the
|
|
32
|
+
wrong order.** The EN16931 / CII XSD `TradeSettlementPaymentMeansType`
|
|
33
|
+
sequence requires `PayerPartyDebtorFinancialAccount` before
|
|
34
|
+
`PayeePartyCreditorFinancialAccount` and
|
|
35
|
+
`PayeeSpecifiedCreditorFinancialInstitution`, but the builder appended the
|
|
36
|
+
debtor account last. Any invoice combining a SEPA direct debit
|
|
37
|
+
(`payment.meansCode = "59"`) with `payment.debtorIban` plus a payee
|
|
38
|
+
IBAN/BIC failed XSD validation with `Element 'PayerPartyDebtorFinancialAccount':
|
|
39
|
+
This element is not expected`. Reordered the three blocks in
|
|
40
|
+
`src/core/xml-builder.ts`; regression test added in
|
|
41
|
+
`tests/xml-builder.test.ts`.
|
|
42
|
+
|
|
10
43
|
## [1.0.5] — 2026-05-12
|
|
11
44
|
|
|
12
45
|
- **`ChargeIndicator`'s inner `Indicator` element was emitted in the wrong XML
|
|
@@ -80,6 +113,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
80
113
|
- Deno compatibility test suite
|
|
81
114
|
|
|
82
115
|
[Unreleased]: https://github.com/StackForge-EU/factur-x/commits/main
|
|
116
|
+
[1.0.7]: https://github.com/StackForge-EU/factur-x/compare/v1.0.6...v1.0.7
|
|
117
|
+
[1.0.6]: https://github.com/StackForge-EU/factur-x/compare/v1.0.5...v1.0.6
|
|
83
118
|
[1.0.5]: https://github.com/StackForge-EU/factur-x/compare/v1.0.4...v1.0.5
|
|
84
119
|
[1.0.4]: https://github.com/StackForge-EU/factur-x/compare/v1.0.3...v1.0.4
|
|
85
120
|
[1.0.3]: https://github.com/StackForge-EU/factur-x/compare/v1.0.2...v1.0.3
|
package/dist/index.cjs
CHANGED
|
@@ -591,6 +591,11 @@ function buildXml(input, profile, flavor) {
|
|
|
591
591
|
sett += tag("ram:PayeeTradeParty", buildParty(input.payee, profile));
|
|
592
592
|
if (payment?.meansCode && atLeast(profile, "BASIC_WL" /* BASIC_WL */)) {
|
|
593
593
|
let pm = tag("ram:TypeCode", escapeXml(payment.meansCode));
|
|
594
|
+
if (payment.debtorIban)
|
|
595
|
+
pm += tag(
|
|
596
|
+
"ram:PayerPartyDebtorFinancialAccount",
|
|
597
|
+
tag("ram:IBANID", escapeXml(payment.debtorIban))
|
|
598
|
+
);
|
|
594
599
|
if (payment.iban || payment.accountId || payment.accountName) {
|
|
595
600
|
let acct = "";
|
|
596
601
|
if (payment.iban) acct += tag("ram:IBANID", escapeXml(payment.iban));
|
|
@@ -604,11 +609,6 @@ function buildXml(input, profile, flavor) {
|
|
|
604
609
|
"ram:PayeeSpecifiedCreditorFinancialInstitution",
|
|
605
610
|
tag("ram:BICID", escapeXml(payment.bic))
|
|
606
611
|
);
|
|
607
|
-
if (payment.debtorIban)
|
|
608
|
-
pm += tag(
|
|
609
|
-
"ram:PayerPartyDebtorFinancialAccount",
|
|
610
|
-
tag("ram:IBANID", escapeXml(payment.debtorIban))
|
|
611
|
-
);
|
|
612
612
|
sett += tag("ram:SpecifiedTradeSettlementPaymentMeans", pm);
|
|
613
613
|
}
|
|
614
614
|
if (input.vatBreakdown && atLeast(profile, "BASIC_WL" /* BASIC_WL */))
|
|
@@ -1111,6 +1111,17 @@ var AF_RELATIONSHIP_MAP = {
|
|
|
1111
1111
|
Data: pdfLib.AFRelationship.Data,
|
|
1112
1112
|
Source: pdfLib.AFRelationship.Source
|
|
1113
1113
|
};
|
|
1114
|
+
var XMP_CONFORMANCE_LEVEL = {
|
|
1115
|
+
["MINIMUM" /* MINIMUM */]: "MINIMUM",
|
|
1116
|
+
["BASIC_WL" /* BASIC_WL */]: "BASIC WL",
|
|
1117
|
+
["BASIC" /* BASIC */]: "BASIC",
|
|
1118
|
+
["EN16931" /* EN16931 */]: "EN 16931",
|
|
1119
|
+
["EXTENDED" /* EXTENDED */]: "EXTENDED"
|
|
1120
|
+
};
|
|
1121
|
+
function resolveXmpConformanceLevel(profile, flavor) {
|
|
1122
|
+
if (flavor === "xrechnung" /* XRECHNUNG */) return "XRECHNUNG";
|
|
1123
|
+
return XMP_CONFORMANCE_LEVEL[profile];
|
|
1124
|
+
}
|
|
1114
1125
|
function ensureOutputIntent(pdfDoc, iccBytes, identifier) {
|
|
1115
1126
|
const existing = pdfDoc.catalog.lookup(pdfLib.PDFName.of("OutputIntents"));
|
|
1116
1127
|
if (existing instanceof pdfLib.PDFArray && existing.size() > 0) return;
|
|
@@ -1375,7 +1386,7 @@ async function embedFacturX(options) {
|
|
|
1375
1386
|
createDate: isoDate,
|
|
1376
1387
|
modifyDate: isoDate,
|
|
1377
1388
|
documentFileName: flavorConfig.attachmentFilename,
|
|
1378
|
-
conformanceLevel: profile
|
|
1389
|
+
conformanceLevel: resolveXmpConformanceLevel(profile, flavor)
|
|
1379
1390
|
});
|
|
1380
1391
|
const xmpBytes = new TextEncoder().encode(xmpXml);
|
|
1381
1392
|
const metadataStream = pdfDoc.context.stream(xmpBytes, {
|
package/dist/index.js
CHANGED
|
@@ -561,6 +561,11 @@ function buildXml(input, profile, flavor) {
|
|
|
561
561
|
sett += tag("ram:PayeeTradeParty", buildParty(input.payee, profile));
|
|
562
562
|
if (payment?.meansCode && atLeast(profile, "BASIC_WL" /* BASIC_WL */)) {
|
|
563
563
|
let pm = tag("ram:TypeCode", escapeXml(payment.meansCode));
|
|
564
|
+
if (payment.debtorIban)
|
|
565
|
+
pm += tag(
|
|
566
|
+
"ram:PayerPartyDebtorFinancialAccount",
|
|
567
|
+
tag("ram:IBANID", escapeXml(payment.debtorIban))
|
|
568
|
+
);
|
|
564
569
|
if (payment.iban || payment.accountId || payment.accountName) {
|
|
565
570
|
let acct = "";
|
|
566
571
|
if (payment.iban) acct += tag("ram:IBANID", escapeXml(payment.iban));
|
|
@@ -574,11 +579,6 @@ function buildXml(input, profile, flavor) {
|
|
|
574
579
|
"ram:PayeeSpecifiedCreditorFinancialInstitution",
|
|
575
580
|
tag("ram:BICID", escapeXml(payment.bic))
|
|
576
581
|
);
|
|
577
|
-
if (payment.debtorIban)
|
|
578
|
-
pm += tag(
|
|
579
|
-
"ram:PayerPartyDebtorFinancialAccount",
|
|
580
|
-
tag("ram:IBANID", escapeXml(payment.debtorIban))
|
|
581
|
-
);
|
|
582
582
|
sett += tag("ram:SpecifiedTradeSettlementPaymentMeans", pm);
|
|
583
583
|
}
|
|
584
584
|
if (input.vatBreakdown && atLeast(profile, "BASIC_WL" /* BASIC_WL */))
|
|
@@ -1081,6 +1081,17 @@ var AF_RELATIONSHIP_MAP = {
|
|
|
1081
1081
|
Data: AFRelationship.Data,
|
|
1082
1082
|
Source: AFRelationship.Source
|
|
1083
1083
|
};
|
|
1084
|
+
var XMP_CONFORMANCE_LEVEL = {
|
|
1085
|
+
["MINIMUM" /* MINIMUM */]: "MINIMUM",
|
|
1086
|
+
["BASIC_WL" /* BASIC_WL */]: "BASIC WL",
|
|
1087
|
+
["BASIC" /* BASIC */]: "BASIC",
|
|
1088
|
+
["EN16931" /* EN16931 */]: "EN 16931",
|
|
1089
|
+
["EXTENDED" /* EXTENDED */]: "EXTENDED"
|
|
1090
|
+
};
|
|
1091
|
+
function resolveXmpConformanceLevel(profile, flavor) {
|
|
1092
|
+
if (flavor === "xrechnung" /* XRECHNUNG */) return "XRECHNUNG";
|
|
1093
|
+
return XMP_CONFORMANCE_LEVEL[profile];
|
|
1094
|
+
}
|
|
1084
1095
|
function ensureOutputIntent(pdfDoc, iccBytes, identifier) {
|
|
1085
1096
|
const existing = pdfDoc.catalog.lookup(PDFName.of("OutputIntents"));
|
|
1086
1097
|
if (existing instanceof PDFArray && existing.size() > 0) return;
|
|
@@ -1345,7 +1356,7 @@ async function embedFacturX(options) {
|
|
|
1345
1356
|
createDate: isoDate,
|
|
1346
1357
|
modifyDate: isoDate,
|
|
1347
1358
|
documentFileName: flavorConfig.attachmentFilename,
|
|
1348
|
-
conformanceLevel: profile
|
|
1359
|
+
conformanceLevel: resolveXmpConformanceLevel(profile, flavor)
|
|
1349
1360
|
});
|
|
1350
1361
|
const xmpBytes = new TextEncoder().encode(xmpXml);
|
|
1351
1362
|
const metadataStream = pdfDoc.context.stream(xmpBytes, {
|