@fin.cx/einvoice 8.1.0 → 8.2.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.
Files changed (75) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/einvoice.d.ts +14 -1
  3. package/dist_ts/einvoice.js +17 -2
  4. package/dist_ts/formats/base/base.decoder.js +4 -3
  5. package/dist_ts/formats/base/base.encoder.d.ts +1 -1
  6. package/dist_ts/formats/cii/cii.decoder.d.ts +27 -1
  7. package/dist_ts/formats/cii/cii.decoder.js +61 -1
  8. package/dist_ts/formats/cii/cii.encoder.d.ts +14 -3
  9. package/dist_ts/formats/cii/cii.encoder.js +58 -6
  10. package/dist_ts/formats/cii/cii.types.d.ts +1 -0
  11. package/dist_ts/formats/cii/cii.types.js +3 -2
  12. package/dist_ts/formats/cii/facturx/facturx.decoder.d.ts +1 -1
  13. package/dist_ts/formats/cii/facturx/facturx.decoder.js +11 -8
  14. package/dist_ts/formats/cii/facturx/facturx.encoder.d.ts +2 -2
  15. package/dist_ts/formats/cii/facturx/facturx.encoder.js +18 -15
  16. package/dist_ts/formats/cii/zugferd/zugferd.decoder.d.ts +1 -1
  17. package/dist_ts/formats/cii/zugferd/zugferd.decoder.js +12 -10
  18. package/dist_ts/formats/cii/zugferd/zugferd.encoder.d.ts +1 -1
  19. package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +26 -19
  20. package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.d.ts +19 -1
  21. package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.js +51 -10
  22. package/dist_ts/formats/ubl/generic/ubl.encoder.d.ts +23 -1
  23. package/dist_ts/formats/ubl/generic/ubl.encoder.js +69 -14
  24. package/dist_ts/formats/ubl/ubl.decoder.d.ts +1 -1
  25. package/dist_ts/formats/ubl/ubl.encoder.d.ts +16 -3
  26. package/dist_ts/formats/ubl/ubl.encoder.js +75 -13
  27. package/dist_ts/formats/ubl/ubl.types.d.ts +6 -0
  28. package/dist_ts/formats/ubl/ubl.types.js +9 -3
  29. package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.d.ts +6 -1
  30. package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +46 -14
  31. package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.d.ts +1 -1
  32. package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.js +7 -12
  33. package/dist_ts/formats/utils/date.value.d.ts +28 -0
  34. package/dist_ts/formats/utils/date.value.js +50 -0
  35. package/dist_ts/formats/utils/document.typecode.d.ts +3 -3
  36. package/dist_ts/formats/utils/document.typecode.js +8 -4
  37. package/dist_ts/formats/utils/format.detector.js +6 -4
  38. package/dist_ts/formats/utils/payment.terms.d.ts +18 -0
  39. package/dist_ts/formats/utils/payment.terms.js +32 -0
  40. package/dist_ts/formats/utils/preceding.invoice.d.ts +26 -0
  41. package/dist_ts/formats/utils/preceding.invoice.js +52 -0
  42. package/dist_ts/formats/validation/integrated.validator.js +6 -3
  43. package/dist_ts/formats/validation/xrechnung.validator.js +6 -4
  44. package/dist_ts/index.d.ts +1 -1
  45. package/dist_ts/index.js +1 -1
  46. package/dist_ts/interfaces/common.d.ts +6 -2
  47. package/package.json +3 -3
  48. package/readme.md +77 -10
  49. package/ts/00_commitinfo_data.ts +1 -1
  50. package/ts/einvoice.ts +18 -1
  51. package/ts/formats/base/base.decoder.ts +3 -2
  52. package/ts/formats/base/base.encoder.ts +1 -1
  53. package/ts/formats/cii/cii.decoder.ts +74 -1
  54. package/ts/formats/cii/cii.encoder.ts +62 -6
  55. package/ts/formats/cii/cii.types.ts +2 -1
  56. package/ts/formats/cii/facturx/facturx.decoder.ts +11 -7
  57. package/ts/formats/cii/facturx/facturx.encoder.ts +18 -14
  58. package/ts/formats/cii/zugferd/zugferd.decoder.ts +12 -9
  59. package/ts/formats/cii/zugferd/zugferd.encoder.ts +26 -18
  60. package/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +58 -9
  61. package/ts/formats/ubl/generic/ubl.encoder.ts +76 -13
  62. package/ts/formats/ubl/ubl.decoder.ts +1 -1
  63. package/ts/formats/ubl/ubl.encoder.ts +77 -13
  64. package/ts/formats/ubl/ubl.types.ts +8 -2
  65. package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +56 -13
  66. package/ts/formats/ubl/xrechnung/xrechnung.encoder.ts +7 -12
  67. package/ts/formats/utils/date.value.ts +52 -0
  68. package/ts/formats/utils/document.typecode.ts +7 -3
  69. package/ts/formats/utils/format.detector.ts +5 -5
  70. package/ts/formats/utils/payment.terms.ts +37 -0
  71. package/ts/formats/utils/preceding.invoice.ts +74 -0
  72. package/ts/formats/validation/integrated.validator.ts +5 -2
  73. package/ts/formats/validation/xrechnung.validator.ts +5 -3
  74. package/ts/index.ts +3 -0
  75. package/ts/interfaces/common.ts +6 -1
