@fin.cx/einvoice 5.0.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.
Files changed (171) hide show
  1. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  2. package/dist_ts/00_commitinfo_data.js +9 -0
  3. package/dist_ts/classes.decoder.d.ts +40 -0
  4. package/dist_ts/classes.decoder.js +320 -0
  5. package/dist_ts/classes.encoder.d.ts +51 -0
  6. package/dist_ts/classes.encoder.js +293 -0
  7. package/dist_ts/classes.xinvoice.d.ts +136 -0
  8. package/dist_ts/classes.xinvoice.js +352 -0
  9. package/dist_ts/einvoice.d.ts +217 -0
  10. package/dist_ts/einvoice.js +505 -0
  11. package/dist_ts/errors.d.ts +121 -0
  12. package/dist_ts/errors.js +241 -0
  13. package/dist_ts/formats/base/base.decoder.d.ts +32 -0
  14. package/dist_ts/formats/base/base.decoder.js +52 -0
  15. package/dist_ts/formats/base/base.encoder.d.ts +13 -0
  16. package/dist_ts/formats/base/base.encoder.js +7 -0
  17. package/dist_ts/formats/base/base.validator.d.ts +44 -0
  18. package/dist_ts/formats/base/base.validator.js +39 -0
  19. package/dist_ts/formats/base.decoder.d.ts +19 -0
  20. package/dist_ts/formats/base.decoder.js +130 -0
  21. package/dist_ts/formats/base.validator.d.ts +44 -0
  22. package/dist_ts/formats/base.validator.js +39 -0
  23. package/dist_ts/formats/cii/cii.decoder.d.ts +61 -0
  24. package/dist_ts/formats/cii/cii.decoder.js +111 -0
  25. package/dist_ts/formats/cii/cii.encoder.d.ts +43 -0
  26. package/dist_ts/formats/cii/cii.encoder.js +48 -0
  27. package/dist_ts/formats/cii/cii.types.d.ts +31 -0
  28. package/dist_ts/formats/cii/cii.types.js +40 -0
  29. package/dist_ts/formats/cii/cii.validator.d.ts +56 -0
  30. package/dist_ts/formats/cii/cii.validator.js +146 -0
  31. package/dist_ts/formats/cii/facturx/facturx.decoder.d.ts +43 -0
  32. package/dist_ts/formats/cii/facturx/facturx.decoder.js +207 -0
  33. package/dist_ts/formats/cii/facturx/facturx.encoder.d.ts +82 -0
  34. package/dist_ts/formats/cii/facturx/facturx.encoder.js +400 -0
  35. package/dist_ts/formats/cii/facturx/facturx.types.d.ts +10 -0
  36. package/dist_ts/formats/cii/facturx/facturx.types.js +15 -0
  37. package/dist_ts/formats/cii/facturx/facturx.validator.d.ts +32 -0
  38. package/dist_ts/formats/cii/facturx/facturx.validator.js +138 -0
  39. package/dist_ts/formats/cii/zugferd/zugferd.decoder.d.ts +43 -0
  40. package/dist_ts/formats/cii/zugferd/zugferd.decoder.js +218 -0
  41. package/dist_ts/formats/cii/zugferd/zugferd.encoder.d.ts +97 -0
  42. package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +550 -0
  43. package/dist_ts/formats/cii/zugferd/zugferd.types.d.ts +10 -0
  44. package/dist_ts/formats/cii/zugferd/zugferd.types.js +15 -0
  45. package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.d.ts +49 -0
  46. package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.js +229 -0
  47. package/dist_ts/formats/cii/zugferd/zugferd.validator.d.ts +16 -0
  48. package/dist_ts/formats/cii/zugferd/zugferd.validator.js +29 -0
  49. package/dist_ts/formats/decoder.factory.d.ts +15 -0
  50. package/dist_ts/formats/decoder.factory.js +46 -0
  51. package/dist_ts/formats/factories/decoder.factory.d.ts +13 -0
  52. package/dist_ts/formats/factories/decoder.factory.js +56 -0
  53. package/dist_ts/formats/factories/encoder.factory.d.ts +14 -0
  54. package/dist_ts/formats/factories/encoder.factory.js +40 -0
  55. package/dist_ts/formats/factories/validator.factory.d.ts +12 -0
  56. package/dist_ts/formats/factories/validator.factory.js +110 -0
  57. package/dist_ts/formats/factorx.decoder.d.ts +18 -0
  58. package/dist_ts/formats/factorx.decoder.js +178 -0
  59. package/dist_ts/formats/factorx.encoder.d.ts +51 -0
  60. package/dist_ts/formats/factorx.encoder.js +293 -0
  61. package/dist_ts/formats/facturx.decoder.d.ts +18 -0
  62. package/dist_ts/formats/facturx.decoder.js +210 -0
  63. package/dist_ts/formats/facturx.encoder.d.ts +51 -0
  64. package/dist_ts/formats/facturx.encoder.js +293 -0
  65. package/dist_ts/formats/facturx.validator.d.ts +67 -0
  66. package/dist_ts/formats/facturx.validator.js +266 -0
  67. package/dist_ts/formats/pdf/extractors/associated.extractor.d.ts +14 -0
  68. package/dist_ts/formats/pdf/extractors/associated.extractor.js +68 -0
  69. package/dist_ts/formats/pdf/extractors/base.extractor.d.ts +92 -0
  70. package/dist_ts/formats/pdf/extractors/base.extractor.js +319 -0
  71. package/dist_ts/formats/pdf/extractors/index.d.ts +4 -0
  72. package/dist_ts/formats/pdf/extractors/index.js +5 -0
  73. package/dist_ts/formats/pdf/extractors/standard.extractor.d.ts +13 -0
  74. package/dist_ts/formats/pdf/extractors/standard.extractor.js +74 -0
  75. package/dist_ts/formats/pdf/extractors/text.extractor.d.ts +45 -0
  76. package/dist_ts/formats/pdf/extractors/text.extractor.js +151 -0
  77. package/dist_ts/formats/pdf/pdf.embedder.d.ts +69 -0
  78. package/dist_ts/formats/pdf/pdf.embedder.js +183 -0
  79. package/dist_ts/formats/pdf/pdf.extractor.d.ts +49 -0
  80. package/dist_ts/formats/pdf/pdf.extractor.js +98 -0
  81. package/dist_ts/formats/pdf/robust-pdf.extractor.d.ts +40 -0
  82. package/dist_ts/formats/pdf/robust-pdf.extractor.js +324 -0
  83. package/dist_ts/formats/ubl/en16931.ubl.validator.d.ts +18 -0
  84. package/dist_ts/formats/ubl/en16931.ubl.validator.js +169 -0
  85. package/dist_ts/formats/ubl/generic/ubl.encoder.d.ts +151 -0
  86. package/dist_ts/formats/ubl/generic/ubl.encoder.js +892 -0
  87. package/dist_ts/formats/ubl/ubl.decoder.d.ts +61 -0
  88. package/dist_ts/formats/ubl/ubl.decoder.js +95 -0
  89. package/dist_ts/formats/ubl/ubl.encoder.d.ts +38 -0
  90. package/dist_ts/formats/ubl/ubl.encoder.js +46 -0
  91. package/dist_ts/formats/ubl/ubl.types.d.ts +16 -0
  92. package/dist_ts/formats/ubl/ubl.types.js +21 -0
  93. package/dist_ts/formats/ubl/ubl.validator.d.ts +50 -0
  94. package/dist_ts/formats/ubl/ubl.validator.js +112 -0
  95. package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.d.ts +34 -0
  96. package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +424 -0
  97. package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.d.ts +75 -0
  98. package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.js +555 -0
  99. package/dist_ts/formats/ubl/xrechnung.validator.d.ts +15 -0
  100. package/dist_ts/formats/ubl/xrechnung.validator.js +107 -0
  101. package/dist_ts/formats/ubl.validator.d.ts +82 -0
  102. package/dist_ts/formats/ubl.validator.js +306 -0
  103. package/dist_ts/formats/utils/format.detector.d.ts +62 -0
  104. package/dist_ts/formats/utils/format.detector.js +249 -0
  105. package/dist_ts/formats/validation/en16931.validator.d.ts +23 -0
  106. package/dist_ts/formats/validation/en16931.validator.js +119 -0
  107. package/dist_ts/formats/validator.factory.d.ts +18 -0
  108. package/dist_ts/formats/validator.factory.js +78 -0
  109. package/dist_ts/formats/xinvoice.decoder.d.ts +28 -0
  110. package/dist_ts/formats/xinvoice.decoder.js +332 -0
  111. package/dist_ts/formats/xinvoice.encoder.d.ts +19 -0
  112. package/dist_ts/formats/xinvoice.encoder.js +282 -0
  113. package/dist_ts/index.d.ts +51 -0
  114. package/dist_ts/index.js +90 -0
  115. package/dist_ts/interfaces/common.d.ts +79 -0
  116. package/dist_ts/interfaces/common.js +24 -0
  117. package/dist_ts/interfaces.d.ts +87 -0
  118. package/dist_ts/interfaces.js +23 -0
  119. package/dist_ts/plugins.d.ts +14 -0
  120. package/dist_ts/plugins.js +31 -0
  121. package/npmextra.json +35 -0
  122. package/package.json +71 -0
  123. package/readme.hints.md +1107 -0
  124. package/readme.howtofixtests.md +38 -0
  125. package/readme.literature.md +1 -0
  126. package/readme.md +998 -0
  127. package/readme.plan.md +481 -0
  128. package/ts/00_commitinfo_data.ts +8 -0
  129. package/ts/einvoice.ts +603 -0
  130. package/ts/errors.ts +341 -0
  131. package/ts/formats/base/base.decoder.ts +68 -0
  132. package/ts/formats/base/base.encoder.ts +14 -0
  133. package/ts/formats/base/base.validator.ts +64 -0
  134. package/ts/formats/cii/cii.decoder.ts +139 -0
  135. package/ts/formats/cii/cii.encoder.ts +64 -0
  136. package/ts/formats/cii/cii.types.ts +44 -0
  137. package/ts/formats/cii/cii.validator.ts +171 -0
  138. package/ts/formats/cii/facturx/facturx.decoder.ts +243 -0
  139. package/ts/formats/cii/facturx/facturx.encoder.ts +483 -0
  140. package/ts/formats/cii/facturx/facturx.types.ts +18 -0
  141. package/ts/formats/cii/facturx/facturx.validator.ts +180 -0
  142. package/ts/formats/cii/zugferd/zugferd.decoder.ts +256 -0
  143. package/ts/formats/cii/zugferd/zugferd.encoder.ts +661 -0
  144. package/ts/formats/cii/zugferd/zugferd.types.ts +18 -0
  145. package/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +267 -0
  146. package/ts/formats/cii/zugferd/zugferd.validator.ts +31 -0
  147. package/ts/formats/factories/decoder.factory.ts +62 -0
  148. package/ts/formats/factories/encoder.factory.ts +47 -0
  149. package/ts/formats/factories/validator.factory.ts +134 -0
  150. package/ts/formats/pdf/extractors/associated.extractor.ts +78 -0
  151. package/ts/formats/pdf/extractors/base.extractor.ts +355 -0
  152. package/ts/formats/pdf/extractors/index.ts +4 -0
  153. package/ts/formats/pdf/extractors/standard.extractor.ts +86 -0
  154. package/ts/formats/pdf/extractors/text.extractor.ts +162 -0
  155. package/ts/formats/pdf/pdf.embedder.ts +242 -0
  156. package/ts/formats/pdf/pdf.extractor.ts +141 -0
  157. package/ts/formats/ubl/en16931.ubl.validator.ts +216 -0
  158. package/ts/formats/ubl/generic/ubl.encoder.ts +1041 -0
  159. package/ts/formats/ubl/ubl.decoder.ts +121 -0
  160. package/ts/formats/ubl/ubl.encoder.ts +63 -0
  161. package/ts/formats/ubl/ubl.types.ts +22 -0
  162. package/ts/formats/ubl/ubl.validator.ts +133 -0
  163. package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +471 -0
  164. package/ts/formats/ubl/xrechnung/xrechnung.encoder.ts +619 -0
  165. package/ts/formats/ubl/xrechnung.validator.ts +185 -0
  166. package/ts/formats/utils/format.detector.ts +306 -0
  167. package/ts/formats/validation/en16931.validator.ts +135 -0
  168. package/ts/index.ts +164 -0
  169. package/ts/interfaces/common.ts +90 -0
  170. package/ts/interfaces.ts +98 -0
  171. package/ts/plugins.ts +61 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * CII-specific types and constants
