@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,661 @@
1
+ import { CIIBaseEncoder } from '../cii.encoder.js';
2
+ import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js';
3
+ import { ZUGFERD_PROFILE_IDS } from './zugferd.types.js';
4
+ import { CIIProfile } from '../cii.types.js';
5
+ import { DOMParser, XMLSerializer } from '../../../plugins.js';
6
+
7
+ /**
8
+ * Encoder for ZUGFeRD invoice format
9
+ */
10
+ export class ZUGFeRDEncoder extends CIIBaseEncoder {
11
+ constructor() {
12
+ super();
13
+ // Set default profile to BASIC
14
+ this.profile = CIIProfile.BASIC;
15
+ }
16
+
17
+ /**
18
+ * Encodes a credit note into ZUGFeRD XML
19
+ * @param creditNote Credit note to encode
20
+ * @returns ZUGFeRD XML string
21
+ */
22
+ protected async encodeCreditNote(creditNote: TCreditNote): Promise<string> {
23
+ // Create base XML
24
+ const xmlDoc = this.createBaseXml();
25
+
26
+ // Set document type code to credit note (381)
27
+ this.setDocumentTypeCode(xmlDoc, '381');
28
+
29
+ // Add common invoice data
30
+ this.addCommonInvoiceData(xmlDoc, creditNote as unknown as TInvoice);
31
+
32
+ // Serialize to string
33
+ return new XMLSerializer().serializeToString(xmlDoc);
34
+ }
35
+
36
+ /**
37
+ * Encodes a debit note (invoice) into ZUGFeRD XML
38
+ * @param debitNote Debit note to encode
39
+ * @returns ZUGFeRD XML string
40
+ */
41
+ protected async encodeDebitNote(debitNote: TDebitNote): Promise<string> {
42
+ // Create base XML
43
+ const xmlDoc = this.createBaseXml();
44
+
45
+ // Set document type code to invoice (380)
46
+ this.setDocumentTypeCode(xmlDoc, '380');
47
+
48
+ // Add common invoice data
49
+ this.addCommonInvoiceData(xmlDoc, debitNote as unknown as TInvoice);
50
+
51
+ // Serialize to string
52
+ return new XMLSerializer().serializeToString(xmlDoc);
53
+ }
54
+
55
+ /**
56
+ * Creates a base ZUGFeRD XML document
57
+ * @returns XML document with basic structure
58
+ */
59
+ private createBaseXml(): Document {
60
+ // Create XML document from template
61
+ const xmlString = this.createXmlRoot();
62
+ const doc = new DOMParser().parseFromString(xmlString, 'application/xml');
63
+
64
+ // Add ZUGFeRD profile
65
+ this.addProfile(doc);
66
+
67
+ return doc;
68
+ }
69
+
70
+ /**
71
+ * Adds ZUGFeRD profile information to the XML document
72
+ * @param doc XML document
73
+ */
74
+ private addProfile(doc: Document): void {
75
+ // Get root element
76
+ const root = doc.documentElement;
77
+
78
+ // Create context element if it doesn't exist
79
+ let contextElement = root.getElementsByTagName('rsm:ExchangedDocumentContext')[0];
80
+ if (!contextElement) {
81
+ contextElement = doc.createElement('rsm:ExchangedDocumentContext');
82
+ root.appendChild(contextElement);
83
+ }
84
+
85
+ // Create guideline parameter element
86
+ const guidelineElement = doc.createElement('ram:GuidelineSpecifiedDocumentContextParameter');
87
+ contextElement.appendChild(guidelineElement);
88
+
89
+ // Add ID element with profile
90
+ const idElement = doc.createElement('ram:ID');
91
+
92
+ // Set profile based on the selected profile
93
+ let profileId = ZUGFERD_PROFILE_IDS.BASIC;
94
+ if (this.profile === CIIProfile.COMFORT) {
95
+ profileId = ZUGFERD_PROFILE_IDS.COMFORT;
96
+ } else if (this.profile === CIIProfile.EXTENDED) {
97
+ profileId = ZUGFERD_PROFILE_IDS.EXTENDED;
98
+ }
99
+
100
+ idElement.textContent = profileId;
101
+ guidelineElement.appendChild(idElement);
102
+ }
103
+
104
+ /**
105
+ * Sets the document type code in the XML document
106
+ * @param doc XML document
107
+ * @param typeCode Document type code (380 for invoice, 381 for credit note)
108
+ */
109
+ private setDocumentTypeCode(doc: Document, typeCode: string): void {
110
+ // Get root element
111
+ const root = doc.documentElement;
112
+
113
+ // Create document element if it doesn't exist
114
+ let documentElement = root.getElementsByTagName('rsm:ExchangedDocument')[0];
115
+ if (!documentElement) {
116
+ documentElement = doc.createElement('rsm:ExchangedDocument');
117
+ root.appendChild(documentElement);
118
+ }
119
+
120
+ // Add type code element
121
+ const typeCodeElement = doc.createElement('ram:TypeCode');
122
+ typeCodeElement.textContent = typeCode;
123
+ documentElement.appendChild(typeCodeElement);
124
+ }
125
+
126
+ /**
127
+ * Adds common invoice data to the XML document
128
+ * @param doc XML document
129
+ * @param invoice Invoice data
130
+ */
131
+ private addCommonInvoiceData(doc: Document, invoice: TInvoice): void {
132
+ // Get root element
133
+ const root = doc.documentElement;
134
+
135
+ // Get document element or create it
136
+ let documentElement = root.getElementsByTagName('rsm:ExchangedDocument')[0];
137
+ if (!documentElement) {
138
+ documentElement = doc.createElement('rsm:ExchangedDocument');
139
+ root.appendChild(documentElement);
140
+ }
141
+
142
+ // Add ID element
143
+ const idElement = doc.createElement('ram:ID');
144
+ idElement.textContent = invoice.id;
145
+ documentElement.appendChild(idElement);
146
+
147
+ // Add issue date element
148
+ const issueDateElement = doc.createElement('ram:IssueDateTime');
149
+ const dateStringElement = doc.createElement('udt:DateTimeString');
150
+ dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
151
+ dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.date);
152
+ issueDateElement.appendChild(dateStringElement);
153
+ documentElement.appendChild(issueDateElement);
154
+
155
+ // Add notes - include subject as first note if it exists
156
+ const allNotes: string[] = [];
157
+ if (invoice.subject && invoice.subject.trim()) {
158
+ allNotes.push(invoice.subject);
159
+ }
160
+ if (invoice.notes && invoice.notes.length > 0) {
161
+ allNotes.push(...invoice.notes);
162
+ }
163
+
164
+ // Write all notes
165
+ for (const note of allNotes) {
166
+ const noteElement = doc.createElement('ram:IncludedNote');
167
+ const contentElement = doc.createElement('ram:Content');
168
+ contentElement.textContent = note;
169
+ noteElement.appendChild(contentElement);
170
+ documentElement.appendChild(noteElement);
171
+ }
172
+
173
+ // Create transaction element if it doesn't exist
174
+ let transactionElement = root.getElementsByTagName('rsm:SupplyChainTradeTransaction')[0];
175
+ if (!transactionElement) {
176
+ transactionElement = doc.createElement('rsm:SupplyChainTradeTransaction');
177
+ root.appendChild(transactionElement);
178
+ }
179
+
180
+ // Add agreement section with seller and buyer
181
+ this.addAgreementSection(doc, transactionElement, invoice);
182
+
183
+ // Add delivery section
184
+ this.addDeliverySection(doc, transactionElement, invoice);
185
+
186
+ // Add settlement section with payment terms and totals
187
+ this.addSettlementSection(doc, transactionElement, invoice);
188
+
189
+ // Add line items
190
+ this.addLineItems(doc, transactionElement, invoice);
191
+ }
192
+
193
+ /**
194
+ * Adds agreement section with seller and buyer information
195
+ * @param doc XML document
196
+ * @param transactionElement Transaction element
197
+ * @param invoice Invoice data
198
+ */
199
+ private addAgreementSection(doc: Document, transactionElement: Element, invoice: TInvoice): void {
200
+ // Create agreement element
201
+ const agreementElement = doc.createElement('ram:ApplicableHeaderTradeAgreement');
202
+ transactionElement.appendChild(agreementElement);
203
+
204
+ // Add buyer reference if available
205
+ if (invoice.buyerReference) {
206
+ const buyerRefElement = doc.createElement('ram:BuyerReference');
207
+ buyerRefElement.textContent = invoice.buyerReference;
208
+ agreementElement.appendChild(buyerRefElement);
209
+ }
210
+
211
+ // Add seller
212
+ const sellerElement = doc.createElement('ram:SellerTradeParty');
213
+ this.addPartyInfo(doc, sellerElement, invoice.from);
214
+
215
+ // Add seller electronic address if available
216
+ if (invoice.electronicAddress && invoice.from.type === 'company') {
217
+ const contactElement = doc.createElement('ram:DefinedTradeContact');
218
+ const uriElement = doc.createElement('ram:URIID');
219
+ uriElement.setAttribute('schemeID', invoice.electronicAddress.scheme);
220
+ uriElement.textContent = invoice.electronicAddress.value;
221
+ contactElement.appendChild(uriElement);
222
+ sellerElement.appendChild(contactElement);
223
+ }
224
+
225
+ agreementElement.appendChild(sellerElement);
226
+
227
+ // Add buyer
228
+ const buyerElement = doc.createElement('ram:BuyerTradeParty');
229
+ this.addPartyInfo(doc, buyerElement, invoice.to);
230
+ agreementElement.appendChild(buyerElement);
231
+ }
232
+
233
+ /**
234
+ * Adds party information to an element
235
+ * @param doc XML document
236
+ * @param partyElement Party element
237
+ * @param party Party data
238
+ */
239
+ private addPartyInfo(doc: Document, partyElement: Element, party: any): void {
240
+ // Add name
241
+ const nameElement = doc.createElement('ram:Name');
242
+ nameElement.textContent = party.name;
243
+ partyElement.appendChild(nameElement);
244
+
245
+ // Add postal address
246
+ const addressElement = doc.createElement('ram:PostalTradeAddress');
247
+
248
+ // Add address line 1 (street)
249
+ if (party.address.streetName) {
250
+ const line1Element = doc.createElement('ram:LineOne');
251
+ line1Element.textContent = party.address.streetName;
252
+ addressElement.appendChild(line1Element);
253
+ }
254
+
255
+ // Add address line 2 (house number) if present
256
+ if (party.address.houseNumber && party.address.houseNumber !== '0') {
257
+ const line2Element = doc.createElement('ram:LineTwo');
258
+ line2Element.textContent = party.address.houseNumber;
259
+ addressElement.appendChild(line2Element);
260
+ }
261
+
262
+ // Add postal code
263
+ if (party.address.postalCode) {
264
+ const postalCodeElement = doc.createElement('ram:PostcodeCode');
265
+ postalCodeElement.textContent = party.address.postalCode;
266
+ addressElement.appendChild(postalCodeElement);
267
+ }
268
+
269
+ // Add city
270
+ if (party.address.city) {
271
+ const cityElement = doc.createElement('ram:CityName');
272
+ cityElement.textContent = party.address.city;
273
+ addressElement.appendChild(cityElement);
274
+ }
275
+
276
+ // Add country
277
+ if (party.address.country || party.address.countryCode) {
278
+ const countryElement = doc.createElement('ram:CountryID');
279
+ countryElement.textContent = party.address.countryCode || party.address.country;
280
+ addressElement.appendChild(countryElement);
281
+ }
282
+
283
+ partyElement.appendChild(addressElement);
284
+
285
+ // Add VAT ID if available
286
+ if (party.registrationDetails && party.registrationDetails.vatId) {
287
+ const taxRegistrationElement = doc.createElement('ram:SpecifiedTaxRegistration');
288
+ const taxIdElement = doc.createElement('ram:ID');
289
+ taxIdElement.setAttribute('schemeID', 'VA');
290
+ taxIdElement.textContent = party.registrationDetails.vatId;
291
+ taxRegistrationElement.appendChild(taxIdElement);
292
+ partyElement.appendChild(taxRegistrationElement);
293
+ }
294
+
295
+ // Add registration ID if available
296
+ if (party.registrationDetails && party.registrationDetails.registrationId) {
297
+ const regRegistrationElement = doc.createElement('ram:SpecifiedTaxRegistration');
298
+ const regIdElement = doc.createElement('ram:ID');
299
+ regIdElement.setAttribute('schemeID', 'FC');
300
+ regIdElement.textContent = party.registrationDetails.registrationId;
301
+ regRegistrationElement.appendChild(regIdElement);
302
+ partyElement.appendChild(regRegistrationElement);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Adds delivery section with delivery information
308
+ * @param doc XML document
309
+ * @param transactionElement Transaction element
310
+ * @param invoice Invoice data
311
+ */
312
+ private addDeliverySection(doc: Document, transactionElement: Element, invoice: TInvoice): void {
313
+ // Create delivery element
314
+ const deliveryElement = doc.createElement('ram:ApplicableHeaderTradeDelivery');
315
+ transactionElement.appendChild(deliveryElement);
316
+
317
+ // Add delivery date if available
318
+ if (invoice.deliveryDate) {
319
+ const deliveryDateElement = doc.createElement('ram:ActualDeliverySupplyChainEvent');
320
+ const occurrenceDateElement = doc.createElement('ram:OccurrenceDateTime');
321
+ const dateStringElement = doc.createElement('udt:DateTimeString');
322
+ dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
323
+ dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.deliveryDate);
324
+ occurrenceDateElement.appendChild(dateStringElement);
325
+ deliveryDateElement.appendChild(occurrenceDateElement);
326
+ deliveryElement.appendChild(deliveryDateElement);
327
+ }
328
+
329
+ // Add period of performance if available
330
+ if (invoice.periodOfPerformance) {
331
+ const periodElement = doc.createElement('ram:BillingSpecifiedPeriod');
332
+
333
+ // Start date
334
+ if (invoice.periodOfPerformance.from) {
335
+ const startDateElement = doc.createElement('ram:StartDateTime');
336
+ const startDateStringElement = doc.createElement('udt:DateTimeString');
337
+ startDateStringElement.setAttribute('format', '102'); // YYYYMMDD format
338
+ startDateStringElement.textContent = this.formatDateYYYYMMDD(invoice.periodOfPerformance.from);
339
+ startDateElement.appendChild(startDateStringElement);
340
+ periodElement.appendChild(startDateElement);
341
+ }
342
+
343
+ // End date
344
+ if (invoice.periodOfPerformance.to) {
345
+ const endDateElement = doc.createElement('ram:EndDateTime');
346
+ const endDateStringElement = doc.createElement('udt:DateTimeString');
347
+ endDateStringElement.setAttribute('format', '102'); // YYYYMMDD format
348
+ endDateStringElement.textContent = this.formatDateYYYYMMDD(invoice.periodOfPerformance.to);
349
+ endDateElement.appendChild(endDateStringElement);
350
+ periodElement.appendChild(endDateElement);
351
+ }
352
+
353
+ deliveryElement.appendChild(periodElement);
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Adds settlement section with payment terms and totals
359
+ * @param doc XML document
360
+ * @param transactionElement Transaction element
361
+ * @param invoice Invoice data
362
+ */
363
+ private addSettlementSection(doc: Document, transactionElement: Element, invoice: TInvoice): void {
364
+ // Create settlement element
365
+ const settlementElement = doc.createElement('ram:ApplicableHeaderTradeSettlement');
366
+ transactionElement.appendChild(settlementElement);
367
+
368
+ // Add currency
369
+ const currencyElement = doc.createElement('ram:InvoiceCurrencyCode');
370
+ currencyElement.textContent = invoice.currency;
371
+ settlementElement.appendChild(currencyElement);
372
+
373
+ // Add payment terms
374
+ const paymentTermsElement = doc.createElement('ram:SpecifiedTradePaymentTerms');
375
+
376
+ // Add payment instructions if available
377
+ if (invoice.paymentOptions) {
378
+ // Add payment instructions as description - this is generic enough to work with any payment type
379
+ const descriptionElement = doc.createElement('ram:Description');
380
+ descriptionElement.textContent = `Due in ${invoice.dueInDays} days. ${invoice.paymentOptions.description || ''}`;
381
+ paymentTermsElement.appendChild(descriptionElement);
382
+ }
383
+
384
+ // Add due date
385
+ const dueDateElement = doc.createElement('ram:DueDateDateTime');
386
+ const dateStringElement = doc.createElement('udt:DateTimeString');
387
+ dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
388
+
389
+ // Calculate due date
390
+ const dueDate = new Date(invoice.date);
391
+ dueDate.setDate(dueDate.getDate() + invoice.dueInDays);
392
+
393
+ dateStringElement.textContent = this.formatDateYYYYMMDD(dueDate.getTime());
394
+ dueDateElement.appendChild(dateStringElement);
395
+ paymentTermsElement.appendChild(dueDateElement);
396
+
397
+ settlementElement.appendChild(paymentTermsElement);
398
+
399
+ // Add payment means if available (using a generic approach)
400
+ if (invoice.paymentOptions) {
401
+ const paymentMeansElement = doc.createElement('ram:SpecifiedTradeSettlementPaymentMeans');
402
+
403
+ // Payment type code (58 for SEPA transfer as default)
404
+ const typeCodeElement = doc.createElement('ram:TypeCode');
405
+ typeCodeElement.textContent = '58';
406
+ paymentMeansElement.appendChild(typeCodeElement);
407
+
408
+ // Description (optional)
409
+ if (invoice.paymentOptions.description) {
410
+ const infoElement = doc.createElement('ram:Information');
411
+ infoElement.textContent = invoice.paymentOptions.description;
412
+ paymentMeansElement.appendChild(infoElement);
413
+ }
414
+
415
+ // If payment details are available in a standard format
416
+ if (invoice.paymentOptions.sepaConnection.iban) {
417
+ // Payee account
418
+ const payeeAccountElement = doc.createElement('ram:PayeePartyCreditorFinancialAccount');
419
+ const ibanElement = doc.createElement('ram:IBANID');
420
+ ibanElement.textContent = invoice.paymentOptions.sepaConnection.iban;
421
+ payeeAccountElement.appendChild(ibanElement);
422
+ paymentMeansElement.appendChild(payeeAccountElement);
423
+
424
+ // Payee financial institution if BIC available
425
+ if (invoice.paymentOptions.sepaConnection.bic) {
426
+ const institutionElement = doc.createElement('ram:PayeeSpecifiedCreditorFinancialInstitution');
427
+ const bicElement = doc.createElement('ram:BICID');
428
+ bicElement.textContent = invoice.paymentOptions.sepaConnection.bic;
429
+ institutionElement.appendChild(bicElement);
430
+ paymentMeansElement.appendChild(institutionElement);
431
+ }
432
+ }
433
+
434
+ settlementElement.appendChild(paymentMeansElement);
435
+ }
436
+
437
+ // Add tax details
438
+ this.addTaxDetails(doc, settlementElement, invoice);
439
+
440
+ // Add totals
441
+ this.addMonetarySummation(doc, settlementElement, invoice);
442
+ }
443
+
444
+ /**
445
+ * Adds tax details to the settlement section
446
+ * @param doc XML document
447
+ * @param settlementElement Settlement element
448
+ * @param invoice Invoice data
449
+ */
450
+ private addTaxDetails(doc: Document, settlementElement: Element, invoice: TInvoice): void {
451
+ // Calculate tax categories and totals
452
+ const taxCategories = new Map<number, number>(); // Map of VAT rate to net amount
453
+
454
+ // Calculate from items
455
+ if (invoice.items) {
456
+ for (const item of invoice.items) {
457
+ const itemNetAmount = item.unitNetPrice * item.unitQuantity;
458
+ const vatRate = item.vatPercentage;
459
+
460
+ const currentAmount = taxCategories.get(vatRate) || 0;
461
+ taxCategories.set(vatRate, currentAmount + itemNetAmount);
462
+ }
463
+ }
464
+
465
+ // Add each tax category
466
+ for (const [rate, baseAmount] of taxCategories.entries()) {
467
+ const taxElement = doc.createElement('ram:ApplicableTradeTax');
468
+
469
+ // Calculate tax amount
470
+ const taxAmount = baseAmount * (rate / 100);
471
+
472
+ // Add calculated amount
473
+ const calculatedAmountElement = doc.createElement('ram:CalculatedAmount');
474
+ calculatedAmountElement.textContent = taxAmount.toFixed(2);
475
+ taxElement.appendChild(calculatedAmountElement);
476
+
477
+ // Add type code (VAT)
478
+ const typeCodeElement = doc.createElement('ram:TypeCode');
479
+ typeCodeElement.textContent = 'VAT';
480
+ taxElement.appendChild(typeCodeElement);
481
+
482
+ // Add basis amount
483
+ const basisAmountElement = doc.createElement('ram:BasisAmount');
484
+ basisAmountElement.textContent = baseAmount.toFixed(2);
485
+ taxElement.appendChild(basisAmountElement);
486
+
487
+ // Add category code
488
+ const categoryCodeElement = doc.createElement('ram:CategoryCode');
489
+ categoryCodeElement.textContent = invoice.reverseCharge ? 'AE' : 'S';
490
+ taxElement.appendChild(categoryCodeElement);
491
+
492
+ // Add rate
493
+ const rateElement = doc.createElement('ram:RateApplicablePercent');
494
+ rateElement.textContent = rate.toString();
495
+ taxElement.appendChild(rateElement);
496
+
497
+ settlementElement.appendChild(taxElement);
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Adds monetary summation to the settlement section
503
+ * @param doc XML document
504
+ * @param settlementElement Settlement element
505
+ * @param invoice Invoice data
506
+ */
507
+ private addMonetarySummation(doc: Document, settlementElement: Element, invoice: TInvoice): void {
508
+ const monetarySummationElement = doc.createElement('ram:SpecifiedTradeSettlementHeaderMonetarySummation');
509
+
510
+ // Calculate totals
511
+ let totalNetAmount = 0;
512
+ let totalTaxAmount = 0;
513
+
514
+ // Calculate from items
515
+ if (invoice.items) {
516
+ for (const item of invoice.items) {
517
+ const itemNetAmount = item.unitNetPrice * item.unitQuantity;
518
+ const itemTaxAmount = itemNetAmount * (item.vatPercentage / 100);
519
+
520
+ totalNetAmount += itemNetAmount;
521
+ totalTaxAmount += itemTaxAmount;
522
+ }
523
+ }
524
+
525
+ const totalGrossAmount = totalNetAmount + totalTaxAmount;
526
+
527
+ // Add line total amount
528
+ const lineTotalElement = doc.createElement('ram:LineTotalAmount');
529
+ lineTotalElement.textContent = totalNetAmount.toFixed(2);
530
+ monetarySummationElement.appendChild(lineTotalElement);
531
+
532
+ // Add tax total amount
533
+ const taxTotalElement = doc.createElement('ram:TaxTotalAmount');
534
+ taxTotalElement.textContent = totalTaxAmount.toFixed(2);
535
+ taxTotalElement.setAttribute('currencyID', invoice.currency);
536
+ monetarySummationElement.appendChild(taxTotalElement);
537
+
538
+ // Add grand total amount
539
+ const grandTotalElement = doc.createElement('ram:GrandTotalAmount');
540
+ grandTotalElement.textContent = totalGrossAmount.toFixed(2);
541
+ monetarySummationElement.appendChild(grandTotalElement);
542
+
543
+ // Add due payable amount
544
+ const duePayableElement = doc.createElement('ram:DuePayableAmount');
545
+ duePayableElement.textContent = totalGrossAmount.toFixed(2);
546
+ monetarySummationElement.appendChild(duePayableElement);
547
+
548
+ settlementElement.appendChild(monetarySummationElement);
549
+ }
550
+
551
+ /**
552
+ * Adds line items to the XML document
553
+ * @param doc XML document
554
+ * @param transactionElement Transaction element
555
+ * @param invoice Invoice data
556
+ */
557
+ private addLineItems(doc: Document, transactionElement: Element, invoice: TInvoice): void {
558
+ // Add each line item
559
+ if (invoice.items) {
560
+ for (const item of invoice.items) {
561
+ // Create line item element
562
+ const lineItemElement = doc.createElement('ram:IncludedSupplyChainTradeLineItem');
563
+
564
+ // Add line ID
565
+ const lineIdElement = doc.createElement('ram:AssociatedDocumentLineDocument');
566
+ const lineIdValueElement = doc.createElement('ram:LineID');
567
+ lineIdValueElement.textContent = item.position.toString();
568
+ lineIdElement.appendChild(lineIdValueElement);
569
+ lineItemElement.appendChild(lineIdElement);
570
+
571
+ // Add product information
572
+ const productElement = doc.createElement('ram:SpecifiedTradeProduct');
573
+
574
+ // Add name
575
+ const nameElement = doc.createElement('ram:Name');
576
+ nameElement.textContent = item.name;
577
+ productElement.appendChild(nameElement);
578
+
579
+ // Add article number if available
580
+ if (item.articleNumber) {
581
+ const articleNumberElement = doc.createElement('ram:SellerAssignedID');
582
+ articleNumberElement.textContent = item.articleNumber;
583
+ productElement.appendChild(articleNumberElement);
584
+ }
585
+
586
+ lineItemElement.appendChild(productElement);
587
+
588
+ // Add agreement information (price)
589
+ const agreementElement = doc.createElement('ram:SpecifiedLineTradeAgreement');
590
+ const priceElement = doc.createElement('ram:NetPriceProductTradePrice');
591
+ const chargeAmountElement = doc.createElement('ram:ChargeAmount');
592
+ chargeAmountElement.textContent = item.unitNetPrice.toFixed(2);
593
+ priceElement.appendChild(chargeAmountElement);
594
+ agreementElement.appendChild(priceElement);
595
+ lineItemElement.appendChild(agreementElement);
596
+
597
+ // Add delivery information (quantity)
598
+ const deliveryElement = doc.createElement('ram:SpecifiedLineTradeDelivery');
599
+ const quantityElement = doc.createElement('ram:BilledQuantity');
600
+ quantityElement.textContent = item.unitQuantity.toString();
601
+ quantityElement.setAttribute('unitCode', item.unitType);
602
+ deliveryElement.appendChild(quantityElement);
603
+ lineItemElement.appendChild(deliveryElement);
604
+
605
+ // Add settlement information (tax)
606
+ const settlementElement = doc.createElement('ram:SpecifiedLineTradeSettlement');
607
+
608
+ // Add tax information
609
+ const taxElement = doc.createElement('ram:ApplicableTradeTax');
610
+
611
+ // Add tax type code
612
+ const taxTypeCodeElement = doc.createElement('ram:TypeCode');
613
+ taxTypeCodeElement.textContent = 'VAT';
614
+ taxElement.appendChild(taxTypeCodeElement);
615
+
616
+ // Add tax category code
617
+ const taxCategoryCodeElement = doc.createElement('ram:CategoryCode');
618
+ taxCategoryCodeElement.textContent = invoice.reverseCharge ? 'AE' : 'S';
619
+ taxElement.appendChild(taxCategoryCodeElement);
620
+
621
+ // Add tax rate
622
+ const taxRateElement = doc.createElement('ram:RateApplicablePercent');
623
+ taxRateElement.textContent = item.vatPercentage.toString();
624
+ taxElement.appendChild(taxRateElement);
625
+
626
+ settlementElement.appendChild(taxElement);
627
+
628
+ // Add monetary summation
629
+ const monetarySummationElement = doc.createElement('ram:SpecifiedLineTradeSettlementMonetarySummation');
630
+
631
+ // Calculate item total
632
+ const itemNetAmount = item.unitNetPrice * item.unitQuantity;
633
+
634
+ // Add line total amount
635
+ const lineTotalElement = doc.createElement('ram:LineTotalAmount');
636
+ lineTotalElement.textContent = itemNetAmount.toFixed(2);
637
+ monetarySummationElement.appendChild(lineTotalElement);
638
+
639
+ settlementElement.appendChild(monetarySummationElement);
640
+
641
+ lineItemElement.appendChild(settlementElement);
642
+
643
+ // Add line item to transaction
644
+ transactionElement.appendChild(lineItemElement);
645
+ }
646
+ }
647
+ }
648
+
649
+ /**
650
+ * Formats a date as YYYYMMDD
651
+ * @param timestamp Timestamp to format
652
+ * @returns Formatted date string
653
+ */
654
+ private formatDateYYYYMMDD(timestamp: number): string {
655
+ const date = new Date(timestamp);
656
+ const year = date.getFullYear();
657
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
658
+ const day = date.getDate().toString().padStart(2, '0');
659
+ return `${year}${month}${day}`;
660
+ }
661
+ }
@@ -0,0 +1,18 @@
1
+ import { CIIProfile, CII_PROFILE_IDS } from '../cii.types.js';
2
+
3
+ /**
4
+ * ZUGFeRD specific constants and types
5
+ */
6
+
7
+ // ZUGFeRD profile IDs
8
+ export const ZUGFERD_PROFILE_IDS = {
9
+ BASIC: CII_PROFILE_IDS.ZUGFERD_BASIC,
10
+ COMFORT: CII_PROFILE_IDS.ZUGFERD_COMFORT,
11
+ EXTENDED: CII_PROFILE_IDS.ZUGFERD_EXTENDED
12
+ };
13
+
14
+ // ZUGFeRD PDF attachment filename
15
+ export const ZUGFERD_ATTACHMENT_FILENAME = 'zugferd-invoice.xml';
16
+
17
+ // ZUGFeRD PDF attachment description
18
+ export const ZUGFERD_ATTACHMENT_DESCRIPTION = 'ZUGFeRD XML Invoice';