@stackforge-eu/factur-x 1.4.0 → 1.4.2
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 +22 -0
- package/dist/index.cjs +20 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +20 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.4.2] — 2026-09-25
|
|
11
|
+
|
|
12
|
+
- **Fixed the embedded XML MIME type back to `text/xml`.** 1.2.1 switched the
|
|
13
|
+
`factur-x.xml` filespec `/Subtype` to `application/xml` (#7), but the
|
|
14
|
+
Factur-X specification requires `text/xml`, and the official FNFE-MPE
|
|
15
|
+
validator rejects anything else. PDFs generated with 1.2.1 through 1.4.1
|
|
16
|
+
should be regenerated. A test now pins the value (#19).
|
|
17
|
+
|
|
18
|
+
## [1.4.1] — 2026-09-14
|
|
19
|
+
|
|
20
|
+
- **Added the rounding amount (BT-114).** A new optional `roundingAmount`
|
|
21
|
+
field on `InvoiceTotalsInput` emits `ram:RoundingAmount` between the tax
|
|
22
|
+
total and the grand total, so an invoice can state the amount that takes
|
|
23
|
+
the grand total (BT-112) to the payable figure (BT-115). Emitted for
|
|
24
|
+
EN 16931 and EXTENDED only, as the lower-profile XSDs do not define the
|
|
25
|
+
element. `validateInput` now also checks BR-CO-16
|
|
26
|
+
(`duePayableAmount = grandTotal - prepaidAmount + roundingAmount`), which
|
|
27
|
+
the schematron already enforced. Omitting the field keeps the output
|
|
28
|
+
byte-for-byte identical (#17).
|
|
29
|
+
|
|
10
30
|
## [1.4.0] — 2026-09-03
|
|
11
31
|
|
|
12
32
|
- **Added the item price discount (BT-147).** A new optional `priceDiscount`
|
|
@@ -245,6 +265,8 @@ This element is not expected`. Reordered the three blocks in
|
|
|
245
265
|
- Deno compatibility test suite
|
|
246
266
|
|
|
247
267
|
[Unreleased]: https://github.com/StackForge-EU/factur-x/commits/main
|
|
268
|
+
[1.4.2]: https://github.com/StackForge-EU/factur-x/compare/v1.4.1...v1.4.2
|
|
269
|
+
[1.4.1]: https://github.com/StackForge-EU/factur-x/compare/v1.4.0...v1.4.1
|
|
248
270
|
[1.4.0]: https://github.com/StackForge-EU/factur-x/compare/v1.3.1...v1.4.0
|
|
249
271
|
[1.3.1]: https://github.com/StackForge-EU/factur-x/compare/v1.3.0...v1.3.1
|
|
250
272
|
[1.3.0]: https://github.com/StackForge-EU/factur-x/compare/v1.2.1...v1.3.0
|
package/dist/index.cjs
CHANGED
|
@@ -496,6 +496,8 @@ function buildMonetarySummation(t, profile) {
|
|
|
496
496
|
x += tag("ram:TaxTotalAmount", fmtAmt(t.taxTotal), {
|
|
497
497
|
currencyID: t.currency
|
|
498
498
|
});
|
|
499
|
+
if (atLeast(profile, "EN16931" /* EN16931 */) && t.roundingAmount !== void 0)
|
|
500
|
+
x += tag("ram:RoundingAmount", fmtAmt(t.roundingAmount));
|
|
499
501
|
x += tag("ram:GrandTotalAmount", fmtAmt(t.grandTotal));
|
|
500
502
|
if (atLeast(profile, "BASIC_WL" /* BASIC_WL */) && t.prepaidAmount !== void 0)
|
|
501
503
|
x += tag("ram:TotalPrepaidAmount", fmtAmt(t.prepaidAmount));
|
|
@@ -936,6 +938,22 @@ function validateInput(input, profile, flavor) {
|
|
|
936
938
|
}
|
|
937
939
|
}
|
|
938
940
|
}
|
|
941
|
+
if (req("BASIC_WL" /* BASIC_WL */) && input.totals) {
|
|
942
|
+
const t = input.totals;
|
|
943
|
+
if (t.grandTotal != null && t.duePayableAmount != null) {
|
|
944
|
+
const prepaid = t.prepaidAmount ?? 0;
|
|
945
|
+
const rounding = t.roundingAmount ?? 0;
|
|
946
|
+
const expected = round2(t.grandTotal - prepaid + rounding);
|
|
947
|
+
if (Math.abs(round2(t.duePayableAmount) - expected) > 0.01) {
|
|
948
|
+
addError(
|
|
949
|
+
errors,
|
|
950
|
+
"totals.duePayableAmount",
|
|
951
|
+
`duePayableAmount ${t.duePayableAmount} does not equal grandTotal - prepaidAmount + roundingAmount (${expected}) (BR-CO-16).`,
|
|
952
|
+
"BASIC_WL" /* BASIC_WL */
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
939
957
|
if (req("BASIC_WL" /* BASIC_WL */) && input.totals) {
|
|
940
958
|
const t = input.totals;
|
|
941
959
|
if (t.lineTotal != null && t.taxBasisTotal != null) {
|
|
@@ -1424,7 +1442,8 @@ async function embedFacturX(options) {
|
|
|
1424
1442
|
const xmlBytes = new TextEncoder().encode(xml);
|
|
1425
1443
|
const afRel = resolveAfRelationship(profile, options.afRelationship);
|
|
1426
1444
|
await pdfDoc.attach(xmlBytes, flavorConfig.attachmentFilename, {
|
|
1427
|
-
|
|
1445
|
+
// Factur-X mandates text/xml on the filespec; the FNFE validator rejects application/xml.
|
|
1446
|
+
mimeType: "text/xml",
|
|
1428
1447
|
afRelationship: afRel,
|
|
1429
1448
|
description: "Factur-X Invoice"
|
|
1430
1449
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -859,6 +859,14 @@ interface InvoiceTotalsInput {
|
|
|
859
859
|
* @see EN 16931 BT-113
|
|
860
860
|
*/
|
|
861
861
|
prepaidAmount?: number;
|
|
862
|
+
/**
|
|
863
|
+
* Rounding amount — the amount added to the grand total (BT-112) to round
|
|
864
|
+
* the amount due (BT-115) to a payable figure, e.g. `0.02` to take
|
|
865
|
+
* `29.95` to `29.97`. Emitted as `ram:RoundingAmount` for EN 16931 and
|
|
866
|
+
* EXTENDED only; the BASIC / BASIC WL XSDs do not define the element.
|
|
867
|
+
* @see EN 16931 BT-114, BR-CO-16
|
|
868
|
+
*/
|
|
869
|
+
roundingAmount?: number;
|
|
862
870
|
/**
|
|
863
871
|
* Due payable amount.
|
|
864
872
|
* @see EN 16931 BT-115
|
package/dist/index.d.ts
CHANGED
|
@@ -859,6 +859,14 @@ interface InvoiceTotalsInput {
|
|
|
859
859
|
* @see EN 16931 BT-113
|
|
860
860
|
*/
|
|
861
861
|
prepaidAmount?: number;
|
|
862
|
+
/**
|
|
863
|
+
* Rounding amount — the amount added to the grand total (BT-112) to round
|
|
864
|
+
* the amount due (BT-115) to a payable figure, e.g. `0.02` to take
|
|
865
|
+
* `29.95` to `29.97`. Emitted as `ram:RoundingAmount` for EN 16931 and
|
|
866
|
+
* EXTENDED only; the BASIC / BASIC WL XSDs do not define the element.
|
|
867
|
+
* @see EN 16931 BT-114, BR-CO-16
|
|
868
|
+
*/
|
|
869
|
+
roundingAmount?: number;
|
|
862
870
|
/**
|
|
863
871
|
* Due payable amount.
|
|
864
872
|
* @see EN 16931 BT-115
|
package/dist/index.js
CHANGED
|
@@ -466,6 +466,8 @@ function buildMonetarySummation(t, profile) {
|
|
|
466
466
|
x += tag("ram:TaxTotalAmount", fmtAmt(t.taxTotal), {
|
|
467
467
|
currencyID: t.currency
|
|
468
468
|
});
|
|
469
|
+
if (atLeast(profile, "EN16931" /* EN16931 */) && t.roundingAmount !== void 0)
|
|
470
|
+
x += tag("ram:RoundingAmount", fmtAmt(t.roundingAmount));
|
|
469
471
|
x += tag("ram:GrandTotalAmount", fmtAmt(t.grandTotal));
|
|
470
472
|
if (atLeast(profile, "BASIC_WL" /* BASIC_WL */) && t.prepaidAmount !== void 0)
|
|
471
473
|
x += tag("ram:TotalPrepaidAmount", fmtAmt(t.prepaidAmount));
|
|
@@ -906,6 +908,22 @@ function validateInput(input, profile, flavor) {
|
|
|
906
908
|
}
|
|
907
909
|
}
|
|
908
910
|
}
|
|
911
|
+
if (req("BASIC_WL" /* BASIC_WL */) && input.totals) {
|
|
912
|
+
const t = input.totals;
|
|
913
|
+
if (t.grandTotal != null && t.duePayableAmount != null) {
|
|
914
|
+
const prepaid = t.prepaidAmount ?? 0;
|
|
915
|
+
const rounding = t.roundingAmount ?? 0;
|
|
916
|
+
const expected = round2(t.grandTotal - prepaid + rounding);
|
|
917
|
+
if (Math.abs(round2(t.duePayableAmount) - expected) > 0.01) {
|
|
918
|
+
addError(
|
|
919
|
+
errors,
|
|
920
|
+
"totals.duePayableAmount",
|
|
921
|
+
`duePayableAmount ${t.duePayableAmount} does not equal grandTotal - prepaidAmount + roundingAmount (${expected}) (BR-CO-16).`,
|
|
922
|
+
"BASIC_WL" /* BASIC_WL */
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
909
927
|
if (req("BASIC_WL" /* BASIC_WL */) && input.totals) {
|
|
910
928
|
const t = input.totals;
|
|
911
929
|
if (t.lineTotal != null && t.taxBasisTotal != null) {
|
|
@@ -1394,7 +1412,8 @@ async function embedFacturX(options) {
|
|
|
1394
1412
|
const xmlBytes = new TextEncoder().encode(xml);
|
|
1395
1413
|
const afRel = resolveAfRelationship(profile, options.afRelationship);
|
|
1396
1414
|
await pdfDoc.attach(xmlBytes, flavorConfig.attachmentFilename, {
|
|
1397
|
-
|
|
1415
|
+
// Factur-X mandates text/xml on the filespec; the FNFE validator rejects application/xml.
|
|
1416
|
+
mimeType: "text/xml",
|
|
1398
1417
|
afRelationship: afRel,
|
|
1399
1418
|
description: "Factur-X Invoice"
|
|
1400
1419
|
});
|