@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,121 @@
|
|
|
1
|
+
import { BaseDecoder } from '../base/base.decoder.js';
|
|
2
|
+
import type { TInvoice, TCreditNote, TDebitNote } from '../../interfaces/common.js';
|
|
3
|
+
import { UBLDocumentType, UBL_NAMESPACES } from './ubl.types.js';
|
|
4
|
+
import { DOMParser, xpath } from '../../plugins.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base decoder for UBL-based invoice formats
|
|
8
|
+
*/
|
|
9
|
+
export abstract class UBLBaseDecoder extends BaseDecoder {
|
|
10
|
+
protected doc: Document;
|
|
11
|
+
protected namespaces: Record<string, string>;
|
|
12
|
+
protected select: xpath.XPathSelect;
|
|
13
|
+
|
|
14
|
+
constructor(xml: string, skipValidation: boolean = false) {
|
|
15
|
+
super(xml, skipValidation);
|
|
16
|
+
|
|
17
|
+
// Parse XML document
|
|
18
|
+
this.doc = new DOMParser().parseFromString(xml, 'application/xml');
|
|
19
|
+
|
|
20
|
+
// Set up namespaces for XPath queries
|
|
21
|
+
this.namespaces = {
|
|
22
|
+
cbc: UBL_NAMESPACES.CBC,
|
|
23
|
+
cac: UBL_NAMESPACES.CAC
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Create XPath selector with namespaces
|
|
27
|
+
this.select = xpath.useNamespaces(this.namespaces);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Decodes UBL XML into a TInvoice object
|
|
32
|
+
* @returns Promise resolving to a TInvoice object
|
|
33
|
+
*/
|
|
34
|
+
public async decode(): Promise<TInvoice> {
|
|
35
|
+
// Determine document type
|
|
36
|
+
const documentType = this.getDocumentType();
|
|
37
|
+
|
|
38
|
+
if (documentType === UBLDocumentType.CREDIT_NOTE) {
|
|
39
|
+
return this.decodeCreditNote() as unknown as TInvoice;
|
|
40
|
+
} else {
|
|
41
|
+
return this.decodeDebitNote() as unknown as TInvoice;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Gets the UBL document type
|
|
47
|
+
* @returns UBL document type
|
|
48
|
+
*/
|
|
49
|
+
protected getDocumentType(): UBLDocumentType {
|
|
50
|
+
const rootName = this.doc.documentElement.nodeName;
|
|
51
|
+
|
|
52
|
+
if (rootName === UBLDocumentType.CREDIT_NOTE) {
|
|
53
|
+
return UBLDocumentType.CREDIT_NOTE;
|
|
54
|
+
} else {
|
|
55
|
+
return UBLDocumentType.INVOICE;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Decodes a UBL credit note
|
|
61
|
+
* @returns Promise resolving to a TCreditNote object
|
|
62
|
+
*/
|
|
63
|
+
protected abstract decodeCreditNote(): Promise<TCreditNote>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Decodes a UBL debit note (invoice)
|
|
67
|
+
* @returns Promise resolving to a TDebitNote object
|
|
68
|
+
*/
|
|
69
|
+
protected abstract decodeDebitNote(): Promise<TDebitNote>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets a text value from an XPath expression
|
|
73
|
+
* @param xpath XPath expression
|
|
74
|
+
* @param context Optional context node
|
|
75
|
+
* @returns Text value or empty string if not found
|
|
76
|
+
*/
|
|
77
|
+
protected getText(xpathExpr: string, context?: Node): string {
|
|
78
|
+
const node = this.select(xpathExpr, context || this.doc)[0];
|
|
79
|
+
return node ? (node.textContent || '') : '';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Gets a number value from an XPath expression
|
|
84
|
+
* @param xpath XPath expression
|
|
85
|
+
* @param context Optional context node
|
|
86
|
+
* @returns Number value or 0 if not found or not a number
|
|
87
|
+
*/
|
|
88
|
+
protected getNumber(xpathExpr: string, context?: Node): number {
|
|
89
|
+
const text = this.getText(xpathExpr, context);
|
|
90
|
+
const num = parseFloat(text);
|
|
91
|
+
return isNaN(num) ? 0 : num;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets a date value from an XPath expression
|
|
96
|
+
* @param xpath XPath expression
|
|
97
|
+
* @param context Optional context node
|
|
98
|
+
* @returns Date timestamp or current time if not found or invalid
|
|
99
|
+
*/
|
|
100
|
+
protected getDate(xpathExpr: string, context?: Node): number {
|
|
101
|
+
const text = this.getText(xpathExpr, context);
|
|
102
|
+
if (!text) return Date.now();
|
|
103
|
+
|
|
104
|
+
const date = new Date(text);
|
|
105
|
+
return isNaN(date.getTime()) ? Date.now() : date.getTime();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Checks if a node exists
|
|
110
|
+
* @param xpath XPath expression
|
|
111
|
+
* @param context Optional context node
|
|
112
|
+
* @returns True if node exists
|
|
113
|
+
*/
|
|
114
|
+
protected exists(xpathExpr: string, context?: Node): boolean {
|
|
115
|
+
const nodes = this.select(xpathExpr, context || this.doc);
|
|
116
|
+
if (Array.isArray(nodes)) {
|
|
117
|
+
return nodes.length > 0;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BaseEncoder } from '../base/base.encoder.js';
|
|
2
|
+
import type { TInvoice, TCreditNote, TDebitNote } from '../../interfaces/common.js';
|
|
3
|
+
import { UBLDocumentType, UBL_NAMESPACES } from './ubl.types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base encoder for UBL-based invoice formats
|
|
7
|
+
*/
|
|
8
|
+
export abstract class UBLBaseEncoder extends BaseEncoder {
|
|
9
|
+
/**
|
|
10
|
+
* Encodes a TInvoice object into UBL XML
|
|
11
|
+
* @param invoice TInvoice object to encode
|
|
12
|
+
* @returns UBL XML string
|
|
13
|
+
*/
|
|
14
|
+
public async encode(invoice: TInvoice): Promise<string> {
|
|
15
|
+
// TInvoice is always an invoice, treat it as debit note for encoding
|
|
16
|
+
return this.encodeDebitNote(invoice as unknown as TDebitNote);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Encodes a TCreditNote object into UBL XML
|
|
21
|
+
* @param creditNote TCreditNote object to encode
|
|
22
|
+
* @returns UBL XML string
|
|
23
|
+
*/
|
|
24
|
+
protected abstract encodeCreditNote(creditNote: TCreditNote): Promise<string>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Encodes a TDebitNote object into UBL XML
|
|
28
|
+
* @param debitNote TDebitNote object to encode
|
|
29
|
+
* @returns UBL XML string
|
|
30
|
+
*/
|
|
31
|
+
protected abstract encodeDebitNote(debitNote: TDebitNote): Promise<string>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates the XML declaration and root element
|
|
35
|
+
* @param documentType UBL document type
|
|
36
|
+
* @returns XML string with declaration and root element
|
|
37
|
+
*/
|
|
38
|
+
protected createXmlRoot(documentType: UBLDocumentType): string {
|
|
39
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
40
|
+
<${documentType} xmlns="urn:oasis:names:specification:ubl:schema:xsd:${documentType}-2"
|
|
41
|
+
xmlns:cac="${UBL_NAMESPACES.CAC}"
|
|
42
|
+
xmlns:cbc="${UBL_NAMESPACES.CBC}">
|
|
43
|
+
</${documentType}>`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Formats a date as an ISO string (YYYY-MM-DD)
|
|
48
|
+
* @param timestamp Timestamp to format
|
|
49
|
+
* @returns Formatted date string
|
|
50
|
+
*/
|
|
51
|
+
protected formatDate(timestamp: number): string {
|
|
52
|
+
// Ensure timestamp is valid
|
|
53
|
+
if (!timestamp || isNaN(timestamp)) {
|
|
54
|
+
timestamp = Date.now();
|
|
55
|
+
}
|
|
56
|
+
const date = new Date(timestamp);
|
|
57
|
+
// Check if date is valid
|
|
58
|
+
if (isNaN(date.getTime())) {
|
|
59
|
+
return new Date().toISOString().split('T')[0];
|
|
60
|
+
}
|
|
61
|
+
return date.toISOString().split('T')[0];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UBL-specific types and constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// UBL namespaces
|
|
6
|
+
export const UBL_NAMESPACES = {
|
|
7
|
+
CBC: 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
|
8
|
+
CAC: 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
|
|
9
|
+
UBL: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// UBL document types
|
|
13
|
+
export enum UBLDocumentType {
|
|
14
|
+
INVOICE = 'Invoice',
|
|
15
|
+
CREDIT_NOTE = 'CreditNote'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// UBL customization IDs for different formats
|
|
19
|
+
export const UBL_CUSTOMIZATION_IDS = {
|
|
20
|
+
XRECHNUNG: 'urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.0',
|
|
21
|
+
PEPPOL_BIS: 'urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0'
|
|
22
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { BaseValidator } from '../base/base.validator.js';
|
|
2
|
+
import { ValidationLevel } from '../../interfaces/common.js';
|
|
3
|
+
import type { ValidationResult } from '../../interfaces/common.js';
|
|
4
|
+
import { UBLDocumentType } from './ubl.types.js';
|
|
5
|
+
import { DOMParser, xpath } from '../../plugins.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base validator for UBL-based invoice formats
|
|
9
|
+
*/
|
|
10
|
+
export abstract class UBLBaseValidator extends BaseValidator {
|
|
11
|
+
protected doc: Document;
|
|
12
|
+
protected namespaces: Record<string, string>;
|
|
13
|
+
protected select: xpath.XPathSelect;
|
|
14
|
+
|
|
15
|
+
constructor(xml: string) {
|
|
16
|
+
super(xml);
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Parse XML document
|
|
20
|
+
this.doc = new DOMParser().parseFromString(xml, 'application/xml');
|
|
21
|
+
|
|
22
|
+
// Set up namespaces for XPath queries
|
|
23
|
+
this.namespaces = {
|
|
24
|
+
cbc: 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
|
25
|
+
cac: 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Create XPath selector with namespaces
|
|
29
|
+
this.select = xpath.useNamespaces(this.namespaces);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
this.addError('UBL-PARSE', `Failed to parse XML: ${error}`, '/');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validates UBL XML against the specified level of validation
|
|
37
|
+
* @param level Validation level
|
|
38
|
+
* @returns Result of validation
|
|
39
|
+
*/
|
|
40
|
+
public validate(level: ValidationLevel = ValidationLevel.SYNTAX): ValidationResult {
|
|
41
|
+
// Reset errors
|
|
42
|
+
this.errors = [];
|
|
43
|
+
|
|
44
|
+
// Check if document was parsed successfully
|
|
45
|
+
if (!this.doc) {
|
|
46
|
+
return {
|
|
47
|
+
valid: false,
|
|
48
|
+
errors: this.errors,
|
|
49
|
+
level: level
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Perform validation based on level
|
|
54
|
+
let valid = true;
|
|
55
|
+
|
|
56
|
+
if (level === ValidationLevel.SYNTAX) {
|
|
57
|
+
valid = this.validateSchema();
|
|
58
|
+
} else if (level === ValidationLevel.SEMANTIC) {
|
|
59
|
+
valid = this.validateSchema() && this.validateStructure();
|
|
60
|
+
} else if (level === ValidationLevel.BUSINESS) {
|
|
61
|
+
valid = this.validateSchema() &&
|
|
62
|
+
this.validateStructure() &&
|
|
63
|
+
this.validateBusinessRules();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
valid,
|
|
68
|
+
errors: this.errors,
|
|
69
|
+
level
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Validates UBL XML against schema
|
|
75
|
+
* @returns True if schema validation passed
|
|
76
|
+
*/
|
|
77
|
+
protected validateSchema(): boolean {
|
|
78
|
+
// Basic schema validation (simplified for now)
|
|
79
|
+
if (!this.doc) return false;
|
|
80
|
+
|
|
81
|
+
// Check for root element
|
|
82
|
+
const root = this.doc.documentElement;
|
|
83
|
+
if (!root || (root.nodeName !== UBLDocumentType.INVOICE && root.nodeName !== UBLDocumentType.CREDIT_NOTE)) {
|
|
84
|
+
this.addError('UBL-SCHEMA-1', `Root element must be ${UBLDocumentType.INVOICE} or ${UBLDocumentType.CREDIT_NOTE}`, '/');
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validates structure of the UBL XML document
|
|
93
|
+
* @returns True if structure validation passed
|
|
94
|
+
*/
|
|
95
|
+
protected abstract validateStructure(): boolean;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Gets a text value from an XPath expression
|
|
99
|
+
* @param xpath XPath expression
|
|
100
|
+
* @param context Optional context node
|
|
101
|
+
* @returns Text value or empty string if not found
|
|
102
|
+
*/
|
|
103
|
+
protected getText(xpathExpr: string, context?: Node): string {
|
|
104
|
+
const node = this.select(xpathExpr, context || this.doc)[0];
|
|
105
|
+
return node ? (node.textContent || '') : '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Gets a number value from an XPath expression
|
|
110
|
+
* @param xpath XPath expression
|
|
111
|
+
* @param context Optional context node
|
|
112
|
+
* @returns Number value or 0 if not found or not a number
|
|
113
|
+
*/
|
|
114
|
+
protected getNumber(xpathExpr: string, context?: Node): number {
|
|
115
|
+
const text = this.getText(xpathExpr, context);
|
|
116
|
+
const num = parseFloat(text);
|
|
117
|
+
return isNaN(num) ? 0 : num;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if a node exists
|
|
122
|
+
* @param xpath XPath expression
|
|
123
|
+
* @param context Optional context node
|
|
124
|
+
* @returns True if node exists
|
|
125
|
+
*/
|
|
126
|
+
protected exists(xpathExpr: string, context?: Node): boolean {
|
|
127
|
+
const nodes = this.select(xpathExpr, context || this.doc);
|
|
128
|
+
if (Array.isArray(nodes)) {
|
|
129
|
+
return nodes.length > 0;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|