@@ -7,6 +7,7 @@ import { EInvoiceFormatError } from '../../errors.js';
7
7
  */
8
8
  const DOCUMENT_TYPE_CODES: Record<TAccountingDocType, string> = {
9
9
  'invoice': '380',
10
+ 'corrected-invoice': '384',
10
11
  'creditnote': '381',
11
12
  'debitnote': '383',
12
13
  'self-billed-invoice': '389',
@@ -42,9 +43,9 @@ export const getDocumentTypeCode = (accountingDocType: TAccountingDocType): stri
42
43
  };
43
44
 
44
45
  /**
45
- * The accounting document type of a decoded type code. 383 and 389 have a
46
- * type of their own; the remaining credit note related codes are credit
47
- * notes; everything else is an invoice, which covers 380, 384 and the other
46
+ * The accounting document type of a decoded type code. 383, 384 and 389 have
47
+ * a type of their own; the remaining credit note related codes are credit
48
+ * notes; everything else is an invoice, which covers 380 and the other
48
49
  * invoice related codes (326, 386, 875, ...) the envelope has no name for.
49
50
  * A missing or unlisted code stays an invoice as well: the document is an
50
51
  * invoice document, and reporting the code is the validators' job (BR-04,
@@ -55,6 +56,9 @@ export const getAccountingDocType = (typeCode: string): TAccountingDocType => {
55
56
  if (code === DOCUMENT_TYPE_CODES['debitnote']) {
56
57
  return 'debitnote';
57
58
  }
59
+ if (code === DOCUMENT_TYPE_CODES['corrected-invoice']) {
60
+ return 'corrected-invoice';
61
+ }
58
62
  if (code === DOCUMENT_TYPE_CODES['self-billed-invoice']) {
59
63
  return 'self-billed-invoice';
60
64
  }
@@ -80,7 +80,9 @@ export class FormatDetector {
80
80
  );
81
81
  const customizationId = customizationIdMatch?.[1] ?? '';
82
82
 
83
- if (/xrechnung/i.test(customizationId) || /urn:xoev-de:kosit:standard:xrechnung/i.test(customizationId)) {
83
+ // both identifier families: urn:xeinkauf.de:kosit:xrechnung_3.0 (XRechnung 3.0) and
84
+ // urn:xoev-de:kosit:standard:xrechnung_x.y (XRechnung 1.x and 2.x)
85
+ if (/xrechnung/i.test(customizationId)) {
84
86
  return InvoiceFormat.XRECHNUNG;
85
87
  }
86
88
  return InvoiceFormat.UBL;
@@ -133,10 +135,8 @@ export class FormatDetector {
133
135
  }
134
136
 
135
137
  // Check for obvious XRechnung indicators
136
- if (
137
- /xrechnung/i.test(sample) ||
138
- /urn:xoev-de:kosit:standard:xrechnung/i.test(sample)
139
- ) {
138
+ // covers both identifier families (urn:xeinkauf.de:kosit:xrechnung_, urn:xoev-de:kosit:standard:xrechnung_)
139
+ if (/xrechnung/i.test(sample)) {
140
140
  return InvoiceFormat.XRECHNUNG;
141
141
  }
142
142
 
@@ -0,0 +1,37 @@
1
+ import { getWritableDueDate } from './date.value.js';
2
+
3
+ /**
4
+ * The payment terms note (BT-20) the encoders write: the due date (BT-9) the
5
+ * document states, in the document language (German for `de`, English
6
+ * otherwise). It names the date rather than a count of days, so it reads
7
+ * correctly for every term:
8
+ * - a term of 0 days: "Zahlbar sofort" / "Payable immediately";
9
+ * - a later due date: "Zahlbar bis 04.10.2026" / "Payable by 2026-10-04";
10
+ * - an earlier due date, which is legitimate when the payment fell due before
11
+ * the invoice was issued, as with an agreed advance payment or an invoice
12
+ * issued after the contractual due date: "Fällig seit dem 03.09.2026" /
13
+ * "Due since 2026-09-03".
14
+ * A term that is no whole number of days is refused by `getWritableDueDate`.
15
+ * @param issueTimestamp The issue date (BT-2)
16
+ * @param dueInDays The payment term in days, as the document states it
17
+ * @param language The document language
18
+ * @param targetFormat The format being written
19
+ */
20
+ export const getPaymentTermsNote = (
21
+ issueTimestamp: unknown,
22
+ dueInDays: unknown,
23
+ language: string | undefined,
24
+ targetFormat: string,
25
+ ): string => {
26
+ const dueDate = getWritableDueDate(issueTimestamp, dueInDays, targetFormat);
27
+ const isoDate = dueDate.toISOString().slice(0, 10);
28
+ const german = (language ?? '').toLowerCase().startsWith('de');
29
+ const germanDate = `${isoDate.slice(8, 10)}.${isoDate.slice(5, 7)}.${isoDate.slice(0, 4)}`;
30
+ if (dueInDays === 0) {
31
+ return german ? 'Zahlbar sofort' : 'Payable immediately';
32
+ }
33
+ if ((dueInDays as number) > 0) {
34
+ return german ? `Zahlbar bis ${germanDate}` : `Payable by ${isoDate}`;
35
+ }
36
+ return german ? `Fällig seit dem ${germanDate}` : `Due since ${isoDate}`;
37
+ };
@@ -0,0 +1,74 @@
1
+ import type {
2
+ TAccountingDoc,
3
+ TAccountingDocType,
4
+ TCorrectedInvoiceReference,
5
+ TRelatedDocument,
6
+ } from '../../interfaces/common.js';
7
+
8
+ /**
9
+ * One preceding invoice reference (BG-3): the number of the preceding invoice
10
+ * (BT-25) and, when the document states it, its issue date (BT-26).
11
+ */
12
+ export type TPrecedingInvoiceReference = TCorrectedInvoiceReference;
13
+
14
+ /**
15
+ * The preceding invoice references (BG-3) an encoder writes for a document,
16
+ * taken only from what the document states:
17
+ * - a corrected invoice: the invoice it corrects (`correctedInvoice`);
18
+ * - a corrected invoice or a credit note: every related document with the
19
+ * relation `'corrects'`.
20
+ * Other documents and other relations write none.
21
+ */
22
+ export const getPrecedingInvoiceReferences = (
23
+ accountingDoc: TAccountingDoc,
24
+ ): TPrecedingInvoiceReference[] => {
25
+ const references: TPrecedingInvoiceReference[] = [];
26
+ if (accountingDoc.accountingDocType === 'corrected-invoice' && accountingDoc.correctedInvoice) {
27
+ references.push(accountingDoc.correctedInvoice);
28
+ }
29
+ if (
30
+ accountingDoc.accountingDocType === 'corrected-invoice' ||
31
+ accountingDoc.accountingDocType === 'creditnote'
32
+ ) {
33
+ for (const relatedDocument of accountingDoc.relatedDocuments ?? []) {
34
+ if (relatedDocument.relationType === 'corrects') {
35
+ references.push(
36
+ relatedDocument.issueDate === undefined
37
+ ? { documentId: relatedDocument.documentId }
38
+ : { documentId: relatedDocument.documentId, issueDate: relatedDocument.issueDate },
39
+ );
40
+ }
41
+ }
42
+ }
43
+ return references;
44
+ };
45
+
46
+ /**
47
+ * The envelope fields for the preceding invoice references a decoder read, the
48
+ * inverse of {@link getPrecedingInvoiceReferences}: on a corrected invoice the
49
+ * first reference is the invoice it corrects and further ones are related
50
+ * documents it corrects; on a credit note every reference is a related
51
+ * document it corrects. Other documents keep no reference, as before.
52
+ */
53
+ export const getPrecedingInvoiceFields = (
54
+ accountingDocType: TAccountingDocType,
55
+ references: TPrecedingInvoiceReference[],
56
+ ): { correctedInvoice?: TCorrectedInvoiceReference; relatedDocuments?: TRelatedDocument[] } => {
57
+ const asRelated = (reference: TPrecedingInvoiceReference): TRelatedDocument => ({
58
+ relationType: 'corrects',
59
+ ...reference,
60
+ });
61
+ if (references.length === 0) {
62
+ return {};
63
+ }
64
+ if (accountingDocType === 'corrected-invoice') {
65
+ const [correctedInvoice, ...furtherReferences] = references;
66
+ return furtherReferences.length === 0
67
+ ? { correctedInvoice }
68
+ : { correctedInvoice, relatedDocuments: furtherReferences.map(asRelated) };
69
+ }
70
+ if (accountingDocType === 'creditnote') {
71
+ return { relatedDocuments: references.map(asRelated) };
72
+ }
73
+ return {};
74
+ };
@@ -11,6 +11,7 @@ import { EN16931BusinessRulesValidator } from './en16931.business-rules.validato
11
11
  import { CodeListValidator } from './codelist.validator.js';
