@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.
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.decoder.d.ts +40 -0
- package/dist_ts/classes.decoder.js +320 -0
- package/dist_ts/classes.encoder.d.ts +51 -0
- package/dist_ts/classes.encoder.js +293 -0
- package/dist_ts/classes.xinvoice.d.ts +136 -0
- package/dist_ts/classes.xinvoice.js +352 -0
- package/dist_ts/einvoice.d.ts +217 -0
- package/dist_ts/einvoice.js +505 -0
- package/dist_ts/errors.d.ts +121 -0
- package/dist_ts/errors.js +241 -0
- package/dist_ts/formats/base/base.decoder.d.ts +32 -0
- package/dist_ts/formats/base/base.decoder.js +52 -0
- package/dist_ts/formats/base/base.encoder.d.ts +13 -0
- package/dist_ts/formats/base/base.encoder.js +7 -0
- package/dist_ts/formats/base/base.validator.d.ts +44 -0
- package/dist_ts/formats/base/base.validator.js +39 -0
- package/dist_ts/formats/base.decoder.d.ts +19 -0
- package/dist_ts/formats/base.decoder.js +130 -0
- package/dist_ts/formats/base.validator.d.ts +44 -0
- package/dist_ts/formats/base.validator.js +39 -0
- package/dist_ts/formats/cii/cii.decoder.d.ts +61 -0
- package/dist_ts/formats/cii/cii.decoder.js +111 -0
- package/dist_ts/formats/cii/cii.encoder.d.ts +43 -0
- package/dist_ts/formats/cii/cii.encoder.js +48 -0
- package/dist_ts/formats/cii/cii.types.d.ts +31 -0
- package/dist_ts/formats/cii/cii.types.js +40 -0
- package/dist_ts/formats/cii/cii.validator.d.ts +56 -0
- package/dist_ts/formats/cii/cii.validator.js +146 -0
- package/dist_ts/formats/cii/facturx/facturx.decoder.d.ts +43 -0
- package/dist_ts/formats/cii/facturx/facturx.decoder.js +207 -0
- package/dist_ts/formats/cii/facturx/facturx.encoder.d.ts +82 -0
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +400 -0
- package/dist_ts/formats/cii/facturx/facturx.types.d.ts +10 -0
- package/dist_ts/formats/cii/facturx/facturx.types.js +15 -0
- package/dist_ts/formats/cii/facturx/facturx.validator.d.ts +32 -0
- package/dist_ts/formats/cii/facturx/facturx.validator.js +138 -0
- package/dist_ts/formats/cii/zugferd/zugferd.decoder.d.ts +43 -0
- package/dist_ts/formats/cii/zugferd/zugferd.decoder.js +218 -0
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.d.ts +97 -0
- package/dist_ts/formats/cii/zugferd/zugferd.encoder.js +550 -0
- package/dist_ts/formats/cii/zugferd/zugferd.types.d.ts +10 -0
- package/dist_ts/formats/cii/zugferd/zugferd.types.js +15 -0
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.d.ts +49 -0
- package/dist_ts/formats/cii/zugferd/zugferd.v1.decoder.js +229 -0
- package/dist_ts/formats/cii/zugferd/zugferd.validator.d.ts +16 -0
- package/dist_ts/formats/cii/zugferd/zugferd.validator.js +29 -0
- package/dist_ts/formats/decoder.factory.d.ts +15 -0
- package/dist_ts/formats/decoder.factory.js +46 -0
- package/dist_ts/formats/factories/decoder.factory.d.ts +13 -0
- package/dist_ts/formats/factories/decoder.factory.js +56 -0
- package/dist_ts/formats/factories/encoder.factory.d.ts +14 -0
- package/dist_ts/formats/factories/encoder.factory.js +40 -0
- package/dist_ts/formats/factories/validator.factory.d.ts +12 -0
- package/dist_ts/formats/factories/validator.factory.js +110 -0
- package/dist_ts/formats/factorx.decoder.d.ts +18 -0
- package/dist_ts/formats/factorx.decoder.js +178 -0
- package/dist_ts/formats/factorx.encoder.d.ts +51 -0
- package/dist_ts/formats/factorx.encoder.js +293 -0
- package/dist_ts/formats/facturx.decoder.d.ts +18 -0
- package/dist_ts/formats/facturx.decoder.js +210 -0
- package/dist_ts/formats/facturx.encoder.d.ts +51 -0
- package/dist_ts/formats/facturx.encoder.js +293 -0
- package/dist_ts/formats/facturx.validator.d.ts +67 -0
- package/dist_ts/formats/facturx.validator.js +266 -0
- package/dist_ts/formats/pdf/extractors/associated.extractor.d.ts +14 -0
- package/dist_ts/formats/pdf/extractors/associated.extractor.js +68 -0
- package/dist_ts/formats/pdf/extractors/base.extractor.d.ts +92 -0
- package/dist_ts/formats/pdf/extractors/base.extractor.js +319 -0
- package/dist_ts/formats/pdf/extractors/index.d.ts +4 -0
- package/dist_ts/formats/pdf/extractors/index.js +5 -0
- package/dist_ts/formats/pdf/extractors/standard.extractor.d.ts +13 -0
- package/dist_ts/formats/pdf/extractors/standard.extractor.js +74 -0
- package/dist_ts/formats/pdf/extractors/text.extractor.d.ts +45 -0
- package/dist_ts/formats/pdf/extractors/text.extractor.js +151 -0
- package/dist_ts/formats/pdf/pdf.embedder.d.ts +69 -0
- package/dist_ts/formats/pdf/pdf.embedder.js +183 -0
- package/dist_ts/formats/pdf/pdf.extractor.d.ts +49 -0
- package/dist_ts/formats/pdf/pdf.extractor.js +98 -0
- package/dist_ts/formats/pdf/robust-pdf.extractor.d.ts +40 -0
- package/dist_ts/formats/pdf/robust-pdf.extractor.js +324 -0
- package/dist_ts/formats/ubl/en16931.ubl.validator.d.ts +18 -0
- package/dist_ts/formats/ubl/en16931.ubl.validator.js +169 -0
- package/dist_ts/formats/ubl/generic/ubl.encoder.d.ts +151 -0
- package/dist_ts/formats/ubl/generic/ubl.encoder.js +892 -0
- package/dist_ts/formats/ubl/ubl.decoder.d.ts +61 -0
- package/dist_ts/formats/ubl/ubl.decoder.js +95 -0
- package/dist_ts/formats/ubl/ubl.encoder.d.ts +38 -0
- package/dist_ts/formats/ubl/ubl.encoder.js +46 -0
- package/dist_ts/formats/ubl/ubl.types.d.ts +16 -0
- package/dist_ts/formats/ubl/ubl.types.js +21 -0
- package/dist_ts/formats/ubl/ubl.validator.d.ts +50 -0
- package/dist_ts/formats/ubl/ubl.validator.js +112 -0
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.d.ts +34 -0
- package/dist_ts/formats/ubl/xrechnung/xrechnung.decoder.js +424 -0
- package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.d.ts +75 -0
- package/dist_ts/formats/ubl/xrechnung/xrechnung.encoder.js +555 -0
- package/dist_ts/formats/ubl/xrechnung.validator.d.ts +15 -0
- package/dist_ts/formats/ubl/xrechnung.validator.js +107 -0
- package/dist_ts/formats/ubl.validator.d.ts +82 -0
- package/dist_ts/formats/ubl.validator.js +306 -0
- package/dist_ts/formats/utils/format.detector.d.ts +62 -0
- package/dist_ts/formats/utils/format.detector.js +249 -0
- package/dist_ts/formats/validation/en16931.validator.d.ts +23 -0
- package/dist_ts/formats/validation/en16931.validator.js +119 -0
- package/dist_ts/formats/validator.factory.d.ts +18 -0
- package/dist_ts/formats/validator.factory.js +78 -0
- package/dist_ts/formats/xinvoice.decoder.d.ts +28 -0
- package/dist_ts/formats/xinvoice.decoder.js +332 -0
- package/dist_ts/formats/xinvoice.encoder.d.ts +19 -0
- package/dist_ts/formats/xinvoice.encoder.js +282 -0
- package/dist_ts/index.d.ts +51 -0
- package/dist_ts/index.js +90 -0
- package/dist_ts/interfaces/common.d.ts +79 -0
- package/dist_ts/interfaces/common.js +24 -0
- package/dist_ts/interfaces.d.ts +87 -0
- package/dist_ts/interfaces.js +23 -0
- package/dist_ts/plugins.d.ts +14 -0
- package/dist_ts/plugins.js +31 -0
- package/npmextra.json +35 -0
- package/package.json +71 -0
- package/readme.hints.md +1107 -0
- package/readme.howtofixtests.md +38 -0
- package/readme.literature.md +1 -0
- package/readme.md +998 -0
- package/readme.plan.md +481 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/einvoice.ts +603 -0
- package/ts/errors.ts +341 -0
- package/ts/formats/base/base.decoder.ts +68 -0
- package/ts/formats/base/base.encoder.ts +14 -0
- package/ts/formats/base/base.validator.ts +64 -0
- package/ts/formats/cii/cii.decoder.ts +139 -0
- package/ts/formats/cii/cii.encoder.ts +64 -0
- package/ts/formats/cii/cii.types.ts +44 -0
- package/ts/formats/cii/cii.validator.ts +171 -0
- package/ts/formats/cii/facturx/facturx.decoder.ts +243 -0
- package/ts/formats/cii/facturx/facturx.encoder.ts +483 -0
- package/ts/formats/cii/facturx/facturx.types.ts +18 -0
- package/ts/formats/cii/facturx/facturx.validator.ts +180 -0
- package/ts/formats/cii/zugferd/zugferd.decoder.ts +256 -0
- package/ts/formats/cii/zugferd/zugferd.encoder.ts +661 -0
- package/ts/formats/cii/zugferd/zugferd.types.ts +18 -0
- package/ts/formats/cii/zugferd/zugferd.v1.decoder.ts +267 -0
- package/ts/formats/cii/zugferd/zugferd.validator.ts +31 -0
- package/ts/formats/factories/decoder.factory.ts +62 -0
- package/ts/formats/factories/encoder.factory.ts +47 -0
- package/ts/formats/factories/validator.factory.ts +134 -0
- package/ts/formats/pdf/extractors/associated.extractor.ts +78 -0
- package/ts/formats/pdf/extractors/base.extractor.ts +355 -0
- package/ts/formats/pdf/extractors/index.ts +4 -0
- package/ts/formats/pdf/extractors/standard.extractor.ts +86 -0
- package/ts/formats/pdf/extractors/text.extractor.ts +162 -0
- package/ts/formats/pdf/pdf.embedder.ts +242 -0
- package/ts/formats/pdf/pdf.extractor.ts +141 -0
- package/ts/formats/ubl/en16931.ubl.validator.ts +216 -0
- package/ts/formats/ubl/generic/ubl.encoder.ts +1041 -0
- package/ts/formats/ubl/ubl.decoder.ts +121 -0
- package/ts/formats/ubl/ubl.encoder.ts +63 -0
- package/ts/formats/ubl/ubl.types.ts +22 -0
- package/ts/formats/ubl/ubl.validator.ts +133 -0
- package/ts/formats/ubl/xrechnung/xrechnung.decoder.ts +471 -0
- package/ts/formats/ubl/xrechnung/xrechnung.encoder.ts +619 -0
- package/ts/formats/ubl/xrechnung.validator.ts +185 -0
- package/ts/formats/utils/format.detector.ts +306 -0
- package/ts/formats/validation/en16931.validator.ts +135 -0
- package/ts/index.ts +164 -0
- package/ts/interfaces/common.ts +90 -0
- package/ts/interfaces.ts +98 -0
- package/ts/plugins.ts +61 -0
package/ts/errors.ts
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all EInvoice-related errors
|
|
3
|
+
*/
|
|
4
|
+
export class EInvoiceError extends Error {
|
|
5
|
+
public code: string;
|
|
6
|
+
public details?: any;
|
|
7
|
+
public cause?: Error;
|
|
8
|
+
|
|
9
|
+
constructor(message: string, code: string, details?: any, cause?: Error) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'EInvoiceError';
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.details = details;
|
|
14
|
+
this.cause = cause;
|
|
15
|
+
|
|
16
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
17
|
+
if (Error.captureStackTrace) {
|
|
18
|
+
Error.captureStackTrace(this, this.constructor);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns a detailed error message including cause if available
|
|
24
|
+
*/
|
|
25
|
+
public getDetailedMessage(): string {
|
|
26
|
+
let message = `${this.name} [${this.code}]: ${this.message}`;
|
|
27
|
+
if (this.details) {
|
|
28
|
+
message += `\nDetails: ${JSON.stringify(this.details, null, 2)}`;
|
|
29
|
+
}
|
|
30
|
+
if (this.cause) {
|
|
31
|
+
message += `\nCaused by: ${this.cause.message}`;
|
|
32
|
+
}
|
|
33
|
+
return message;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Error thrown when XML parsing fails
|
|
39
|
+
*/
|
|
40
|
+
export class EInvoiceParsingError extends EInvoiceError {
|
|
41
|
+
public line?: number;
|
|
42
|
+
public column?: number;
|
|
43
|
+
public xmlSnippet?: string;
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
message: string,
|
|
47
|
+
details?: {
|
|
48
|
+
line?: number;
|
|
49
|
+
column?: number;
|
|
50
|
+
xmlSnippet?: string;
|
|
51
|
+
format?: string;
|
|
52
|
+
},
|
|
53
|
+
cause?: Error
|
|
54
|
+
) {
|
|
55
|
+
super(message, 'PARSE_ERROR', details, cause);
|
|
56
|
+
this.name = 'EInvoiceParsingError';
|
|
57
|
+
this.line = details?.line;
|
|
58
|
+
this.column = details?.column;
|
|
59
|
+
this.xmlSnippet = details?.xmlSnippet;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns a user-friendly error message with location information
|
|
64
|
+
*/
|
|
65
|
+
public getLocationMessage(): string {
|
|
66
|
+
let message = this.message;
|
|
67
|
+
if (this.line !== undefined && this.column !== undefined) {
|
|
68
|
+
message += ` at line ${this.line}, column ${this.column}`;
|
|
69
|
+
}
|
|
70
|
+
if (this.xmlSnippet) {
|
|
71
|
+
message += `\nNear: ${this.xmlSnippet}`;
|
|
72
|
+
}
|
|
73
|
+
return message;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Error thrown when validation fails
|
|
79
|
+
*/
|
|
80
|
+
export class EInvoiceValidationError extends EInvoiceError {
|
|
81
|
+
public validationErrors: Array<{
|
|
82
|
+
code: string;
|
|
83
|
+
message: string;
|
|
84
|
+
location?: string;
|
|
85
|
+
severity?: 'error' | 'warning';
|
|
86
|
+
}>;
|
|
87
|
+
|
|
88
|
+
constructor(
|
|
89
|
+
message: string,
|
|
90
|
+
validationErrors: Array<{
|
|
91
|
+
code: string;
|
|
92
|
+
message: string;
|
|
93
|
+
location?: string;
|
|
94
|
+
severity?: 'error' | 'warning';
|
|
95
|
+
}>,
|
|
96
|
+
details?: any
|
|
97
|
+
) {
|
|
98
|
+
super(message, 'VALIDATION_ERROR', details);
|
|
99
|
+
this.name = 'EInvoiceValidationError';
|
|
100
|
+
this.validationErrors = validationErrors;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns a formatted validation report
|
|
105
|
+
*/
|
|
106
|
+
public getValidationReport(): string {
|
|
107
|
+
let report = `${this.message}\n\nValidation errors:\n`;
|
|
108
|
+
for (const error of this.validationErrors) {
|
|
109
|
+
report += `- [${error.code}] ${error.message}`;
|
|
110
|
+
if (error.location) {
|
|
111
|
+
report += ` (at ${error.location})`;
|
|
112
|
+
}
|
|
113
|
+
if (error.severity) {
|
|
114
|
+
report += ` [${error.severity.toUpperCase()}]`;
|
|
115
|
+
}
|
|
116
|
+
report += '\n';
|
|
117
|
+
}
|
|
118
|
+
return report;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Gets validation errors by severity
|
|
123
|
+
*/
|
|
124
|
+
public getErrorsBySeverity(severity: 'error' | 'warning'): typeof this.validationErrors {
|
|
125
|
+
return this.validationErrors.filter(e => (e.severity || 'error') === severity);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Error thrown during PDF operations
|
|
131
|
+
*/
|
|
132
|
+
export class EInvoicePDFError extends EInvoiceError {
|
|
133
|
+
public operation: 'extract' | 'embed' | 'create' | 'validate';
|
|
134
|
+
public pdfInfo?: {
|
|
135
|
+
filename?: string;
|
|
136
|
+
size?: number;
|
|
137
|
+
pageCount?: number;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
constructor(
|
|
141
|
+
message: string,
|
|
142
|
+
operation: 'extract' | 'embed' | 'create' | 'validate',
|
|
143
|
+
details?: any,
|
|
144
|
+
cause?: Error
|
|
145
|
+
) {
|
|
146
|
+
super(message, `PDF_${operation.toUpperCase()}_ERROR`, details, cause);
|
|
147
|
+
this.name = 'EInvoicePDFError';
|
|
148
|
+
this.operation = operation;
|
|
149
|
+
this.pdfInfo = details?.pdfInfo;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Returns recovery suggestions based on the operation
|
|
154
|
+
*/
|
|
155
|
+
public getRecoverySuggestions(): string[] {
|
|
156
|
+
const suggestions: string[] = [];
|
|
157
|
+
|
|
158
|
+
switch (this.operation) {
|
|
159
|
+
case 'extract':
|
|
160
|
+
suggestions.push(
|
|
161
|
+
'Ensure the PDF contains embedded XML data',
|
|
162
|
+
'Check if the PDF is a valid PDF/A-3 document',
|
|
163
|
+
'Try using a different extraction method',
|
|
164
|
+
'Verify the PDF is not corrupted'
|
|
165
|
+
);
|
|
166
|
+
break;
|
|
167
|
+
case 'embed':
|
|
168
|
+
suggestions.push(
|
|
169
|
+
'Ensure the source PDF is valid',
|
|
170
|
+
'Check that the XML data is well-formed',
|
|
171
|
+
'Verify sufficient memory is available',
|
|
172
|
+
'Try with a smaller XML payload'
|
|
173
|
+
);
|
|
174
|
+
break;
|
|
175
|
+
case 'create':
|
|
176
|
+
suggestions.push(
|
|
177
|
+
'Verify all required invoice data is provided',
|
|
178
|
+
'Check that the template PDF exists',
|
|
179
|
+
'Ensure write permissions for output directory'
|
|
180
|
+
);
|
|
181
|
+
break;
|
|
182
|
+
case 'validate':
|
|
183
|
+
suggestions.push(
|
|
184
|
+
'Check PDF/A-3 compliance',
|
|
185
|
+
'Verify XML attachment structure',
|
|
186
|
+
'Ensure proper PDF metadata'
|
|
187
|
+
);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return suggestions;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Error thrown for format-specific issues
|
|
197
|
+
*/
|
|
198
|
+
export class EInvoiceFormatError extends EInvoiceError {
|
|
199
|
+
public sourceFormat?: string;
|
|
200
|
+
public targetFormat?: string;
|
|
201
|
+
public unsupportedFeatures?: string[];
|
|
202
|
+
|
|
203
|
+
constructor(
|
|
204
|
+
message: string,
|
|
205
|
+
details: {
|
|
206
|
+
sourceFormat?: string;
|
|
207
|
+
targetFormat?: string;
|
|
208
|
+
unsupportedFeatures?: string[];
|
|
209
|
+
conversionPath?: string;
|
|
210
|
+
},
|
|
211
|
+
cause?: Error
|
|
212
|
+
) {
|
|
213
|
+
super(message, 'FORMAT_ERROR', details, cause);
|
|
214
|
+
this.name = 'EInvoiceFormatError';
|
|
215
|
+
this.sourceFormat = details.sourceFormat;
|
|
216
|
+
this.targetFormat = details.targetFormat;
|
|
217
|
+
this.unsupportedFeatures = details.unsupportedFeatures;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Returns a compatibility report
|
|
222
|
+
*/
|
|
223
|
+
public getCompatibilityReport(): string {
|
|
224
|
+
let report = this.message;
|
|
225
|
+
if (this.sourceFormat && this.targetFormat) {
|
|
226
|
+
report += `\n\nConversion: ${this.sourceFormat} → ${this.targetFormat}`;
|
|
227
|
+
}
|
|
228
|
+
if (this.unsupportedFeatures && this.unsupportedFeatures.length > 0) {
|
|
229
|
+
report += '\n\nUnsupported features:';
|
|
230
|
+
for (const feature of this.unsupportedFeatures) {
|
|
231
|
+
report += `\n- ${feature}`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return report;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Error recovery helper class
|
|
240
|
+
*/
|
|
241
|
+
export class ErrorRecovery {
|
|
242
|
+
/**
|
|
243
|
+
* Attempts to recover from a parsing error by cleaning the XML
|
|
244
|
+
*/
|
|
245
|
+
public static async attemptXMLRecovery(
|
|
246
|
+
xmlString: string,
|
|
247
|
+
error: EInvoiceParsingError
|
|
248
|
+
): Promise<{ success: boolean; cleanedXml?: string; message: string }> {
|
|
249
|
+
try {
|
|
250
|
+
let cleanedXml = xmlString;
|
|
251
|
+
|
|
252
|
+
// Remove BOM if present
|
|
253
|
+
if (cleanedXml.charCodeAt(0) === 0xFEFF) {
|
|
254
|
+
cleanedXml = cleanedXml.slice(1);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Fix common encoding issues
|
|
258
|
+
cleanedXml = cleanedXml
|
|
259
|
+
.replace(/&(?!(amp|lt|gt|apos|quot);)/g, '&')
|
|
260
|
+
.replace(/</g, (match, offset) => {
|
|
261
|
+
// Don't replace if it's part of <![CDATA[
|
|
262
|
+
const before = cleanedXml.substring(Math.max(0, offset - 10), offset);
|
|
263
|
+
if (before.includes('CDATA[')) return match;
|
|
264
|
+
return '<';
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Try to fix unclosed tags if we have location info
|
|
268
|
+
if (error.line && error.xmlSnippet) {
|
|
269
|
+
// This is a simplified approach - real implementation would be more sophisticated
|
|
270
|
+
const lines = cleanedXml.split('\n');
|
|
271
|
+
if (lines[error.line - 1]) {
|
|
272
|
+
// Attempt basic fixes based on the error
|
|
273
|
+
// This is just a placeholder for more sophisticated recovery
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
success: true,
|
|
279
|
+
cleanedXml,
|
|
280
|
+
message: 'Applied basic XML cleaning and encoding fixes'
|
|
281
|
+
};
|
|
282
|
+
} catch (recoveryError) {
|
|
283
|
+
return {
|
|
284
|
+
success: false,
|
|
285
|
+
message: `Recovery failed: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Provides partial data extraction on validation failure
|
|
292
|
+
*/
|
|
293
|
+
public static extractPartialData(
|
|
294
|
+
xmlString: string,
|
|
295
|
+
format: string
|
|
296
|
+
): { success: boolean; partialData?: any; message: string } {
|
|
297
|
+
try {
|
|
298
|
+
// This would implement format-specific partial extraction
|
|
299
|
+
// For now, returning a placeholder
|
|
300
|
+
return {
|
|
301
|
+
success: false,
|
|
302
|
+
message: 'Partial data extraction not yet implemented'
|
|
303
|
+
};
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return {
|
|
306
|
+
success: false,
|
|
307
|
+
message: `Partial extraction failed: ${error instanceof Error ? error.message : String(error)}`
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Error context builder for detailed error information
|
|
315
|
+
*/
|
|
316
|
+
export class ErrorContext {
|
|
317
|
+
private context: Map<string, any> = new Map();
|
|
318
|
+
|
|
319
|
+
public add(key: string, value: any): this {
|
|
320
|
+
this.context.set(key, value);
|
|
321
|
+
return this;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
public addTimestamp(): this {
|
|
325
|
+
this.context.set('timestamp', new Date().toISOString());
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
public addEnvironment(): this {
|
|
330
|
+
this.context.set('environment', {
|
|
331
|
+
nodeVersion: process.version,
|
|
332
|
+
platform: process.platform,
|
|
333
|
+
arch: process.arch
|
|
334
|
+
});
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
public build(): Record<string, any> {
|
|
339
|
+
return Object.fromEntries(this.context);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { TInvoice } from '../../interfaces/common.js';
|
|
2
|
+
import { ValidationLevel } from '../../interfaces/common.js';
|
|
3
|
+
import type { ValidationResult } from '../../interfaces/common.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base decoder class that defines common decoding functionality
|
|
7
|
+
* for all invoice format decoders
|
|
8
|
+
*/
|
|
9
|
+
export abstract class BaseDecoder {
|
|
10
|
+
protected xml: string;
|
|
11
|
+
protected skipValidation: boolean;
|
|
12
|
+
|
|
13
|
+
constructor(xml: string, skipValidation: boolean = false) {
|
|
14
|
+
this.xml = xml;
|
|
15
|
+
this.skipValidation = skipValidation;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Decodes XML into a TInvoice object
|
|
20
|
+
* @returns Promise resolving to a TInvoice object
|
|
21
|
+
*/
|
|
22
|
+
abstract decode(): Promise<TInvoice>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets letter data in the standard format
|
|
26
|
+
* @returns Promise resolving to a TInvoice object
|
|
27
|
+
*/
|
|
28
|
+
public async getLetterData(): Promise<TInvoice> {
|
|
29
|
+
return this.decode();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Gets the raw XML content
|
|
34
|
+
* @returns XML string
|
|
35
|
+
*/
|
|
36
|
+
public getXml(): string {
|
|
37
|
+
return this.xml;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parses a CII date string based on format code
|
|
42
|
+
* @param dateStr Date string
|
|
43
|
+
* @param format Format code (e.g., '102' for YYYYMMDD)
|
|
44
|
+
* @returns Timestamp in milliseconds
|
|
45
|
+
*/
|
|
46
|
+
protected parseCIIDate(dateStr: string, format?: string): number {
|
|
47
|
+
if (!dateStr) return Date.now();
|
|
48
|
+
|
|
49
|
+
// Format 102 is YYYYMMDD
|
|
50
|
+
if (format === '102' && dateStr.length === 8) {
|
|
51
|
+
const year = parseInt(dateStr.substring(0, 4));
|
|
52
|
+
const month = parseInt(dateStr.substring(4, 6)) - 1; // Month is 0-indexed in JS
|
|
53
|
+
const day = parseInt(dateStr.substring(6, 8));
|
|
54
|
+
return new Date(year, month, day).getTime();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Format 610 is YYYYMM
|
|
58
|
+
if (format === '610' && dateStr.length === 6) {
|
|
59
|
+
const year = parseInt(dateStr.substring(0, 4));
|
|
60
|
+
const month = parseInt(dateStr.substring(4, 6)) - 1;
|
|
61
|
+
return new Date(year, month, 1).getTime();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Try to parse as ISO date or other standard formats
|
|
65
|
+
const parsed = Date.parse(dateStr);
|
|
66
|
+
return isNaN(parsed) ? Date.now() : parsed;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TInvoice } from '../../interfaces/common.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base encoder class that defines common encoding functionality
|
|
5
|
+
* for all invoice format encoders
|
|
6
|
+
*/
|
|
7
|
+
export abstract class BaseEncoder {
|
|
8
|
+
/**
|
|
9
|
+
* Encodes a TInvoice object into XML
|
|
10
|
+
* @param invoice TInvoice object to encode
|
|
11
|
+
* @returns XML string
|
|
12
|
+
*/
|
|
13
|
+
abstract encode(invoice: TInvoice): Promise<string>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ValidationLevel } from '../../interfaces/common.js';
|
|
2
|
+
import type { ValidationResult, ValidationError } from '../../interfaces/common.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base validator class that defines common validation functionality
|
|
6
|
+
* for all invoice format validators
|
|
7
|
+
*/
|
|
8
|
+
export abstract class BaseValidator {
|
|
9
|
+
protected xml: string;
|
|
10
|
+
protected errors: ValidationError[] = [];
|
|
11
|
+
|
|
12
|
+
constructor(xml: string) {
|
|
13
|
+
this.xml = xml;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validates XML against the specified level of validation
|
|
18
|
+
* @param level Validation level (syntax, semantic, business)
|
|
19
|
+
* @returns Result of validation
|
|
20
|
+
*/
|
|
21
|
+
abstract validate(level?: ValidationLevel): ValidationResult;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets all validation errors found during validation
|
|
25
|
+
* @returns Array of validation errors
|
|
26
|
+
*/
|
|
27
|
+
public getValidationErrors(): ValidationError[] {
|
|
28
|
+
return this.errors;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Checks if the document is valid
|
|
33
|
+
* @returns True if no validation errors were found
|
|
34
|
+
*/
|
|
35
|
+
public isValid(): boolean {
|
|
36
|
+
return this.errors.length === 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validates XML against schema
|
|
41
|
+
* @returns True if schema validation passed
|
|
42
|
+
*/
|
|
43
|
+
protected abstract validateSchema(): boolean;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Validates business rules
|
|
47
|
+
* @returns True if business rule validation passed
|
|
48
|
+
*/
|
|
49
|
+
protected abstract validateBusinessRules(): boolean;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Adds an error to the validation errors list
|
|
53
|
+
* @param code Error code
|
|
54
|
+
* @param message Error message
|
|
55
|
+
* @param location Location in the XML where the error occurred
|
|
56
|
+
*/
|
|
57
|
+
protected addError(code: string, message: string, location: string = ''): void {
|
|
58
|
+
this.errors.push({
|
|
59
|
+
code,
|
|
60
|
+
message,
|
|
61
|
+
location
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { BaseDecoder } from '../base/base.decoder.js';
|
|
2
|
+
import type { TInvoice, TCreditNote, TDebitNote } from '../../interfaces/common.js';
|
|
3
|
+
import { CII_NAMESPACES, CIIProfile } from './cii.types.js';
|
|
4
|
+
import { DOMParser, xpath } from '../../plugins.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base decoder for CII-based invoice formats
|
|
8
|
+
*/
|
|
9
|
+
export abstract class CIIBaseDecoder extends BaseDecoder {
|
|
10
|
+
protected doc: Document;
|
|
11
|
+
protected namespaces: Record<string, string>;
|
|
12
|
+
protected select: xpath.XPathSelect;
|
|
13
|
+
protected profile: CIIProfile = CIIProfile.EN16931;
|
|
14
|
+
|
|
15
|
+
constructor(xml: string, skipValidation: boolean = false) {
|
|
16
|
+
super(xml, skipValidation);
|
|
17
|
+
|
|
18
|
+
// Parse XML document
|
|
19
|
+
this.doc = new DOMParser().parseFromString(xml, 'application/xml');
|
|
20
|
+
|
|
21
|
+
// Set up namespaces for XPath queries
|
|
22
|
+
this.namespaces = {
|
|
23
|
+
rsm: CII_NAMESPACES.RSM,
|
|
24
|
+
ram: CII_NAMESPACES.RAM,
|
|
25
|
+
udt: CII_NAMESPACES.UDT
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Create XPath selector with namespaces
|
|
29
|
+
this.select = xpath.useNamespaces(this.namespaces);
|
|
30
|
+
|
|
31
|
+
// Detect profile
|
|
32
|
+
this.detectProfile();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Decodes CII XML into a TInvoice object
|
|
37
|
+
* @returns Promise resolving to a TInvoice object
|
|
38
|
+
*/
|
|
39
|
+
public async decode(): Promise<TInvoice> {
|
|
40
|
+
// Determine if it's a credit note or debit note based on type code
|
|
41
|
+
const typeCode = this.getText('//ram:TypeCode');
|
|
42
|
+
|
|
43
|
+
if (typeCode === '381') { // Credit note type code
|
|
44
|
+
return this.decodeCreditNote() as unknown as TInvoice;
|
|
45
|
+
} else {
|
|
46
|
+
return this.decodeDebitNote() as unknown as TInvoice;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Detects the CII profile from the XML
|
|
52
|
+
*/
|
|
53
|
+
protected detectProfile(): void {
|
|
54
|
+
// Look for profile identifier
|
|
55
|
+
const profileNode = this.select(
|
|
56
|
+
'string(//rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/ram:ID)',
|
|
57
|
+
this.doc
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (profileNode) {
|
|
61
|
+
const profileText = profileNode.toString();
|
|
62
|
+
|
|
63
|
+
if (profileText.includes('BASIC')) {
|
|
64
|
+
this.profile = CIIProfile.BASIC;
|
|
65
|
+
} else if (profileText.includes('EN16931')) {
|
|
66
|
+
this.profile = CIIProfile.EN16931;
|
|
67
|
+
} else if (profileText.includes('EXTENDED')) {
|
|
68
|
+
this.profile = CIIProfile.EXTENDED;
|
|
69
|
+
} else if (profileText.includes('MINIMUM')) {
|
|
70
|
+
this.profile = CIIProfile.MINIMUM;
|
|
71
|
+
} else if (profileText.includes('COMFORT')) {
|
|
72
|
+
this.profile = CIIProfile.COMFORT;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Decodes a CII credit note
|
|
79
|
+
* @returns Promise resolving to a TCreditNote object
|
|
80
|
+
*/
|
|
81
|
+
protected abstract decodeCreditNote(): Promise<TCreditNote>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Decodes a CII debit note (invoice)
|
|
85
|
+
* @returns Promise resolving to a TDebitNote object
|
|
86
|
+
*/
|
|
87
|
+
protected abstract decodeDebitNote(): Promise<TDebitNote>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Gets a text value from an XPath expression
|
|
91
|
+
* @param xpath XPath expression
|
|
92
|
+
* @param context Optional context node
|
|
93
|
+
* @returns Text value or empty string if not found
|
|
94
|
+
*/
|
|
95
|
+
protected getText(xpathExpr: string, context?: Node): string {
|
|
96
|
+
const node = this.select(xpathExpr, context || this.doc)[0];
|
|
97
|
+
return node ? (node.textContent || '') : '';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Gets a number value from an XPath expression
|
|
102
|
+
* @param xpath XPath expression
|
|
103
|
+
* @param context Optional context node
|
|
104
|
+
* @returns Number value or 0 if not found or not a number
|
|
105
|
+
*/
|
|
106
|
+
protected getNumber(xpathExpr: string, context?: Node): number {
|
|
107
|
+
const text = this.getText(xpathExpr, context);
|
|
108
|
+
const num = parseFloat(text);
|
|
109
|
+
return isNaN(num) ? 0 : num;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Gets a date value from an XPath expression
|
|
114
|
+
* @param xpath XPath expression
|
|
115
|
+
* @param context Optional context node
|
|
116
|
+
* @returns Date timestamp or current time if not found or invalid
|
|
117
|
+
*/
|
|
118
|
+
protected getDate(xpathExpr: string, context?: Node): number {
|
|
119
|
+
const text = this.getText(xpathExpr, context);
|
|
120
|
+
if (!text) return Date.now();
|
|
121
|
+
|
|
122
|
+
const date = new Date(text);
|
|
123
|
+
return isNaN(date.getTime()) ? Date.now() : date.getTime();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Checks if a node exists
|
|
128
|
+
* @param xpath XPath expression
|
|
129
|
+
* @param context Optional context node
|
|
130
|
+
* @returns True if node exists
|
|
131
|
+
*/
|
|
132
|
+
protected exists(xpathExpr: string, context?: Node): boolean {
|
|
133
|
+
const nodes = this.select(xpathExpr, context || this.doc);
|
|
134
|
+
if (Array.isArray(nodes)) {
|
|
135
|
+
return nodes.length > 0;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { BaseEncoder } from '../base/base.encoder.js';
|
|
2
|
+
import type { TInvoice, TCreditNote, TDebitNote } from '../../interfaces/common.js';
|
|
3
|
+
import { CII_NAMESPACES, CIIProfile } from './cii.types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base encoder for CII-based invoice formats
|
|
7
|
+
*/
|
|
8
|
+
export abstract class CIIBaseEncoder extends BaseEncoder {
|
|
9
|
+
protected profile: CIIProfile = CIIProfile.EN16931;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Sets the CII profile to use for encoding
|
|
13
|
+
* @param profile CII profile
|
|
14
|
+
*/
|
|
15
|
+
public setProfile(profile: CIIProfile): void {
|
|
16
|
+
this.profile = profile;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Encodes a TInvoice object into CII XML
|
|
21
|
+
* @param invoice TInvoice object to encode
|
|
22
|
+
* @returns CII XML string
|
|
23
|
+
*/
|
|
24
|
+
public async encode(invoice: TInvoice): Promise<string> {
|
|
25
|
+
// TInvoice is always an invoice, treat it as debit note for encoding
|
|
26
|
+
return this.encodeDebitNote(invoice as unknown as TDebitNote);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Encodes a TCreditNote object into CII XML
|
|
31
|
+
* @param creditNote TCreditNote object to encode
|
|
32
|
+
* @returns CII XML string
|
|
33
|
+
*/
|
|
34
|
+
protected abstract encodeCreditNote(creditNote: TCreditNote): Promise<string>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Encodes a TDebitNote object into CII XML
|
|
38
|
+
* @param debitNote TDebitNote object to encode
|
|
39
|
+
* @returns CII XML string
|
|
40
|
+
*/
|
|
41
|
+
protected abstract encodeDebitNote(debitNote: TDebitNote): Promise<string>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates the XML declaration and root element
|
|
45
|
+
* @returns XML string with declaration and root element
|
|
46
|
+
*/
|
|
47
|
+
protected createXmlRoot(): string {
|
|
48
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
49
|
+
<rsm:CrossIndustryInvoice xmlns:rsm="${CII_NAMESPACES.RSM}"
|
|
50
|
+
xmlns:ram="${CII_NAMESPACES.RAM}"
|
|
51
|
+
xmlns:udt="${CII_NAMESPACES.UDT}">
|
|
52
|
+
</rsm:CrossIndustryInvoice>`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Formats a date as an ISO string (YYYY-MM-DD)
|
|
57
|
+
* @param timestamp Timestamp to format
|
|
58
|
+
* @returns Formatted date string
|
|
59
|
+
*/
|
|
60
|
+
protected formatDate(timestamp: number): string {
|
|
61
|
+
const date = new Date(timestamp);
|
|
62
|
+
return date.toISOString().split('T')[0];
|
|
63
|
+
}
|
|
64
|
+
}
|