@stackforge-eu/factur-x 1.4.0 → 1.4.1

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 CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.4.1] — 2026-09-14
11
+
12
+ - **Added the rounding amount (BT-114).** A new optional `roundingAmount`
13
+ field on `InvoiceTotalsInput` emits `ram:RoundingAmount` between the tax
14
+ total and the grand total, so an invoice can state the amount that takes
15
+ the grand total (BT-112) to the payable figure (BT-115). Emitted for
16
+ EN 16931 and EXTENDED only, as the lower-profile XSDs do not define the
17
+ element. `validateInput` now also checks BR-CO-16
18
+ (`duePayableAmount = grandTotal - prepaidAmount + roundingAmount`), which
19
+ the schematron already enforced. Omitting the field keeps the output
20
+ byte-for-byte identical (#17).
21
+
10
22
  ## [1.4.0] — 2026-09-03
11
23
 
12
24
  - **Added the item price discount (BT-147).** A new optional `priceDiscount`
@@ -245,6 +257,7 @@ This element is not expected`. Reordered the three blocks in
245
257
  - Deno compatibility test suite
246
258
 
247
259
  [Unreleased]: https://github.com/StackForge-EU/factur-x/commits/main
260
+ [1.4.1]: https://github.com/StackForge-EU/factur-x/compare/v1.4.0...v1.4.1
248
261
  [1.4.0]: https://github.com/StackForge-EU/factur-x/compare/v1.3.1...v1.4.0
249
262
  [1.3.1]: https://github.com/StackForge-EU/factur-x/compare/v1.3.0...v1.3.1
250
263
  [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) {
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackforge-eu/factur-x",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "type": "module",
5
5
  "description": "Embed Factur-X, ZUGFeRD, and XRechnung structured invoice XML into PDF/A-3 documents",
6
6
  "keywords": [