12
12
  import type { ValidationResult, ValidationOptions, ValidationReport } from './validation.types.js';
13
13
  import type { EInvoice } from '../../einvoice.js';
14
+ import { UBL_CUSTOMIZATION_IDS } from '../ubl/ubl.types.js';
14
15
 
15
16
  /**
16
17
  * Main validator that combines all validation capabilities
@@ -196,9 +197,11 @@ export class MainValidator {
196
197
  const profileId = invoice.metadata?.profileId || '';
197
198
  const customizationId = invoice.metadata?.customizationId || '';
198
199
 
200
+ // XRechnung 3.0 (its extension and CVD identifiers begin with it), the identifier
201
+ // family of XRechnung 1.x and 2.x, and any identifier naming XRechnung
199
202
  const xrechnungProfiles = [
200
- 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung',
201
- 'urn:cen.eu:en16931:2017#conformant#urn:xeinkauf.de:kosit:xrechnung',
203
+ UBL_CUSTOMIZATION_IDS.XRECHNUNG,
204
+ 'urn:xoev-de:kosit:standard:xrechnung_',
202
205
  'xrechnung'
203
206
  ];
204
207
 
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type { EInvoice } from '../../einvoice.js';
10
10
  import type { ValidationResult } from './validation.types.js';
11
+ import { UBL_CUSTOMIZATION_IDS } from '../ubl/ubl.types.js';
11
12
 
12
13
  /**
13
14
  * XRechnung-specific validator implementing German CIUS rules
@@ -62,10 +63,11 @@ export class XRechnungValidator {
62
63
  const customizationId = invoice.metadata?.customizationId || '';
63
64
 
64
65
  // XRechnung profile identifiers
66
+ // XRechnung 3.0 (its extension and CVD identifiers begin with it), the identifier
67
+ // family of XRechnung 1.x and 2.x, and any identifier naming XRechnung
65
68
  const xrechnungProfiles = [
66
- 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
67
- 'urn:cen.eu:en16931:2017#conformant#urn:xeinkauf.de:kosit:xrechnung_3.0',
68
- 'urn:cen.eu:en16931:2017:xrechnung',
69
+ UBL_CUSTOMIZATION_IDS.XRECHNUNG,
70
+ 'urn:xoev-de:kosit:standard:xrechnung_',
69
71
  'xrechnung'
70
72
  ];
71
73
 
package/ts/index.ts CHANGED
@@ -74,6 +74,9 @@ import { ZUGFeRDV1Decoder } from './formats/cii/zugferd/zugferd.v1.decoder.js';
74
74
  export type {
75
75
  // Common interfaces
76
76
  TInvoice,
77
+ TCorrectedInvoice,
78
+ TCorrectedInvoiceReference,
79
+ TInvoiceCorrection,
77
80
  TCreditNote,
78
81
  TDebitNote,
79
82
  TSelfBilledInvoice,
@@ -83,11 +83,16 @@ export interface IPdf {
83
83
  // Re-export types from tsclass for convenience
84
84
  import type {
85
85
  TInvoice,
86
+ TCorrectedInvoice,
86
87
  TDebitNote,
87
88
  TSelfBilledInvoice,
88
89
  } from '@tsclass/tsclass/dist_ts/finance/index.js';
89
90
 
90
91
  export type { TInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
92
+ export type { TCorrectedInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
93
+ export type { TCorrectedInvoiceReference } from '@tsclass/tsclass/dist_ts/finance/index.js';
94
+ export type { TInvoiceCorrection } from '@tsclass/tsclass/dist_ts/finance/index.js';
95
+ export type { TRelatedDocument } from '@tsclass/tsclass/dist_ts/finance/index.js';
91
96
  export type { TCreditNote } from '@tsclass/tsclass/dist_ts/finance/index.js';
92
97
  export type { TDebitNote } from '@tsclass/tsclass/dist_ts/finance/index.js';
93
98
  export type { TSelfBilledInvoice } from '@tsclass/tsclass/dist_ts/finance/index.js';
@@ -100,7 +105,7 @@ export type { TAccountingDocEnvelope } from '@tsclass/tsclass/dist_ts/finance/in
100
105
  * `Invoice` root and the CII type codes of the invoice family. Only a credit
101
106
  * note has a document shape of its own.
102
107
  */
103
- export type TInvoiceDocument = TInvoice | TDebitNote | TSelfBilledInvoice;
108
+ export type TInvoiceDocument = TInvoice | TCorrectedInvoice | TDebitNote | TSelfBilledInvoice;
104
109
  export type { TContact } from '@tsclass/tsclass/dist_ts/business/index.js';
105
110
  export type { TLetterEnvelope } from '@tsclass/tsclass/dist_ts/business/index.js';
106
111
  export type { TDocumentEnvelope } from '@tsclass/tsclass/dist_ts/business/index.js';