3
+ */
4
+
5
+ // CII namespaces (ZUGFeRD v2/Factur-X)
6
+ export const CII_NAMESPACES = {
7
+ RSM: 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100',
8
+ RAM: 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100',
9
+ UDT: 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'
10
+ };
11
+
12
+ // ZUGFeRD v1 namespaces
13
+ export const ZUGFERD_V1_NAMESPACES = {
14
+ RSM: 'urn:ferd:CrossIndustryDocument:invoice:1p0',
15
+ RAM: 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:12',
16
+ UDT: 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:15'
17
+ };
18
+
19
+ // CII profiles
20
+ export enum CIIProfile {
21
+ BASIC = 'BASIC',
22
+ COMFORT = 'COMFORT',
23
+ EXTENDED = 'EXTENDED',
24
+ EN16931 = 'EN16931',
25
+ MINIMUM = 'MINIMUM'
26
+ }
27
+
28
+ // CII profile IDs for different formats
29
+ export const CII_PROFILE_IDS = {
30
+ // Factur-X profiles
31
+ FACTURX_MINIMUM: 'urn:factur-x.eu:1p0:minimum',
32
+ FACTURX_BASIC: 'urn:factur-x.eu:1p0:basicwl',
33
+ FACTURX_EN16931: 'urn:cen.eu:en16931:2017',
34
+
35
+ // ZUGFeRD v2 profiles
36
+ ZUGFERD_BASIC: 'urn:zugferd:basic',
37
+ ZUGFERD_COMFORT: 'urn:zugferd:comfort',
38
+ ZUGFERD_EXTENDED: 'urn:zugferd:extended',
39
+
40
+ // ZUGFeRD v1 profiles
41
+ ZUGFERD_V1_BASIC: 'urn:ferd:CrossIndustryDocument:invoice:1p0:basic',
42
+ ZUGFERD_V1_COMFORT: 'urn:ferd:CrossIndustryDocument:invoice:1p0:comfort',
43
+ ZUGFERD_V1_EXTENDED: 'urn:ferd:CrossIndustryDocument:invoice:1p0:extended'
44
+ };
@@ -0,0 +1,171 @@
1
+ import { BaseValidator } from '../base/base.validator.js';
2
+ import { ValidationLevel } from '../../interfaces/common.js';
3
+ import type { ValidationResult } from '../../interfaces/common.js';
4
+ import { CII_NAMESPACES, CIIProfile } from './cii.types.js';
5
+ import { DOMParser, xpath } from '../../plugins.js';
6
+
7
+ /**
8
+ * Base validator for CII-based invoice formats
9
+ */
10
+ export abstract class CIIBaseValidator extends BaseValidator {
11
+ protected doc: Document;
12
+ protected namespaces: Record<string, string>;
13
+ protected select: xpath.XPathSelect;
14
+ protected profile: CIIProfile = CIIProfile.EN16931;
15
+
16
+ constructor(xml: string) {
17
+ super(xml);
18
+
19
+ try {
20
+ // Parse XML document
21
+ this.doc = new DOMParser().parseFromString(xml, 'application/xml');
22
+
23
+ // Set up namespaces for XPath queries
24
+ this.namespaces = {
25
+ rsm: CII_NAMESPACES.RSM,
26
+ ram: CII_NAMESPACES.RAM,
27
+ udt: CII_NAMESPACES.UDT
28
+ };
29
+
30
+ // Create XPath selector with namespaces
31
+ this.select = xpath.useNamespaces(this.namespaces);
32
+
33
+ // Detect profile
34
+ this.detectProfile();
35
+ } catch (error) {
36
+ this.addError('CII-PARSE', `Failed to parse XML: ${error}`, '/');
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Validates CII XML against the specified level of validation
42
+ * @param level Validation level
43
+ * @returns Result of validation
44
+ */
45
+ public validate(level: ValidationLevel = ValidationLevel.SYNTAX): ValidationResult {
46
+ // Reset errors
47
+ this.errors = [];
48
+
49
+ // Check if document was parsed successfully
50
+ if (!this.doc) {
51
+ return {
52
+ valid: false,
53
+ errors: this.errors,
54
+ level: level
55
+ };
56
+ }
57
+
58
+ // Perform validation based on level
59
+ let valid = true;
60
+
61
+ if (level === ValidationLevel.SYNTAX) {
62
+ valid = this.validateSchema();
63
+ } else if (level === ValidationLevel.SEMANTIC) {
64
+ valid = this.validateSchema() && this.validateStructure();
65
+ } else if (level === ValidationLevel.BUSINESS) {
66
+ valid = this.validateSchema() &&
67
+ this.validateStructure() &&
68
+ this.validateBusinessRules();
69
+ }
70
+
71
+ return {
72
+ valid,
73
+ errors: this.errors,
74
+ level
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Validates CII XML against schema
80
+ * @returns True if schema validation passed
81
+ */
82
+ protected validateSchema(): boolean {
83
+ // Basic schema validation (simplified for now)
84
+ if (!this.doc) return false;
85
+
86
+ // Check for root element
87
+ const root = this.doc.documentElement;
88
+ if (!root || root.nodeName !== 'rsm:CrossIndustryInvoice') {
89
+ this.addError('CII-SCHEMA-1', 'Root element must be rsm:CrossIndustryInvoice', '/');
90
+ return false;
91
+ }
92
+
93
+ // Check for required namespaces
94
+ if (!root.lookupNamespaceURI('rsm') || !root.lookupNamespaceURI('ram')) {
95
+ this.addError('CII-SCHEMA-2', 'Required namespaces rsm and ram must be declared', '/');
96
+ return false;
97
+ }
98
+
99
+ return true;
100
+ }
101
+
102
+ /**
103
+ * Validates structure of the CII XML document
104
+ * @returns True if structure validation passed
105
+ */
106
+ protected abstract validateStructure(): boolean;
107
+
108
+ /**
109
+ * Detects the CII profile from the XML
110
+ */
111
+ protected detectProfile(): void {
112
+ // Look for profile identifier
113
+ const profileNode = this.select(
114
+ 'string(//rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/ram:ID)',
115
+ this.doc
116
+ );
117
+
118
+ if (profileNode) {
119
+ const profileText = profileNode.toString();
120
+
121
+ if (profileText.includes('BASIC')) {
122
+ this.profile = CIIProfile.BASIC;
123
+ } else if (profileText.includes('EN16931')) {
124
+ this.profile = CIIProfile.EN16931;
125
+ } else if (profileText.includes('EXTENDED')) {
126
+ this.profile = CIIProfile.EXTENDED;
127
+ } else if (profileText.includes('MINIMUM')) {
128
+ this.profile = CIIProfile.MINIMUM;
129
+ } else if (profileText.includes('COMFORT')) {
130
+ this.profile = CIIProfile.COMFORT;
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Gets a text value from an XPath expression
137
+ * @param xpath XPath expression
138
+ * @param context Optional context node
139
+ * @returns Text value or empty string if not found
140
+ */
141
+ protected getText(xpathExpr: string, context?: Node): string {
142
+ const node = this.select(xpathExpr, context || this.doc)[0];
143
+ return node ? (node.textContent || '') : '';
144
+ }
145
+
146
+ /**
147
+ * Gets a number value from an XPath expression
148
+ * @param xpath XPath expression
149
+ * @param context Optional context node
150
+ * @returns Number value or 0 if not found or not a number
151
+ */
152
+ protected getNumber(xpathExpr: string, context?: Node): number {
153
+ const text = this.getText(xpathExpr, context);
154
+ const num = parseFloat(text);
155
+ return isNaN(num) ? 0 : num;
156
+ }
157
+
158
+ /**
159
+ * Checks if a node exists
160
+ * @param xpath XPath expression
161
+ * @param context Optional context node
162
+ * @returns True if node exists
163
+ */
164
+ protected exists(xpathExpr: string, context?: Node): boolean {
165
+ const nodes = this.select(xpathExpr, context || this.doc);
166
+ if (Array.isArray(nodes)) {
167
+ return nodes.length > 0;
168
+ }
169
+ return false;
170
+ }
171
+ }
@@ -0,0 +1,243 @@
1
+ import { CIIBaseDecoder } from '../cii.decoder.js';
2
+ import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js';
3
+ import { FACTURX_PROFILE_IDS } from './facturx.types.js';
4
+ import { business, finance, general } from '../../../plugins.js';
5
+ import { EN16931Validator } from '../../validation/en16931.validator.js';
6
+
7
+ /**
8
+ * Decoder for Factur-X invoice format
9
+ */
10
+ export class FacturXDecoder extends CIIBaseDecoder {
11
+ /**
12
+ * Decodes a Factur-X credit note
13
+ * @returns Promise resolving to a TCreditNote object
14
+ */
15
+ protected async decodeCreditNote(): Promise<TCreditNote> {
16
+ // Get common invoice data
17
+ const commonData = await this.extractCommonData();
18
+
19
+ // Create a credit note with the common data
20
+ return {
21
+ ...commonData,
22
+ accountingDocType: 'creditnote' as const
23
+ } as unknown as TCreditNote;
24
+ }
25
+
26
+ /**
27
+ * Decodes a Factur-X debit note (invoice)
28
+ * @returns Promise resolving to a TDebitNote object
29
+ */
30
+ protected async decodeDebitNote(): Promise<TDebitNote> {
31
+ // Get common invoice data
32
+ const commonData = await this.extractCommonData();
33
+
34
+ // Create a debit note with the common data
35
+ return {
36
+ ...commonData,
37
+ accountingDocType: 'debitnote' as const
38
+ } as unknown as TDebitNote;
39
+ }
40
+
41
+ /**
42
+ * Extracts common invoice data from Factur-X XML
43
+ * @returns Common invoice data
44
+ */
45
+ private async extractCommonData(): Promise<Partial<TInvoice>> {
46
+ // Extract invoice ID
47
+ const invoiceId = this.getText('//rsm:ExchangedDocument/ram:ID');
48
+
49
+ // Extract issue date
50
+ const issueDateStr = this.getText('//ram:IssueDateTime/udt:DateTimeString');
51
+ const issueDateFormat = this.getText('//ram:IssueDateTime/udt:DateTimeString/@format');
52
+ const issueDate = this.parseCIIDate(issueDateStr, issueDateFormat);
53
+
54
+ // Extract seller information
55
+ const seller = this.extractParty('//ram:SellerTradeParty');
56
+
57
+ // Extract buyer information
58
+ const buyer = this.extractParty('//ram:BuyerTradeParty');
59
+
60
+ // Extract items
61
+ const items = this.extractItems();
62
+
63
+ // Extract due date
64
+ const dueDateStr = this.getText('//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString');
65
+ const dueDateFormat = this.getText('//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime/udt:DateTimeString/@format');
66
+ const dueDate = dueDateStr ? this.parseCIIDate(dueDateStr, dueDateFormat) : issueDate;
67
+ const dueInDays = Math.round((dueDate - issueDate) / (1000 * 60 * 60 * 24));
68
+
69
+ // Extract currency
70
+ const currencyCode = this.getText('//ram:InvoiceCurrencyCode') || 'EUR';
71
+
72
+ // Extract total amount
73
+ const totalAmount = this.getNumber('//ram:GrandTotalAmount');
74
+
75
+ // Extract notes
76
+ const allNotes = this.extractNotes();
77
+
78
+ // Extract subject and notes separately
79
+ let subject = `Invoice ${invoiceId}`;
80
+ let notes = [...allNotes];
81
+
82
+ // If the first note doesn't look like a payment term or other standard note,
83
+ // treat it as the subject
84
+ if (allNotes.length > 0 && !allNotes[0].toLowerCase().includes('due in') &&
85
+ !allNotes[0].toLowerCase().includes('payment')) {
86
+ subject = allNotes[0];
87
+ notes = allNotes.slice(1); // Remove subject from notes
88
+ }
89
+
90
+ // Check for reverse charge
91
+ const reverseCharge = this.exists('//ram:SpecifiedTradeAllowanceCharge/ram:ReasonCode[text()="62"]');
92
+
93
+ // Create the common invoice data
94
+ const invoiceData = {
95
+ type: 'accounting-doc' as const,
96
+ accountingDocType: 'invoice' as const,
97
+ id: invoiceId,
98
+ accountingDocId: invoiceId,
99
+ date: issueDate,
100
+ accountingDocStatus: 'issued' as const,
101
+ versionInfo: {
102
+ type: 'final' as const,
103
+ version: '1.0.0'
104
+ },
105
+ language: 'en',
106
+ incidenceId: invoiceId,
107
+ from: seller,
108
+ to: buyer,
109
+ subject: subject,
110
+ items: items,
111
+ dueInDays: dueInDays,
112
+ reverseCharge: reverseCharge,
113
+ currency: currencyCode as finance.TCurrency,
114
+ notes: notes,
115
+ deliveryDate: issueDate,
116
+ objectActions: []
117
+ };
118
+
119
+ // Validate mandatory EN16931 fields unless validation is skipped
120
+ if (!this.skipValidation) {
121
+ EN16931Validator.validateMandatoryFields(invoiceData);
122
+ }
123
+
124
+ return invoiceData;
125
+ }
126
+
127
+ /**
128
+ * Extracts party information from Factur-X XML
129
+ * @param partyXPath XPath to the party node
130
+ * @returns Party information as TContact
131
+ */
132
+ private extractParty(partyXPath: string): business.TContact {
133
+ // Extract name
134
+ const name = this.getText(`${partyXPath}/ram:Name`);
135
+
136
+ // Extract address
137
+ const address: business.IAddress = {
138
+ streetName: this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:LineOne`) || '',
139
+ houseNumber: this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:LineTwo`) || '0',
140
+ postalCode: this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:PostcodeCode`) || '',
141
+ city: this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CityName`) || '',
142
+ country: this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CountryID`) || '',
143
+ countryCode: this.getText(`${partyXPath}/ram:PostalTradeAddress/ram:CountryID`) || ''
144
+ };
145
+
146
+ // Extract VAT ID
147
+ const vatId = this.getText(`${partyXPath}/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]`) || '';
148
+
149
+ // Extract registration ID
150
+ const registrationId = this.getText(`${partyXPath}/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]`) || '';
151
+
152
+ // Create contact object
153
+ return {
154
+ type: 'company',
155
+ name: name,
156
+ description: '',
157
+ address: address,
158
+ status: 'active',
159
+ foundedDate: this.createDefaultDate(),
160
+ registrationDetails: {
161
+ vatId: vatId,
162
+ registrationId: registrationId,
163
+ registrationName: ''
164
+ }
165
+ } as business.TContact;
166
+ }
167
+
168
+ /**
169
+ * Extracts invoice items from Factur-X XML
170
+ * @returns Array of invoice items
171
+ */
172
+ private extractItems(): finance.TAccountingDocItem[] {
173
+ const items: finance.TAccountingDocItem[] = [];
174
+
175
+ // Get all item nodes
176
+ const itemNodes = this.select('//ram:IncludedSupplyChainTradeLineItem', this.doc);
177
+
178
+ // Process each item
179
+ if (Array.isArray(itemNodes)) {
180
+ for (let i = 0; i < itemNodes.length; i++) {
181
+ const itemNode = itemNodes[i];
182
+
183
+ // Extract item data
184
+ const name = this.getText('ram:SpecifiedTradeProduct/ram:Name', itemNode);
185
+ const articleNumber = this.getText('ram:SpecifiedTradeProduct/ram:SellerAssignedID', itemNode);
186
+ const unitQuantity = this.getNumber('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity', itemNode);
187
+ const unitType = this.getText('ram:SpecifiedLineTradeDelivery/ram:BilledQuantity/@unitCode', itemNode) || 'EA';
188
+ const unitNetPrice = this.getNumber('ram:SpecifiedLineTradeAgreement/ram:NetPriceProductTradePrice/ram:ChargeAmount', itemNode);
189
+ const vatPercentage = this.getNumber('ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', itemNode);
190
+
191
+ // Create item object
192
+ items.push({
193
+ position: i + 1,
194
+ name: name,
195
+ articleNumber: articleNumber,
196
+ unitType: unitType,
197
+ unitQuantity: unitQuantity,
198
+ unitNetPrice: unitNetPrice,
199
+ vatPercentage: vatPercentage
200
+ });
201
+ }
202
+ }
203
+
204
+ return items;
205
+ }
206
+
207
+ /**
208
+ * Extracts notes from Factur-X XML
209
+ * @returns Array of notes
210
+ */
211
+ private extractNotes(): string[] {
212
+ const notes: string[] = [];
213
+
214
+ // Get all note nodes
215
+ const noteNodes = this.select('//ram:IncludedNote', this.doc);
216
+
217
+ // Process each note
218
+ if (Array.isArray(noteNodes)) {
219
+ for (let i = 0; i < noteNodes.length; i++) {
220
+ const noteNode = noteNodes[i];
221
+ const noteText = this.getText('ram:Content', noteNode);
222
+
223
+ if (noteText) {
224
+ notes.push(noteText);
225
+ }
226
+ }
227
+ }
228
+
229
+ return notes;
230
+ }
231
+
232
+ /**
233
+ * Creates a default date object
234
+ * @returns Default date object
235
+ */
236
+ private createDefaultDate(): general.IDate {
237
+ return {
238
+ year: 2000,
239
+ month: 1,
240
+ day: 1
241
+ };
242
+ }
243
+ }