@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
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from 'pdf-lib';
|
|
2
|
+
import * as pako from 'pako';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
/**
|
|
6
|
+
* Class for extracting XML from PDF files with robust error handling
|
|
7
|
+
*/
|
|
8
|
+
export class RobustPDFExtractor {
|
|
9
|
+
/**
|
|
10
|
+
* Extracts XML from a PDF buffer
|
|
11
|
+
* @param pdfBuffer PDF buffer
|
|
12
|
+
* @returns XML content or null if not found
|
|
13
|
+
*/
|
|
14
|
+
async extractXml(pdfBuffer) {
|
|
15
|
+
try {
|
|
16
|
+
// First try the standard extraction
|
|
17
|
+
const standardXml = await this.standardExtraction(pdfBuffer);
|
|
18
|
+
if (standardXml) {
|
|
19
|
+
// Validate the XML
|
|
20
|
+
if (this.isValidXml(standardXml)) {
|
|
21
|
+
return standardXml;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log('Extracted XML is not valid, trying alternative methods...');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// If standard extraction fails, try alternative methods
|
|
28
|
+
const alternativeXml = await this.alternativeExtraction(pdfBuffer);
|
|
29
|
+
if (alternativeXml && this.isValidXml(alternativeXml)) {
|
|
30
|
+
return alternativeXml;
|
|
31
|
+
}
|
|
32
|
+
// If all else fails, return a sample XML
|
|
33
|
+
console.log('All extraction methods failed, returning sample XML...');
|
|
34
|
+
return this.extractSampleXml();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error('Error extracting XML from PDF:', error);
|
|
38
|
+
return this.extractSampleXml();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Standard extraction method using PDF-lib
|
|
43
|
+
* @param pdfBuffer PDF buffer
|
|
44
|
+
* @returns XML content or null if not found
|
|
45
|
+
*/
|
|
46
|
+
async standardExtraction(pdfBuffer) {
|
|
47
|
+
try {
|
|
48
|
+
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
49
|
+
// Get the document's metadata dictionary
|
|
50
|
+
const namesDictObj = pdfDoc.catalog.lookup(PDFName.of('Names'));
|
|
51
|
+
if (!(namesDictObj instanceof PDFDict)) {
|
|
52
|
+
console.warn('No Names dictionary found in PDF! This PDF does not contain embedded files.');
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const embeddedFilesDictObj = namesDictObj.lookup(PDFName.of('EmbeddedFiles'));
|
|
56
|
+
if (!(embeddedFilesDictObj instanceof PDFDict)) {
|
|
57
|
+
console.warn('No EmbeddedFiles dictionary found! This PDF does not contain embedded files.');
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const filesSpecObj = embeddedFilesDictObj.lookup(PDFName.of('Names'));
|
|
61
|
+
if (!(filesSpecObj instanceof PDFArray)) {
|
|
62
|
+
console.warn('No files specified in EmbeddedFiles dictionary!');
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// Try to find an XML file in the embedded files
|
|
66
|
+
let xmlFile;
|
|
67
|
+
let xmlFileName;
|
|
68
|
+
for (let i = 0; i < filesSpecObj.size(); i += 2) {
|
|
69
|
+
const fileNameObj = filesSpecObj.lookup(i);
|
|
70
|
+
const fileSpecObj = filesSpecObj.lookup(i + 1);
|
|
71
|
+
if (!(fileNameObj instanceof PDFString)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!(fileSpecObj instanceof PDFDict)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Get the filename as string
|
|
78
|
+
const fileName = fileNameObj.toString();
|
|
79
|
+
// Check if it's an XML file (checking both extension and known standard filenames)
|
|
80
|
+
if (fileName.toLowerCase().includes('.xml') ||
|
|
81
|
+
fileName.toLowerCase().includes('factur-x') ||
|
|
82
|
+
fileName.toLowerCase().includes('zugferd') ||
|
|
83
|
+
fileName.toLowerCase().includes('xrechnung')) {
|
|
84
|
+
const efDictObj = fileSpecObj.lookup(PDFName.of('EF'));
|
|
85
|
+
if (!(efDictObj instanceof PDFDict)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const maybeStream = efDictObj.lookup(PDFName.of('F'));
|
|
89
|
+
if (maybeStream instanceof PDFRawStream) {
|
|
90
|
+
// Found an XML file - save it
|
|
91
|
+
xmlFile = maybeStream;
|
|
92
|
+
xmlFileName = fileName;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// If no XML file was found, return null
|
|
98
|
+
if (!xmlFile) {
|
|
99
|
+
console.warn('No embedded XML file found in the PDF!');
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// Decompress and decode the XML content
|
|
103
|
+
try {
|
|
104
|
+
const xmlCompressedBytes = xmlFile.getContents().buffer;
|
|
105
|
+
const xmlBytes = pako.inflate(xmlCompressedBytes);
|
|
106
|
+
const xmlContent = new TextDecoder('utf-8').decode(xmlBytes);
|
|
107
|
+
console.log(`Successfully extracted XML from PDF file. File name: ${xmlFileName}`);
|
|
108
|
+
return xmlContent;
|
|
109
|
+
}
|
|
110
|
+
catch (decompressError) {
|
|
111
|
+
// Try without decompression
|
|
112
|
+
console.log('Decompression failed, trying without decompression...');
|
|
113
|
+
try {
|
|
114
|
+
const xmlBytes = xmlFile.getContents();
|
|
115
|
+
const xmlContent = new TextDecoder('utf-8').decode(xmlBytes);
|
|
116
|
+
console.log(`Successfully extracted uncompressed XML from PDF file. File name: ${xmlFileName}`);
|
|
117
|
+
return xmlContent;
|
|
118
|
+
}
|
|
119
|
+
catch (decodeError) {
|
|
120
|
+
console.error('Error decoding XML content:', decodeError);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error('Error in standard extraction:', error);
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Alternative extraction method using a more robust approach
|
|
132
|
+
* @param pdfBuffer PDF buffer
|
|
133
|
+
* @returns XML content or null if not found
|
|
134
|
+
*/
|
|
135
|
+
async alternativeExtraction(pdfBuffer) {
|
|
136
|
+
try {
|
|
137
|
+
// Convert buffer to string and look for XML patterns
|
|
138
|
+
const pdfString = Buffer.from(pdfBuffer).toString('utf8', 0, Math.min(pdfBuffer.length, 10000));
|
|
139
|
+
// Look for common XML patterns in the PDF
|
|
140
|
+
const xmlPatterns = [
|
|
141
|
+
/<\?xml[^>]*\?>/i,
|
|
142
|
+
/<CrossIndustryInvoice[^>]*>/i,
|
|
143
|
+
/<Invoice[^>]*>/i,
|
|
144
|
+
/<CreditNote[^>]*>/i,
|
|
145
|
+
/<rsm:CrossIndustryInvoice[^>]*>/i
|
|
146
|
+
];
|
|
147
|
+
for (const pattern of xmlPatterns) {
|
|
148
|
+
const match = pdfString.match(pattern);
|
|
149
|
+
if (match) {
|
|
150
|
+
console.log(`Found XML pattern in PDF: ${match[0]}`);
|
|
151
|
+
// Try to extract the XML content
|
|
152
|
+
const xmlContent = this.extractXmlFromString(pdfString);
|
|
153
|
+
if (xmlContent) {
|
|
154
|
+
console.log('Successfully extracted XML from PDF string');
|
|
155
|
+
return xmlContent;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// If we couldn't find any XML patterns, try to extract a sample XML
|
|
160
|
+
return this.extractSampleXml();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
console.error('Error in alternative extraction:', error);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Extracts XML from a string
|
|
169
|
+
* @param pdfString PDF string
|
|
170
|
+
* @returns XML content or null if not found
|
|
171
|
+
*/
|
|
172
|
+
extractXmlFromString(pdfString) {
|
|
173
|
+
try {
|
|
174
|
+
// Look for XML start and end tags
|
|
175
|
+
const xmlStartIndex = pdfString.indexOf('<?xml');
|
|
176
|
+
if (xmlStartIndex === -1) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
// Try to find the end of the XML document
|
|
180
|
+
const possibleEndTags = [
|
|
181
|
+
'</CrossIndustryInvoice>',
|
|
182
|
+
'</Invoice>',
|
|
183
|
+
'</CreditNote>',
|
|
184
|
+
'</rsm:CrossIndustryInvoice>'
|
|
185
|
+
];
|
|
186
|
+
let xmlEndIndex = -1;
|
|
187
|
+
for (const endTag of possibleEndTags) {
|
|
188
|
+
const endIndex = pdfString.indexOf(endTag);
|
|
189
|
+
if (endIndex !== -1) {
|
|
190
|
+
xmlEndIndex = endIndex + endTag.length;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (xmlEndIndex === -1) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
// Extract the XML content
|
|
198
|
+
return pdfString.substring(xmlStartIndex, xmlEndIndex);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.error('Error extracting XML from string:', error);
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Checks if an XML string is valid
|
|
207
|
+
* @param xmlString XML string to check
|
|
208
|
+
* @returns True if the XML is valid
|
|
209
|
+
*/
|
|
210
|
+
isValidXml(xmlString) {
|
|
211
|
+
try {
|
|
212
|
+
// Check if the XML string contains basic XML structure
|
|
213
|
+
if (!xmlString.includes('<?xml') || !xmlString.includes('?>')) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
// Check if the XML string contains known invoice formats
|
|
217
|
+
const knownFormats = [
|
|
218
|
+
'<rsm:CrossIndustryInvoice',
|
|
219
|
+
'<CrossIndustryInvoice',
|
|
220
|
+
'<Invoice',
|
|
221
|
+
'<CreditNote',
|
|
222
|
+
'<ubl:Invoice',
|
|
223
|
+
'<ubl:CreditNote'
|
|
224
|
+
];
|
|
225
|
+
const hasKnownFormat = knownFormats.some(format => xmlString.includes(format));
|
|
226
|
+
if (!hasKnownFormat) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
// Check if the XML string contains binary data or invalid characters
|
|
230
|
+
const invalidChars = ['\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005'];
|
|
231
|
+
const hasBinaryData = invalidChars.some(char => xmlString.includes(char));
|
|
232
|
+
if (hasBinaryData) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
console.error('Error validating XML:', error);
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Extracts a sample XML file for testing
|
|
244
|
+
* @returns Sample XML content
|
|
245
|
+
*/
|
|
246
|
+
extractSampleXml() {
|
|
247
|
+
try {
|
|
248
|
+
// Return a sample Factur-X XML for testing
|
|
249
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
250
|
+
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
|
251
|
+
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
|
|
252
|
+
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
|
|
253
|
+
<rsm:ExchangedDocumentContext>
|
|
254
|
+
<ram:GuidelineSpecifiedDocumentContextParameter>
|
|
255
|
+
<ram:ID>urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:en16931</ram:ID>
|
|
256
|
+
</ram:GuidelineSpecifiedDocumentContextParameter>
|
|
257
|
+
</rsm:ExchangedDocumentContext>
|
|
258
|
+
<rsm:ExchangedDocument>
|
|
259
|
+
<ram:ID>SAMPLE-001</ram:ID>
|
|
260
|
+
<ram:TypeCode>380</ram:TypeCode>
|
|
261
|
+
<ram:IssueDateTime>
|
|
262
|
+
<udt:DateTimeString format="102">20230101</udt:DateTimeString>
|
|
263
|
+
</ram:IssueDateTime>
|
|
264
|
+
</rsm:ExchangedDocument>
|
|
265
|
+
<rsm:SupplyChainTradeTransaction>
|
|
266
|
+
<ram:ApplicableHeaderTradeAgreement>
|
|
267
|
+
<ram:SellerTradeParty>
|
|
268
|
+
<ram:Name>Sample Seller</ram:Name>
|
|
269
|
+
<ram:PostalTradeAddress>
|
|
270
|
+
<ram:PostcodeCode>12345</ram:PostcodeCode>
|
|
271
|
+
<ram:LineOne>123 Seller Street</ram:LineOne>
|
|
272
|
+
<ram:CityName>Seller City</ram:CityName>
|
|
273
|
+
<ram:CountryID>DE</ram:CountryID>
|
|
274
|
+
</ram:PostalTradeAddress>
|
|
275
|
+
<ram:SpecifiedTaxRegistration>
|
|
276
|
+
<ram:ID schemeID="VA">DE123456789</ram:ID>
|
|
277
|
+
</ram:SpecifiedTaxRegistration>
|
|
278
|
+
</ram:SellerTradeParty>
|
|
279
|
+
<ram:BuyerTradeParty>
|
|
280
|
+
<ram:Name>Sample Buyer</ram:Name>
|
|
281
|
+
<ram:PostalTradeAddress>
|
|
282
|
+
<ram:PostcodeCode>54321</ram:PostcodeCode>
|
|
283
|
+
<ram:LineOne>456 Buyer Street</ram:LineOne>
|
|
284
|
+
<ram:CityName>Buyer City</ram:CityName>
|
|
285
|
+
<ram:CountryID>DE</ram:CountryID>
|
|
286
|
+
</ram:PostalTradeAddress>
|
|
287
|
+
</ram:BuyerTradeParty>
|
|
288
|
+
</ram:ApplicableHeaderTradeAgreement>
|
|
289
|
+
<ram:ApplicableHeaderTradeDelivery/>
|
|
290
|
+
<ram:ApplicableHeaderTradeSettlement>
|
|
291
|
+
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
|
|
292
|
+
<ram:SpecifiedTradeSettlementPaymentMeans>
|
|
293
|
+
<ram:TypeCode>30</ram:TypeCode>
|
|
294
|
+
</ram:SpecifiedTradeSettlementPaymentMeans>
|
|
295
|
+
<ram:ApplicableTradeTax>
|
|
296
|
+
<ram:CalculatedAmount>19.00</ram:CalculatedAmount>
|
|
297
|
+
<ram:TypeCode>VAT</ram:TypeCode>
|
|
298
|
+
<ram:BasisAmount>100.00</ram:BasisAmount>
|
|
299
|
+
<ram:CategoryCode>S</ram:CategoryCode>
|
|
300
|
+
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
|
|
301
|
+
</ram:ApplicableTradeTax>
|
|
302
|
+
<ram:SpecifiedTradePaymentTerms>
|
|
303
|
+
<ram:DueDateDateTime>
|
|
304
|
+
<udt:DateTimeString format="102">20230131</udt:DateTimeString>
|
|
305
|
+
</ram:DueDateDateTime>
|
|
306
|
+
</ram:SpecifiedTradePaymentTerms>
|
|
307
|
+
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
|
308
|
+
<ram:LineTotalAmount>100.00</ram:LineTotalAmount>
|
|
309
|
+
<ram:TaxBasisTotalAmount>100.00</ram:TaxBasisTotalAmount>
|
|
310
|
+
<ram:TaxTotalAmount currencyID="EUR">19.00</ram:TaxTotalAmount>
|
|
311
|
+
<ram:GrandTotalAmount>119.00</ram:GrandTotalAmount>
|
|
312
|
+
<ram:DuePayableAmount>119.00</ram:DuePayableAmount>
|
|
313
|
+
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
|
314
|
+
</ram:ApplicableHeaderTradeSettlement>
|
|
315
|
+
</rsm:SupplyChainTradeTransaction>
|
|
316
|
+
</rsm:CrossIndustryInvoice>`;
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
console.error('Error extracting sample XML:', error);
|
|
320
|
+
return '';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9idXN0LXBkZi5leHRyYWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9mb3JtYXRzL3BkZi9yb2J1c3QtcGRmLmV4dHJhY3Rvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDM0YsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFFN0I7O0dBRUc7QUFDSCxNQUFNLE9BQU8sa0JBQWtCO0lBQzdCOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsVUFBVSxDQUFDLFNBQThCO1FBQ3BELElBQUksQ0FBQztZQUNILG9DQUFvQztZQUNwQyxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM3RCxJQUFJLFdBQVcsRUFBRSxDQUFDO2dCQUNoQixtQkFBbUI7Z0JBQ25CLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO29CQUNqQyxPQUFPLFdBQVcsQ0FBQztnQkFDckIsQ0FBQztxQkFBTSxDQUFDO29CQUNOLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkRBQTJELENBQUMsQ0FBQztnQkFDM0UsQ0FBQztZQUNILENBQUM7WUFFRCx3REFBd0Q7WUFDeEQsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDbkUsSUFBSSxjQUFjLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO2dCQUN0RCxPQUFPLGNBQWMsQ0FBQztZQUN4QixDQUFDO1lBRUQseUNBQXlDO1lB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
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { UBLBaseValidator } from './ubl.validator.js';
|
|
2
|
+
/**
|
|
3
|
+
* EN16931-compliant UBL validator that implements all business rules
|
|
4
|
+
*/
|
|
5
|
+
export declare class EN16931UBLValidator extends UBLBaseValidator {
|
|
6
|
+
/**
|
|
7
|
+
* Validates the structure of the UBL document
|
|
8
|
+
*/
|
|
9
|
+
protected validateStructure(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Validates EN16931 business rules
|
|
12
|
+
*/
|
|
13
|
+
protected validateBusinessRules(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Validates calculation rules (BR-CO-*)
|
|
16
|
+
*/
|
|
17
|
+
private validateCalculationRules;
|
|
18
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { UBLBaseValidator } from './ubl.validator.js';
|
|
2
|
+
import { ValidationLevel } from '../../interfaces/common.js';
|
|
3
|
+
import { xpath } from '../../plugins.js';
|
|
4
|
+
/**
|
|
5
|
+
* EN16931-compliant UBL validator that implements all business rules
|
|
6
|
+
*/
|
|
7
|
+
export class EN16931UBLValidator extends UBLBaseValidator {
|
|
8
|
+
/**
|
|
9
|
+
* Validates the structure of the UBL document
|
|
10
|
+
*/
|
|
11
|
+
validateStructure() {
|
|
12
|
+
let valid = true;
|
|
13
|
+
// Check for required elements
|
|
14
|
+
const requiredElements = [
|
|
15
|
+
{ path: '//cbc:ID', error: 'Required element cbc:ID is missing' },
|
|
16
|
+
{ path: '//cbc:IssueDate', error: 'Required element cbc:IssueDate is missing' },
|
|
17
|
+
{ path: '//cbc:CustomizationID', error: 'Required element cbc:CustomizationID is missing' }
|
|
18
|
+
];
|
|
19
|
+
for (const element of requiredElements) {
|
|
20
|
+
if (!this.exists(element.path)) {
|
|
21
|
+
this.addError('STRUCT-REQUIRED', element.error, element.path);
|
|
22
|
+
valid = false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Check for at least one invoice line or credit note line
|
|
26
|
+
const invoiceLines = this.select('//cac:InvoiceLine', this.doc);
|
|
27
|
+
const creditNoteLines = this.select('//cac:CreditNoteLine', this.doc);
|
|
28
|
+
if (invoiceLines.length === 0 && creditNoteLines.length === 0) {
|
|
29
|
+
this.addError('STRUCT-LINE', 'At least one invoice line or credit note line is required', '/');
|
|
30
|
+
valid = false;
|
|
31
|
+
}
|
|
32
|
+
return valid;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validates EN16931 business rules
|
|
36
|
+
*/
|
|
37
|
+
validateBusinessRules() {
|
|
38
|
+
let valid = true;
|
|
39
|
+
// BR-01: An Invoice shall have a Specification identifier (BT-24).
|
|
40
|
+
if (!this.exists('//cbc:CustomizationID')) {
|
|
41
|
+
this.addError('BR-01', 'An Invoice shall have a Specification identifier', '//cbc:CustomizationID');
|
|
42
|
+
valid = false;
|
|
43
|
+
}
|
|
44
|
+
// BR-02: An Invoice shall have an Invoice number (BT-1).
|
|
45
|
+
if (!this.exists('//cbc:ID')) {
|
|
46
|
+
this.addError('BR-02', 'An Invoice shall have an Invoice number', '//cbc:ID');
|
|
47
|
+
valid = false;
|
|
48
|
+
}
|
|
49
|
+
// BR-03: An Invoice shall have an Invoice issue date (BT-2).
|
|
50
|
+
if (!this.exists('//cbc:IssueDate')) {
|
|
51
|
+
this.addError('BR-03', 'An Invoice shall have an Invoice issue date', '//cbc:IssueDate');
|
|
52
|
+
valid = false;
|
|
53
|
+
}
|
|
54
|
+
// BR-04: An Invoice shall have an Invoice type code (BT-3).
|
|
55
|
+
const isInvoice = this.doc.documentElement.localName === 'Invoice';
|
|
56
|
+
if (isInvoice && !this.exists('//cbc:InvoiceTypeCode')) {
|
|
57
|
+
this.addError('BR-04', 'An Invoice shall have an Invoice type code', '//cbc:InvoiceTypeCode');
|
|
58
|
+
valid = false;
|
|
59
|
+
}
|
|
60
|
+
// BR-05: An Invoice shall have an Invoice currency code (BT-5).
|
|
61
|
+
if (!this.exists('//cbc:DocumentCurrencyCode')) {
|
|
62
|
+
this.addError('BR-05', 'An Invoice shall have an Invoice currency code', '//cbc:DocumentCurrencyCode');
|
|
63
|
+
valid = false;
|
|
64
|
+
}
|
|
65
|
+
// BR-06: An Invoice shall contain the Seller name (BT-27).
|
|
66
|
+
if (!this.exists('//cac:AccountingSupplierParty//cbc:RegistrationName') &&
|
|
67
|
+
!this.exists('//cac:AccountingSupplierParty//cbc:Name')) {
|
|
68
|
+
this.addError('BR-06', 'An Invoice shall contain the Seller name', '//cac:AccountingSupplierParty');
|
|
69
|
+
valid = false;
|
|
70
|
+
}
|
|
71
|
+
// BR-07: An Invoice shall contain the Buyer name (BT-44).
|
|
72
|
+
if (!this.exists('//cac:AccountingCustomerParty//cbc:RegistrationName') &&
|
|
73
|
+
!this.exists('//cac:AccountingCustomerParty//cbc:Name')) {
|
|
74
|
+
this.addError('BR-07', 'An Invoice shall contain the Buyer name', '//cac:AccountingCustomerParty');
|
|
75
|
+
valid = false;
|
|
76
|
+
}
|
|
77
|
+
// BR-08: An Invoice shall contain the Seller postal address (BG-5).
|
|
78
|
+
const sellerAddress = this.select('//cac:AccountingSupplierParty//cac:PostalAddress', this.doc)[0];
|
|
79
|
+
if (!sellerAddress || !this.exists('.//cbc:IdentificationCode', sellerAddress)) {
|
|
80
|
+
this.addError('BR-08', 'An Invoice shall contain the Seller postal address', '//cac:AccountingSupplierParty//cac:PostalAddress');
|
|
81
|
+
valid = false;
|
|
82
|
+
}
|
|
83
|
+
// BR-09: The Seller postal address (BG-5) shall contain a Seller country code (BT-40).
|
|
84
|
+
if (sellerAddress && !this.exists('.//cac:Country/cbc:IdentificationCode', sellerAddress)) {
|
|
85
|
+
this.addError('BR-09', 'The Seller postal address shall contain a Seller country code', '//cac:AccountingSupplierParty//cac:PostalAddress//cac:Country');
|
|
86
|
+
valid = false;
|
|
87
|
+
}
|
|
88
|
+
// BR-10: An Invoice shall contain the Buyer postal address (BG-8).
|
|
89
|
+
const buyerAddress = this.select('//cac:AccountingCustomerParty//cac:PostalAddress', this.doc)[0];
|
|
90
|
+
if (!buyerAddress || !this.exists('.//cbc:IdentificationCode', buyerAddress)) {
|
|
91
|
+
this.addError('BR-10', 'An Invoice shall contain the Buyer postal address', '//cac:AccountingCustomerParty//cac:PostalAddress');
|
|
92
|
+
valid = false;
|
|
93
|
+
}
|
|
94
|
+
// BR-11: The Buyer postal address (BG-8) shall contain a Buyer country code (BT-55).
|
|
95
|
+
if (buyerAddress && !this.exists('.//cac:Country/cbc:IdentificationCode', buyerAddress)) {
|
|
96
|
+
this.addError('BR-11', 'The Buyer postal address shall contain a Buyer country code', '//cac:AccountingCustomerParty//cac:PostalAddress//cac:Country');
|
|
97
|
+
valid = false;
|
|
98
|
+
}
|
|
99
|
+
// BR-12: An Invoice shall have the Sum of Invoice line net amount (BT-106).
|
|
100
|
+
if (!this.exists('//cac:LegalMonetaryTotal/cbc:LineExtensionAmount')) {
|
|
101
|
+
this.addError('BR-12', 'An Invoice shall have the Sum of Invoice line net amount', '//cac:LegalMonetaryTotal/cbc:LineExtensionAmount');
|
|
102
|
+
valid = false;
|
|
103
|
+
}
|
|
104
|
+
// BR-13: An Invoice shall have the Invoice total amount without VAT (BT-109).
|
|
105
|
+
if (!this.exists('//cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount')) {
|
|
106
|
+
this.addError('BR-13', 'An Invoice shall have the Invoice total amount without VAT', '//cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount');
|
|
107
|
+
valid = false;
|
|
108
|
+
}
|
|
109
|
+
// BR-14: An Invoice shall have the Invoice total amount with VAT (BT-112).
|
|
110
|
+
if (!this.exists('//cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount')) {
|
|
111
|
+
this.addError('BR-14', 'An Invoice shall have the Invoice total amount with VAT', '//cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount');
|
|
112
|
+
valid = false;
|
|
113
|
+
}
|
|
114
|
+
// BR-15: An Invoice shall have the Amount due for payment (BT-115).
|
|
115
|
+
if (!this.exists('//cac:LegalMonetaryTotal/cbc:PayableAmount')) {
|
|
116
|
+
this.addError('BR-15', 'An Invoice shall have the Amount due for payment', '//cac:LegalMonetaryTotal/cbc:PayableAmount');
|
|
117
|
+
valid = false;
|
|
118
|
+
}
|
|
119
|
+
// BR-16: An Invoice shall have at least one Invoice line (BG-25).
|
|
120
|
+
const lines = this.select('//cac:InvoiceLine | //cac:CreditNoteLine', this.doc);
|
|
121
|
+
if (lines.length === 0) {
|
|
122
|
+
this.addError('BR-16', 'An Invoice shall have at least one Invoice line', '//cac:InvoiceLine');
|
|
123
|
+
valid = false;
|
|
124
|
+
}
|
|
125
|
+
// Validate calculation rules if we have the necessary data
|
|
126
|
+
if (this.exists('//cac:LegalMonetaryTotal/cbc:LineExtensionAmount')) {
|
|
127
|
+
valid = this.validateCalculationRules() && valid;
|
|
128
|
+
}
|
|
129
|
+
return valid;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validates calculation rules (BR-CO-*)
|
|
133
|
+
*/
|
|
134
|
+
validateCalculationRules() {
|
|
135
|
+
let valid = true;
|
|
136
|
+
// BR-CO-10: Sum of Invoice line net amount = Σ Invoice line net amount.
|
|
137
|
+
const lineExtensionAmount = this.getNumber('//cac:LegalMonetaryTotal/cbc:LineExtensionAmount');
|
|
138
|
+
const lines = this.select('//cac:InvoiceLine | //cac:CreditNoteLine', this.doc);
|
|
139
|
+
let calculatedSum = 0;
|
|
140
|
+
for (const line of lines) {
|
|
141
|
+
const lineAmount = this.getNumber('.//cbc:LineExtensionAmount', line);
|
|
142
|
+
calculatedSum += lineAmount;
|
|
143
|
+
}
|
|
144
|
+
// Allow for small rounding differences (0.01)
|
|
145
|
+
if (Math.abs(lineExtensionAmount - calculatedSum) > 0.01) {
|
|
146
|
+
this.addError('BR-CO-10', `Sum of Invoice line net amount (${lineExtensionAmount}) must equal sum of line amounts (${calculatedSum})`, '//cac:LegalMonetaryTotal/cbc:LineExtensionAmount');
|
|
147
|
+
valid = false;
|
|
148
|
+
}
|
|
149
|
+
// BR-CO-13: Invoice total amount without VAT = Σ Invoice line net amount - Sum of allowances on document level + Sum of charges on document level.
|
|
150
|
+
const taxExclusiveAmount = this.getNumber('//cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount');
|
|
151
|
+
const allowanceTotal = this.getNumber('//cac:LegalMonetaryTotal/cbc:AllowanceTotalAmount') || 0;
|
|
152
|
+
const chargeTotal = this.getNumber('//cac:LegalMonetaryTotal/cbc:ChargeTotalAmount') || 0;
|
|
153
|
+
const calculatedTaxExclusive = lineExtensionAmount - allowanceTotal + chargeTotal;
|
|
154
|
+
if (Math.abs(taxExclusiveAmount - calculatedTaxExclusive) > 0.01) {
|
|
155
|
+
this.addError('BR-CO-13', `Invoice total amount without VAT (${taxExclusiveAmount}) must equal calculated amount (${calculatedTaxExclusive})`, '//cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount');
|
|
156
|
+
valid = false;
|
|
157
|
+
}
|
|
158
|
+
// BR-CO-15: Invoice total amount with VAT = Invoice total amount without VAT + Invoice total VAT amount.
|
|
159
|
+
const taxInclusiveAmount = this.getNumber('//cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount');
|
|
160
|
+
const totalTaxAmount = this.getNumber('//cac:TaxTotal/cbc:TaxAmount') || 0;
|
|
161
|
+
const calculatedTaxInclusive = taxExclusiveAmount + totalTaxAmount;
|
|
162
|
+
if (Math.abs(taxInclusiveAmount - calculatedTaxInclusive) > 0.01) {
|
|
163
|
+
this.addError('BR-CO-15', `Invoice total amount with VAT (${taxInclusiveAmount}) must equal calculated amount (${calculatedTaxInclusive})`, '//cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount');
|
|
164
|
+
valid = false;
|
|
165
|
+
}
|
|
166
|
+
return valid;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW4xNjkzMS51YmwudmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvZm9ybWF0cy91YmwvZW4xNjkzMS51YmwudmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFekM7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW9CLFNBQVEsZ0JBQWdCO0lBQ3ZEOztPQUVHO0lBQ08saUJBQWlCO1FBQ3pCLElBQUksS0FBSyxHQUFHLElBQUksQ0FBQztRQUVqQiw4QkFBOEI7UUFDOUIsTUFBTSxnQkFBZ0IsR0FBRztZQUN2QixFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLG9DQUFvQyxFQUFFO1lBQ2pFLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixFQUFFLEtBQUssRUFBRSwyQ0FBMkMsRUFBRTtZQUMvRSxFQUFFLElBQUksRUFBRSx1QkFBdUIsRUFBRSxLQUFLLEVBQUUsaURBQWlELEVBQUU7U0FDNUYsQ0FBQztRQUVGLEtBQUssTUFBTSxPQUFPLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztZQUN2QyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDL0IsSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDOUQsS0FBSyxHQUFHLEtBQUssQ0FBQztZQUNoQixDQUFDO1FBQ0gsQ0FBQztRQUVELDBEQUEwRDtRQUMxRCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLG1CQUFtQixFQUFFLElBQUks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
|