@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,483 @@
|
|
|
1
|
+
import { CIIBaseEncoder } from '../cii.encoder.js';
|
|
2
|
+
import type { TInvoice, TCreditNote, TDebitNote } from '../../../interfaces/common.js';
|
|
3
|
+
import { FACTURX_PROFILE_IDS } from './facturx.types.js';
|
|
4
|
+
import { DOMParser, XMLSerializer } from '../../../plugins.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Encoder for Factur-X invoice format
|
|
8
|
+
*/
|
|
9
|
+
export class FacturXEncoder extends CIIBaseEncoder {
|
|
10
|
+
/**
|
|
11
|
+
* Encodes a TCreditNote object into Factur-X XML
|
|
12
|
+
* @param creditNote TCreditNote object to encode
|
|
13
|
+
* @returns Factur-X XML string
|
|
14
|
+
*/
|
|
15
|
+
protected async encodeCreditNote(creditNote: TCreditNote): Promise<string> {
|
|
16
|
+
// Create base XML
|
|
17
|
+
const xmlDoc = this.createBaseXml();
|
|
18
|
+
|
|
19
|
+
// Set document type code to credit note (381)
|
|
20
|
+
this.setDocumentTypeCode(xmlDoc, '381');
|
|
21
|
+
|
|
22
|
+
// Add common invoice data
|
|
23
|
+
this.addCommonInvoiceData(xmlDoc, creditNote as unknown as TInvoice);
|
|
24
|
+
|
|
25
|
+
// Serialize to string
|
|
26
|
+
return new XMLSerializer().serializeToString(xmlDoc);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Encodes a TDebitNote object into Factur-X XML
|
|
31
|
+
* @param debitNote TDebitNote object to encode
|
|
32
|
+
* @returns Factur-X XML string
|
|
33
|
+
*/
|
|
34
|
+
protected async encodeDebitNote(debitNote: TDebitNote): Promise<string> {
|
|
35
|
+
// Create base XML
|
|
36
|
+
const xmlDoc = this.createBaseXml();
|
|
37
|
+
|
|
38
|
+
// Set document type code to invoice (380)
|
|
39
|
+
this.setDocumentTypeCode(xmlDoc, '380');
|
|
40
|
+
|
|
41
|
+
// Add common invoice data
|
|
42
|
+
this.addCommonInvoiceData(xmlDoc, debitNote as unknown as TInvoice);
|
|
43
|
+
|
|
44
|
+
// Serialize to string
|
|
45
|
+
return new XMLSerializer().serializeToString(xmlDoc);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a base Factur-X XML document
|
|
50
|
+
* @returns XML document with basic structure
|
|
51
|
+
*/
|
|
52
|
+
private createBaseXml(): Document {
|
|
53
|
+
// Create XML document from template
|
|
54
|
+
const xmlString = this.createXmlRoot();
|
|
55
|
+
const doc = new DOMParser().parseFromString(xmlString, 'application/xml');
|
|
56
|
+
|
|
57
|
+
// Add Factur-X profile
|
|
58
|
+
this.addProfile(doc);
|
|
59
|
+
|
|
60
|
+
return doc;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Adds Factur-X profile information to the XML document
|
|
65
|
+
* @param doc XML document
|
|
66
|
+
*/
|
|
67
|
+
private addProfile(doc: Document): void {
|
|
68
|
+
// Get root element
|
|
69
|
+
const root = doc.documentElement;
|
|
70
|
+
|
|
71
|
+
// Create context element if it doesn't exist
|
|
72
|
+
let contextElement = root.getElementsByTagName('rsm:ExchangedDocumentContext')[0];
|
|
73
|
+
if (!contextElement) {
|
|
74
|
+
contextElement = doc.createElement('rsm:ExchangedDocumentContext');
|
|
75
|
+
root.appendChild(contextElement);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Create guideline parameter element
|
|
79
|
+
const guidelineElement = doc.createElement('ram:GuidelineSpecifiedDocumentContextParameter');
|
|
80
|
+
contextElement.appendChild(guidelineElement);
|
|
81
|
+
|
|
82
|
+
// Add ID element with profile
|
|
83
|
+
const idElement = doc.createElement('ram:ID');
|
|
84
|
+
|
|
85
|
+
// Set profile based on the selected profile
|
|
86
|
+
let profileId = FACTURX_PROFILE_IDS.EN16931;
|
|
87
|
+
if (this.profile === 'BASIC') {
|
|
88
|
+
profileId = FACTURX_PROFILE_IDS.BASIC;
|
|
89
|
+
} else if (this.profile === 'MINIMUM') {
|
|
90
|
+
profileId = FACTURX_PROFILE_IDS.MINIMUM;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
idElement.textContent = profileId;
|
|
94
|
+
guidelineElement.appendChild(idElement);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Sets the document type code in the XML document
|
|
99
|
+
* @param doc XML document
|
|
100
|
+
* @param typeCode Document type code (380 for invoice, 381 for credit note)
|
|
101
|
+
*/
|
|
102
|
+
private setDocumentTypeCode(doc: Document, typeCode: string): void {
|
|
103
|
+
// Get root element
|
|
104
|
+
const root = doc.documentElement;
|
|
105
|
+
|
|
106
|
+
// Create document element if it doesn't exist
|
|
107
|
+
let documentElement = root.getElementsByTagName('rsm:ExchangedDocument')[0];
|
|
108
|
+
if (!documentElement) {
|
|
109
|
+
documentElement = doc.createElement('rsm:ExchangedDocument');
|
|
110
|
+
root.appendChild(documentElement);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Add type code element
|
|
114
|
+
const typeCodeElement = doc.createElement('ram:TypeCode');
|
|
115
|
+
typeCodeElement.textContent = typeCode;
|
|
116
|
+
documentElement.appendChild(typeCodeElement);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Adds common invoice data to the XML document
|
|
121
|
+
* @param doc XML document
|
|
122
|
+
* @param invoice Invoice data
|
|
123
|
+
*/
|
|
124
|
+
private addCommonInvoiceData(doc: Document, invoice: TInvoice): void {
|
|
125
|
+
// Get root element
|
|
126
|
+
const root = doc.documentElement;
|
|
127
|
+
|
|
128
|
+
// Get document element or create it
|
|
129
|
+
let documentElement = root.getElementsByTagName('rsm:ExchangedDocument')[0];
|
|
130
|
+
if (!documentElement) {
|
|
131
|
+
documentElement = doc.createElement('rsm:ExchangedDocument');
|
|
132
|
+
root.appendChild(documentElement);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add ID element
|
|
136
|
+
const idElement = doc.createElement('ram:ID');
|
|
137
|
+
idElement.textContent = invoice.id;
|
|
138
|
+
documentElement.appendChild(idElement);
|
|
139
|
+
|
|
140
|
+
// Add issue date element
|
|
141
|
+
const issueDateElement = doc.createElement('ram:IssueDateTime');
|
|
142
|
+
const dateStringElement = doc.createElement('udt:DateTimeString');
|
|
143
|
+
dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
|
|
144
|
+
dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.date);
|
|
145
|
+
issueDateElement.appendChild(dateStringElement);
|
|
146
|
+
documentElement.appendChild(issueDateElement);
|
|
147
|
+
|
|
148
|
+
// Add notes - include subject as first note if it exists
|
|
149
|
+
const allNotes: string[] = [];
|
|
150
|
+
if (invoice.subject && invoice.subject.trim()) {
|
|
151
|
+
allNotes.push(invoice.subject);
|
|
152
|
+
}
|
|
153
|
+
if (invoice.notes && invoice.notes.length > 0) {
|
|
154
|
+
allNotes.push(...invoice.notes);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Write all notes
|
|
158
|
+
for (const note of allNotes) {
|
|
159
|
+
const noteElement = doc.createElement('ram:IncludedNote');
|
|
160
|
+
const contentElement = doc.createElement('ram:Content');
|
|
161
|
+
contentElement.textContent = note;
|
|
162
|
+
noteElement.appendChild(contentElement);
|
|
163
|
+
documentElement.appendChild(noteElement);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Create transaction element if it doesn't exist
|
|
167
|
+
let transactionElement = root.getElementsByTagName('rsm:SupplyChainTradeTransaction')[0];
|
|
168
|
+
if (!transactionElement) {
|
|
169
|
+
transactionElement = doc.createElement('rsm:SupplyChainTradeTransaction');
|
|
170
|
+
root.appendChild(transactionElement);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Add agreement section with seller and buyer
|
|
174
|
+
this.addAgreementSection(doc, transactionElement, invoice);
|
|
175
|
+
|
|
176
|
+
// Add delivery section
|
|
177
|
+
this.addDeliverySection(doc, transactionElement, invoice);
|
|
178
|
+
|
|
179
|
+
// Add settlement section with payment terms and totals
|
|
180
|
+
this.addSettlementSection(doc, transactionElement, invoice);
|
|
181
|
+
|
|
182
|
+
// Add line items
|
|
183
|
+
this.addLineItems(doc, transactionElement, invoice);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Adds agreement section with seller and buyer information
|
|
188
|
+
* @param doc XML document
|
|
189
|
+
* @param transactionElement Transaction element
|
|
190
|
+
* @param invoice Invoice data
|
|
191
|
+
*/
|
|
192
|
+
private addAgreementSection(doc: Document, transactionElement: Element, invoice: TInvoice): void {
|
|
193
|
+
// Create agreement element
|
|
194
|
+
const agreementElement = doc.createElement('ram:ApplicableHeaderTradeAgreement');
|
|
195
|
+
transactionElement.appendChild(agreementElement);
|
|
196
|
+
|
|
197
|
+
// Add seller
|
|
198
|
+
const sellerElement = doc.createElement('ram:SellerTradeParty');
|
|
199
|
+
this.addPartyInfo(doc, sellerElement, invoice.from);
|
|
200
|
+
agreementElement.appendChild(sellerElement);
|
|
201
|
+
|
|
202
|
+
// Add buyer
|
|
203
|
+
const buyerElement = doc.createElement('ram:BuyerTradeParty');
|
|
204
|
+
this.addPartyInfo(doc, buyerElement, invoice.to);
|
|
205
|
+
agreementElement.appendChild(buyerElement);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Adds party information to an element
|
|
210
|
+
* @param doc XML document
|
|
211
|
+
* @param partyElement Party element
|
|
212
|
+
* @param party Party data
|
|
213
|
+
*/
|
|
214
|
+
private addPartyInfo(doc: Document, partyElement: Element, party: any): void {
|
|
215
|
+
// Add name
|
|
216
|
+
const nameElement = doc.createElement('ram:Name');
|
|
217
|
+
nameElement.textContent = party.name;
|
|
218
|
+
partyElement.appendChild(nameElement);
|
|
219
|
+
|
|
220
|
+
// Add postal address
|
|
221
|
+
const addressElement = doc.createElement('ram:PostalTradeAddress');
|
|
222
|
+
|
|
223
|
+
// Add address line 1 (street)
|
|
224
|
+
const line1Element = doc.createElement('ram:LineOne');
|
|
225
|
+
line1Element.textContent = party.address.streetName;
|
|
226
|
+
addressElement.appendChild(line1Element);
|
|
227
|
+
|
|
228
|
+
// Add address line 2 (house number)
|
|
229
|
+
const line2Element = doc.createElement('ram:LineTwo');
|
|
230
|
+
line2Element.textContent = party.address.houseNumber;
|
|
231
|
+
addressElement.appendChild(line2Element);
|
|
232
|
+
|
|
233
|
+
// Add postal code
|
|
234
|
+
const postalCodeElement = doc.createElement('ram:PostcodeCode');
|
|
235
|
+
postalCodeElement.textContent = party.address.postalCode;
|
|
236
|
+
addressElement.appendChild(postalCodeElement);
|
|
237
|
+
|
|
238
|
+
// Add city
|
|
239
|
+
const cityElement = doc.createElement('ram:CityName');
|
|
240
|
+
cityElement.textContent = party.address.city;
|
|
241
|
+
addressElement.appendChild(cityElement);
|
|
242
|
+
|
|
243
|
+
// Add country
|
|
244
|
+
const countryElement = doc.createElement('ram:CountryID');
|
|
245
|
+
countryElement.textContent = party.address.countryCode || party.address.country;
|
|
246
|
+
addressElement.appendChild(countryElement);
|
|
247
|
+
|
|
248
|
+
partyElement.appendChild(addressElement);
|
|
249
|
+
|
|
250
|
+
// Add VAT ID if available
|
|
251
|
+
if (party.registrationDetails && party.registrationDetails.vatId) {
|
|
252
|
+
const taxRegistrationElement = doc.createElement('ram:SpecifiedTaxRegistration');
|
|
253
|
+
const taxIdElement = doc.createElement('ram:ID');
|
|
254
|
+
taxIdElement.setAttribute('schemeID', 'VA');
|
|
255
|
+
taxIdElement.textContent = party.registrationDetails.vatId;
|
|
256
|
+
taxRegistrationElement.appendChild(taxIdElement);
|
|
257
|
+
partyElement.appendChild(taxRegistrationElement);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Add registration ID if available
|
|
261
|
+
if (party.registrationDetails && party.registrationDetails.registrationId) {
|
|
262
|
+
const regRegistrationElement = doc.createElement('ram:SpecifiedTaxRegistration');
|
|
263
|
+
const regIdElement = doc.createElement('ram:ID');
|
|
264
|
+
regIdElement.setAttribute('schemeID', 'FC');
|
|
265
|
+
regIdElement.textContent = party.registrationDetails.registrationId;
|
|
266
|
+
regRegistrationElement.appendChild(regIdElement);
|
|
267
|
+
partyElement.appendChild(regRegistrationElement);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Adds delivery section with delivery information
|
|
273
|
+
* @param doc XML document
|
|
274
|
+
* @param transactionElement Transaction element
|
|
275
|
+
* @param invoice Invoice data
|
|
276
|
+
*/
|
|
277
|
+
private addDeliverySection(doc: Document, transactionElement: Element, invoice: TInvoice): void {
|
|
278
|
+
// Create delivery element
|
|
279
|
+
const deliveryElement = doc.createElement('ram:ApplicableHeaderTradeDelivery');
|
|
280
|
+
transactionElement.appendChild(deliveryElement);
|
|
281
|
+
|
|
282
|
+
// Add delivery date if available
|
|
283
|
+
if (invoice.deliveryDate) {
|
|
284
|
+
const deliveryDateElement = doc.createElement('ram:ActualDeliverySupplyChainEvent');
|
|
285
|
+
const occurrenceDateElement = doc.createElement('ram:OccurrenceDateTime');
|
|
286
|
+
const dateStringElement = doc.createElement('udt:DateTimeString');
|
|
287
|
+
dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
|
|
288
|
+
dateStringElement.textContent = this.formatDateYYYYMMDD(invoice.deliveryDate);
|
|
289
|
+
occurrenceDateElement.appendChild(dateStringElement);
|
|
290
|
+
deliveryDateElement.appendChild(occurrenceDateElement);
|
|
291
|
+
deliveryElement.appendChild(deliveryDateElement);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Adds settlement section with payment terms and totals
|
|
297
|
+
* @param doc XML document
|
|
298
|
+
* @param transactionElement Transaction element
|
|
299
|
+
* @param invoice Invoice data
|
|
300
|
+
*/
|
|
301
|
+
private addSettlementSection(doc: Document, transactionElement: Element, invoice: TInvoice): void {
|
|
302
|
+
// Create settlement element
|
|
303
|
+
const settlementElement = doc.createElement('ram:ApplicableHeaderTradeSettlement');
|
|
304
|
+
transactionElement.appendChild(settlementElement);
|
|
305
|
+
|
|
306
|
+
// Add currency
|
|
307
|
+
const currencyElement = doc.createElement('ram:InvoiceCurrencyCode');
|
|
308
|
+
currencyElement.textContent = invoice.currency;
|
|
309
|
+
settlementElement.appendChild(currencyElement);
|
|
310
|
+
|
|
311
|
+
// Add payment terms
|
|
312
|
+
const paymentTermsElement = doc.createElement('ram:SpecifiedTradePaymentTerms');
|
|
313
|
+
|
|
314
|
+
// Add due date
|
|
315
|
+
const dueDateElement = doc.createElement('ram:DueDateDateTime');
|
|
316
|
+
const dateStringElement = doc.createElement('udt:DateTimeString');
|
|
317
|
+
dateStringElement.setAttribute('format', '102'); // YYYYMMDD format
|
|
318
|
+
|
|
319
|
+
// Calculate due date
|
|
320
|
+
const dueDate = new Date(invoice.date);
|
|
321
|
+
dueDate.setDate(dueDate.getDate() + invoice.dueInDays);
|
|
322
|
+
|
|
323
|
+
dateStringElement.textContent = this.formatDateYYYYMMDD(dueDate.getTime());
|
|
324
|
+
dueDateElement.appendChild(dateStringElement);
|
|
325
|
+
paymentTermsElement.appendChild(dueDateElement);
|
|
326
|
+
|
|
327
|
+
settlementElement.appendChild(paymentTermsElement);
|
|
328
|
+
|
|
329
|
+
// Add totals
|
|
330
|
+
const monetarySummationElement = doc.createElement('ram:SpecifiedTradeSettlementHeaderMonetarySummation');
|
|
331
|
+
|
|
332
|
+
// Calculate totals
|
|
333
|
+
let totalNetAmount = 0;
|
|
334
|
+
let totalTaxAmount = 0;
|
|
335
|
+
|
|
336
|
+
// Calculate from items
|
|
337
|
+
if (invoice.items) {
|
|
338
|
+
for (const item of invoice.items) {
|
|
339
|
+
const itemNetAmount = item.unitNetPrice * item.unitQuantity;
|
|
340
|
+
const itemTaxAmount = itemNetAmount * (item.vatPercentage / 100);
|
|
341
|
+
|
|
342
|
+
totalNetAmount += itemNetAmount;
|
|
343
|
+
totalTaxAmount += itemTaxAmount;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const totalGrossAmount = totalNetAmount + totalTaxAmount;
|
|
348
|
+
|
|
349
|
+
// Add line total amount
|
|
350
|
+
const lineTotalElement = doc.createElement('ram:LineTotalAmount');
|
|
351
|
+
lineTotalElement.textContent = totalNetAmount.toFixed(2);
|
|
352
|
+
monetarySummationElement.appendChild(lineTotalElement);
|
|
353
|
+
|
|
354
|
+
// Add tax total amount
|
|
355
|
+
const taxTotalElement = doc.createElement('ram:TaxTotalAmount');
|
|
356
|
+
taxTotalElement.textContent = totalTaxAmount.toFixed(2);
|
|
357
|
+
taxTotalElement.setAttribute('currencyID', invoice.currency);
|
|
358
|
+
monetarySummationElement.appendChild(taxTotalElement);
|
|
359
|
+
|
|
360
|
+
// Add grand total amount
|
|
361
|
+
const grandTotalElement = doc.createElement('ram:GrandTotalAmount');
|
|
362
|
+
grandTotalElement.textContent = totalGrossAmount.toFixed(2);
|
|
363
|
+
monetarySummationElement.appendChild(grandTotalElement);
|
|
364
|
+
|
|
365
|
+
// Add due payable amount
|
|
366
|
+
const duePayableElement = doc.createElement('ram:DuePayableAmount');
|
|
367
|
+
duePayableElement.textContent = totalGrossAmount.toFixed(2);
|
|
368
|
+
monetarySummationElement.appendChild(duePayableElement);
|
|
369
|
+
|
|
370
|
+
settlementElement.appendChild(monetarySummationElement);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Adds line items to the XML document
|
|
375
|
+
* @param doc XML document
|
|
376
|
+
* @param transactionElement Transaction element
|
|
377
|
+
* @param invoice Invoice data
|
|
378
|
+
*/
|
|
379
|
+
private addLineItems(doc: Document, transactionElement: Element, invoice: TInvoice): void {
|
|
380
|
+
// Add each line item
|
|
381
|
+
if (invoice.items) {
|
|
382
|
+
for (const item of invoice.items) {
|
|
383
|
+
// Create line item element
|
|
384
|
+
const lineItemElement = doc.createElement('ram:IncludedSupplyChainTradeLineItem');
|
|
385
|
+
|
|
386
|
+
// Add line ID
|
|
387
|
+
const lineIdElement = doc.createElement('ram:AssociatedDocumentLineDocument');
|
|
388
|
+
const lineIdValueElement = doc.createElement('ram:LineID');
|
|
389
|
+
lineIdValueElement.textContent = item.position.toString();
|
|
390
|
+
lineIdElement.appendChild(lineIdValueElement);
|
|
391
|
+
lineItemElement.appendChild(lineIdElement);
|
|
392
|
+
|
|
393
|
+
// Add product information
|
|
394
|
+
const productElement = doc.createElement('ram:SpecifiedTradeProduct');
|
|
395
|
+
|
|
396
|
+
// Add name
|
|
397
|
+
const nameElement = doc.createElement('ram:Name');
|
|
398
|
+
nameElement.textContent = item.name;
|
|
399
|
+
productElement.appendChild(nameElement);
|
|
400
|
+
|
|
401
|
+
// Add article number if available
|
|
402
|
+
if (item.articleNumber) {
|
|
403
|
+
const articleNumberElement = doc.createElement('ram:SellerAssignedID');
|
|
404
|
+
articleNumberElement.textContent = item.articleNumber;
|
|
405
|
+
productElement.appendChild(articleNumberElement);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
lineItemElement.appendChild(productElement);
|
|
409
|
+
|
|
410
|
+
// Add agreement information (price)
|
|
411
|
+
const agreementElement = doc.createElement('ram:SpecifiedLineTradeAgreement');
|
|
412
|
+
const priceElement = doc.createElement('ram:NetPriceProductTradePrice');
|
|
413
|
+
const chargeAmountElement = doc.createElement('ram:ChargeAmount');
|
|
414
|
+
chargeAmountElement.textContent = item.unitNetPrice.toFixed(2);
|
|
415
|
+
priceElement.appendChild(chargeAmountElement);
|
|
416
|
+
agreementElement.appendChild(priceElement);
|
|
417
|
+
lineItemElement.appendChild(agreementElement);
|
|
418
|
+
|
|
419
|
+
// Add delivery information (quantity)
|
|
420
|
+
const deliveryElement = doc.createElement('ram:SpecifiedLineTradeDelivery');
|
|
421
|
+
const quantityElement = doc.createElement('ram:BilledQuantity');
|
|
422
|
+
quantityElement.textContent = item.unitQuantity.toString();
|
|
423
|
+
quantityElement.setAttribute('unitCode', item.unitType);
|
|
424
|
+
deliveryElement.appendChild(quantityElement);
|
|
425
|
+
lineItemElement.appendChild(deliveryElement);
|
|
426
|
+
|
|
427
|
+
// Add settlement information (tax)
|
|
428
|
+
const settlementElement = doc.createElement('ram:SpecifiedLineTradeSettlement');
|
|
429
|
+
|
|
430
|
+
// Add tax information
|
|
431
|
+
const taxElement = doc.createElement('ram:ApplicableTradeTax');
|
|
432
|
+
|
|
433
|
+
// Add tax type code
|
|
434
|
+
const taxTypeCodeElement = doc.createElement('ram:TypeCode');
|
|
435
|
+
taxTypeCodeElement.textContent = 'VAT';
|
|
436
|
+
taxElement.appendChild(taxTypeCodeElement);
|
|
437
|
+
|
|
438
|
+
// Add tax category code
|
|
439
|
+
const taxCategoryCodeElement = doc.createElement('ram:CategoryCode');
|
|
440
|
+
taxCategoryCodeElement.textContent = 'S';
|
|
441
|
+
taxElement.appendChild(taxCategoryCodeElement);
|
|
442
|
+
|
|
443
|
+
// Add tax rate
|
|
444
|
+
const taxRateElement = doc.createElement('ram:RateApplicablePercent');
|
|
445
|
+
taxRateElement.textContent = item.vatPercentage.toString();
|
|
446
|
+
taxElement.appendChild(taxRateElement);
|
|
447
|
+
|
|
448
|
+
settlementElement.appendChild(taxElement);
|
|
449
|
+
|
|
450
|
+
// Add monetary summation
|
|
451
|
+
const monetarySummationElement = doc.createElement('ram:SpecifiedLineTradeSettlementMonetarySummation');
|
|
452
|
+
|
|
453
|
+
// Calculate item total
|
|
454
|
+
const itemNetAmount = item.unitNetPrice * item.unitQuantity;
|
|
455
|
+
|
|
456
|
+
// Add line total amount
|
|
457
|
+
const lineTotalElement = doc.createElement('ram:LineTotalAmount');
|
|
458
|
+
lineTotalElement.textContent = itemNetAmount.toFixed(2);
|
|
459
|
+
monetarySummationElement.appendChild(lineTotalElement);
|
|
460
|
+
|
|
461
|
+
settlementElement.appendChild(monetarySummationElement);
|
|
462
|
+
|
|
463
|
+
lineItemElement.appendChild(settlementElement);
|
|
464
|
+
|
|
465
|
+
// Add line item to transaction
|
|
466
|
+
transactionElement.appendChild(lineItemElement);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Formats a date as YYYYMMDD
|
|
473
|
+
* @param timestamp Timestamp to format
|
|
474
|
+
* @returns Formatted date string
|
|
475
|
+
*/
|
|
476
|
+
private formatDateYYYYMMDD(timestamp: number): string {
|
|
477
|
+
const date = new Date(timestamp);
|
|
478
|
+
const year = date.getFullYear();
|
|
479
|
+
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
480
|
+
const day = date.getDate().toString().padStart(2, '0');
|
|
481
|
+
return `${year}${month}${day}`;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CIIProfile, CII_PROFILE_IDS } from '../cii.types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Factur-X specific constants and types
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Factur-X profile IDs
|
|
8
|
+
export const FACTURX_PROFILE_IDS = {
|
|
9
|
+
MINIMUM: CII_PROFILE_IDS.FACTURX_MINIMUM,
|
|
10
|
+
BASIC: CII_PROFILE_IDS.FACTURX_BASIC,
|
|
11
|
+
EN16931: CII_PROFILE_IDS.FACTURX_EN16931
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Factur-X PDF attachment filename
|
|
15
|
+
export const FACTURX_ATTACHMENT_FILENAME = 'factur-x.xml';
|
|
16
|
+
|
|
17
|
+
// Factur-X PDF attachment description
|
|
18
|
+
export const FACTURX_ATTACHMENT_DESCRIPTION = 'Factur-X XML Invoice';
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { CIIBaseValidator } from '../cii.validator.js';
|
|
2
|
+
import { ValidationLevel } from '../../../interfaces/common.js';
|
|
3
|
+
import type { ValidationResult } from '../../../interfaces/common.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validator for Factur-X invoice format
|
|
7
|
+
* Implements validation rules according to EN16931 and Factur-X specification
|
|
8
|
+
*/
|
|
9
|
+
export class FacturXValidator extends CIIBaseValidator {
|
|
10
|
+
/**
|
|
11
|
+
* Validates structure of the Factur-X XML document
|
|
12
|
+
* @returns True if structure validation passed
|
|
13
|
+
*/
|
|
14
|
+
protected validateStructure(): boolean {
|
|
15
|
+
if (!this.doc) return false;
|
|
16
|
+
|
|
17
|
+
let valid = true;
|
|
18
|
+
|
|
19
|
+
// Check for required main sections
|
|
20
|
+
const sections = [
|
|
21
|
+
'rsm:ExchangedDocumentContext',
|
|
22
|
+
'rsm:ExchangedDocument',
|
|
23
|
+
'rsm:SupplyChainTradeTransaction'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
for (const section of sections) {
|
|
27
|
+
if (!this.exists(section)) {
|
|
28
|
+
this.addError('FX-STRUCT-1', `Required section ${section} is missing`, '/rsm:CrossIndustryInvoice');
|
|
29
|
+
valid = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check for SupplyChainTradeTransaction sections
|
|
34
|
+
if (this.exists('rsm:SupplyChainTradeTransaction')) {
|
|
35
|
+
const tradeSubsections = [
|
|
36
|
+
'ram:ApplicableHeaderTradeAgreement',
|
|
37
|
+
'ram:ApplicableHeaderTradeDelivery',
|
|
38
|
+
'ram:ApplicableHeaderTradeSettlement'
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const subsection of tradeSubsections) {
|
|
42
|
+
if (!this.exists(`rsm:SupplyChainTradeTransaction/${subsection}`)) {
|
|
43
|
+
this.addError('FX-STRUCT-2', `Required subsection ${subsection} is missing`,
|
|
44
|
+
'/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction');
|
|
45
|
+
valid = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return valid;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validates business rules
|
|
55
|
+
* @returns True if business rule validation passed
|
|
56
|
+
*/
|
|
57
|
+
protected validateBusinessRules(): boolean {
|
|
58
|
+
if (!this.doc) return false;
|
|
59
|
+
|
|
60
|
+
let valid = true;
|
|
61
|
+
|
|
62
|
+
// BR-16: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113)
|
|
63
|
+
valid = this.validateAmounts() && valid;
|
|
64
|
+
|
|
65
|
+
// BR-CO-3: Value added tax point date (BT-7) and Value added tax point date code (BT-8) are mutually exclusive
|
|
66
|
+
valid = this.validateMutuallyExclusiveFields() && valid;
|
|
67
|
+
|
|
68
|
+
// BR-S-1: An Invoice that contains a line (BG-25) where the Invoiced item VAT category code (BT-151) is "Standard rated"
|
|
69
|
+
// shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32)
|
|
70
|
+
// and/or the Seller tax representative VAT identifier (BT-63).
|
|
71
|
+
valid = this.validateSellerVatIdentifier() && valid;
|
|
72
|
+
|
|
73
|
+
return valid;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Validates amount calculations in the invoice
|
|
78
|
+
* @returns True if amount validation passed
|
|
79
|
+
*/
|
|
80
|
+
private validateAmounts(): boolean {
|
|
81
|
+
if (!this.doc) return false;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
// Extract amounts
|
|
85
|
+
const totalAmount = this.getNumber(
|
|
86
|
+
'//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:GrandTotalAmount'
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const paidAmount = this.getNumber(
|
|
90
|
+
'//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:TotalPrepaidAmount'
|
|
91
|
+
) || 0;
|
|
92
|
+
|
|
93
|
+
const dueAmount = this.getNumber(
|
|
94
|
+
'//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:DuePayableAmount'
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Calculate expected due amount
|
|
98
|
+
const expectedDueAmount = totalAmount - paidAmount;
|
|
99
|
+
|
|
100
|
+
// Compare with a small tolerance for rounding errors
|
|
101
|
+
if (Math.abs(dueAmount - expectedDueAmount) > 0.01) {
|
|
102
|
+
this.addError(
|
|
103
|
+
'BR-16',
|
|
104
|
+
`Amount due for payment (${dueAmount}) must equal Invoice total amount with VAT (${totalAmount}) - Paid amount (${paidAmount})`,
|
|
105
|
+
'//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation'
|
|
106
|
+
);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.addError('FX-AMOUNT', `Error validating amounts: ${error}`, '/');
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validates mutually exclusive fields
|
|
119
|
+
* @returns True if validation passed
|
|
120
|
+
*/
|
|
121
|
+
private validateMutuallyExclusiveFields(): boolean {
|
|
122
|
+
if (!this.doc) return false;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Check for VAT point date and code (BR-CO-3)
|
|
126
|
+
const vatPointDate = this.exists('//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:TaxPointDate');
|
|
127
|
+
const vatPointDateCode = this.exists('//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:DueDateTypeCode');
|
|
128
|
+
|
|
129
|
+
if (vatPointDate && vatPointDateCode) {
|
|
130
|
+
this.addError(
|
|
131
|
+
'BR-CO-3',
|
|
132
|
+
'Value added tax point date and Value added tax point date code are mutually exclusive',
|
|
133
|
+
'//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax'
|
|
134
|
+
);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return true;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
this.addError('FX-MUTUAL', `Error validating mutually exclusive fields: ${error}`, '/');
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Validates seller VAT identifier requirements
|
|
147
|
+
* @returns True if validation passed
|
|
148
|
+
*/
|
|
149
|
+
private validateSellerVatIdentifier(): boolean {
|
|
150
|
+
if (!this.doc) return false;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// Check if there are any standard rated line items
|
|
154
|
+
const standardRatedItems = this.exists(
|
|
155
|
+
'//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:CategoryCode[text()="S"]'
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
if (standardRatedItems) {
|
|
159
|
+
// Check for seller VAT identifier
|
|
160
|
+
const sellerVatId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]');
|
|
161
|
+
const sellerTaxId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]');
|
|
162
|
+
const sellerTaxRepId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTaxRepresentativeTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]');
|
|
163
|
+
|
|
164
|
+
if (!sellerVatId && !sellerTaxId && !sellerTaxRepId) {
|
|
165
|
+
this.addError(
|
|
166
|
+
'BR-S-1',
|
|
167
|
+
'An Invoice with standard rated items must contain the Seller VAT Identifier, Tax registration identifier or Tax representative VAT identifier',
|
|
168
|
+
'//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty'
|
|
169
|
+
);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return true;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.addError('FX-VAT', `Error validating seller VAT identifier: ${error}`, '/');
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|