@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,293 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* A class to convert a given ILetter with invoice data
|
|
4
|
+
* into a Factur-X compliant XML (also compatible with ZUGFeRD and EN16931).
|
|
5
|
+
*
|
|
6
|
+
* Factur-X is the French implementation of the European e-invoicing standard EN16931,
|
|
7
|
+
* which is also implemented in Germany as ZUGFeRD. Both formats are based on
|
|
8
|
+
* UN/CEFACT Cross Industry Invoice (CII) XML schemas.
|
|
9
|
+
*/
|
|
10
|
+
export class FacturXEncoder {
|
|
11
|
+
constructor() { }
|
|
12
|
+
/**
|
|
13
|
+
* Alias for createFacturXXml to maintain backward compatibility
|
|
14
|
+
*/
|
|
15
|
+
createZugferdXml(letterArg) {
|
|
16
|
+
return this.createFacturXXml(letterArg);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a Factur-X compliant XML based on the provided letter data.
|
|
20
|
+
* This XML is also compliant with ZUGFeRD and EN16931 standards.
|
|
21
|
+
*/
|
|
22
|
+
createFacturXXml(letterArg) {
|
|
23
|
+
// 1) Get your "SmartXml" or "xmlbuilder2" instance
|
|
24
|
+
const smartxmlInstance = new plugins.smartxml.SmartXml();
|
|
25
|
+
if (!letterArg?.content?.invoiceData) {
|
|
26
|
+
throw new Error('Letter does not contain invoice data.');
|
|
27
|
+
}
|
|
28
|
+
const invoice = letterArg.content.invoiceData;
|
|
29
|
+
const billedBy = invoice.billedBy;
|
|
30
|
+
const billedTo = invoice.billedTo;
|
|
31
|
+
// 2) Start building the document
|
|
32
|
+
const doc = smartxmlInstance
|
|
33
|
+
.create({ version: '1.0', encoding: 'UTF-8' })
|
|
34
|
+
.ele('rsm:CrossIndustryInvoice', {
|
|
35
|
+
'xmlns:rsm': 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100',
|
|
36
|
+
'xmlns:udt': 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100',
|
|
37
|
+
'xmlns:qdt': 'urn:un:unece:uncefact:data:standard:QualifiedDataType:100',
|
|
38
|
+
'xmlns:ram': 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'
|
|
39
|
+
});
|
|
40
|
+
// 3) Exchanged Document Context
|
|
41
|
+
const docContext = doc.ele('rsm:ExchangedDocumentContext');
|
|
42
|
+
// Add test indicator
|
|
43
|
+
docContext.ele('ram:TestIndicator')
|
|
44
|
+
.ele('udt:Indicator')
|
|
45
|
+
.txt(this.isDraft(letterArg) ? 'true' : 'false')
|
|
46
|
+
.up()
|
|
47
|
+
.up();
|
|
48
|
+
// Add Factur-X profile information
|
|
49
|
+
// EN16931 profile is compliant with both Factur-X and ZUGFeRD
|
|
50
|
+
docContext.ele('ram:GuidelineSpecifiedDocumentContextParameter')
|
|
51
|
+
.ele('ram:ID')
|
|
52
|
+
.txt('urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:en16931')
|
|
53
|
+
.up()
|
|
54
|
+
.up();
|
|
55
|
+
docContext.up(); // </rsm:ExchangedDocumentContext>
|
|
56
|
+
// 4) Exchanged Document (Invoice Header Info)
|
|
57
|
+
const exchangedDoc = doc.ele('rsm:ExchangedDocument');
|
|
58
|
+
// Invoice ID
|
|
59
|
+
exchangedDoc.ele('ram:ID').txt(invoice.id).up();
|
|
60
|
+
// Document type code
|
|
61
|
+
// 380 = commercial invoice, 381 = credit note
|
|
62
|
+
const documentTypeCode = invoice.type === 'creditnote' ? '381' : '380';
|
|
63
|
+
exchangedDoc.ele('ram:TypeCode').txt(documentTypeCode).up();
|
|
64
|
+
// Issue date
|
|
65
|
+
exchangedDoc
|
|
66
|
+
.ele('ram:IssueDateTime')
|
|
67
|
+
.ele('udt:DateTimeString', { format: '102' })
|
|
68
|
+
// Format 'YYYYMMDD' as per Factur-X specification
|
|
69
|
+
.txt(this.formatDate(letterArg.date))
|
|
70
|
+
.up()
|
|
71
|
+
.up();
|
|
72
|
+
// Document name - Factur-X recommended field
|
|
73
|
+
const documentName = invoice.type === 'creditnote' ? 'CREDIT NOTE' : 'INVOICE';
|
|
74
|
+
exchangedDoc.ele('ram:Name').txt(documentName).up();
|
|
75
|
+
// Optional: Add language indicator (recommended for Factur-X)
|
|
76
|
+
// Use document language if specified, default to 'en'
|
|
77
|
+
const languageCode = letterArg.language?.toUpperCase() || 'EN';
|
|
78
|
+
exchangedDoc
|
|
79
|
+
.ele('ram:IncludedNote')
|
|
80
|
+
.ele('ram:Content').txt('Invoice created with Factur-X compliant software').up()
|
|
81
|
+
.ele('ram:SubjectCode').txt('REG').up() // REG = regulatory information
|
|
82
|
+
.up();
|
|
83
|
+
exchangedDoc.up(); // </rsm:ExchangedDocument>
|
|
84
|
+
// 5) Supply Chain Trade Transaction
|
|
85
|
+
const supplyChainEle = doc.ele('rsm:SupplyChainTradeTransaction');
|
|
86
|
+
// 5.1) Included Supply Chain Trade Line Items
|
|
87
|
+
invoice.items.forEach((item) => {
|
|
88
|
+
const lineItemEle = supplyChainEle.ele('ram:IncludedSupplyChainTradeLineItem');
|
|
89
|
+
lineItemEle.ele('ram:SpecifiedTradeProduct')
|
|
90
|
+
.ele('ram:Name')
|
|
91
|
+
.txt(item.name)
|
|
92
|
+
.up()
|
|
93
|
+
.up(); // </ram:SpecifiedTradeProduct>
|
|
94
|
+
lineItemEle.ele('ram:SpecifiedLineTradeAgreement')
|
|
95
|
+
.ele('ram:GrossPriceProductTradePrice')
|
|
96
|
+
.ele('ram:ChargeAmount')
|
|
97
|
+
.txt(item.unitNetPrice.toFixed(2))
|
|
98
|
+
.up()
|
|
99
|
+
.up()
|
|
100
|
+
.up(); // </ram:SpecifiedLineTradeAgreement>
|
|
101
|
+
lineItemEle.ele('ram:SpecifiedLineTradeDelivery')
|
|
102
|
+
.ele('ram:BilledQuantity')
|
|
103
|
+
.txt(item.unitQuantity.toString())
|
|
104
|
+
.up()
|
|
105
|
+
.up(); // </ram:SpecifiedLineTradeDelivery>
|
|
106
|
+
lineItemEle.ele('ram:SpecifiedLineTradeSettlement')
|
|
107
|
+
.ele('ram:ApplicableTradeTax')
|
|
108
|
+
.ele('ram:RateApplicablePercent')
|
|
109
|
+
.txt(item.vatPercentage.toFixed(2))
|
|
110
|
+
.up()
|
|
111
|
+
.up()
|
|
112
|
+
.ele('ram:SpecifiedTradeSettlementLineMonetarySummation')
|
|
113
|
+
.ele('ram:LineTotalAmount')
|
|
114
|
+
.txt((item.unitQuantity *
|
|
115
|
+
item.unitNetPrice *
|
|
116
|
+
(1 + item.vatPercentage / 100)).toFixed(2))
|
|
117
|
+
.up()
|
|
118
|
+
.up()
|
|
119
|
+
.up(); // </ram:SpecifiedLineTradeSettlement>
|
|
120
|
+
});
|
|
121
|
+
// 5.2) Applicable Header Trade Agreement
|
|
122
|
+
const headerTradeAgreementEle = supplyChainEle.ele('ram:ApplicableHeaderTradeAgreement');
|
|
123
|
+
// Seller
|
|
124
|
+
const sellerPartyEle = headerTradeAgreementEle.ele('ram:SellerTradeParty');
|
|
125
|
+
sellerPartyEle.ele('ram:Name').txt(billedBy.name).up();
|
|
126
|
+
// Example: If it's a company, put company name, etc.
|
|
127
|
+
const sellerAddressEle = sellerPartyEle.ele('ram:PostalTradeAddress');
|
|
128
|
+
sellerAddressEle.ele('ram:PostcodeCode').txt(billedBy.address.postalCode).up();
|
|
129
|
+
sellerAddressEle.ele('ram:LineOne').txt(billedBy.address.streetName).up();
|
|
130
|
+
sellerAddressEle.ele('ram:CityName').txt(billedBy.address.city).up();
|
|
131
|
+
// Typically you'd include 'ram:CountryID' with ISO2 code, e.g. "DE"
|
|
132
|
+
sellerAddressEle.up(); // </ram:PostalTradeAddress>
|
|
133
|
+
sellerPartyEle.up(); // </ram:SellerTradeParty>
|
|
134
|
+
// Buyer
|
|
135
|
+
const buyerPartyEle = headerTradeAgreementEle.ele('ram:BuyerTradeParty');
|
|
136
|
+
buyerPartyEle.ele('ram:Name').txt(billedTo.name).up();
|
|
137
|
+
const buyerAddressEle = buyerPartyEle.ele('ram:PostalTradeAddress');
|
|
138
|
+
buyerAddressEle.ele('ram:PostcodeCode').txt(billedTo.address.postalCode).up();
|
|
139
|
+
buyerAddressEle.ele('ram:LineOne').txt(billedTo.address.streetName).up();
|
|
140
|
+
buyerAddressEle.ele('ram:CityName').txt(billedTo.address.city).up();
|
|
141
|
+
buyerAddressEle.up(); // </ram:PostalTradeAddress>
|
|
142
|
+
buyerPartyEle.up(); // </ram:BuyerTradeParty>
|
|
143
|
+
headerTradeAgreementEle.up(); // </ram:ApplicableHeaderTradeAgreement>
|
|
144
|
+
// 5.3) Applicable Header Trade Delivery
|
|
145
|
+
const headerTradeDeliveryEle = supplyChainEle.ele('ram:ApplicableHeaderTradeDelivery');
|
|
146
|
+
const actualDeliveryEle = headerTradeDeliveryEle.ele('ram:ActualDeliverySupplyChainEvent');
|
|
147
|
+
const occurrenceEle = actualDeliveryEle.ele('ram:OccurrenceDateTime')
|
|
148
|
+
.ele('udt:DateTimeString', { format: '102' });
|
|
149
|
+
const deliveryDate = invoice.deliveryDate || letterArg.date;
|
|
150
|
+
occurrenceEle.txt(this.formatDate(deliveryDate)).up();
|
|
151
|
+
actualDeliveryEle.up(); // </ram:ActualDeliverySupplyChainEvent>
|
|
152
|
+
headerTradeDeliveryEle.up(); // </ram:ApplicableHeaderTradeDelivery>
|
|
153
|
+
// 5.4) Applicable Header Trade Settlement
|
|
154
|
+
const headerTradeSettlementEle = supplyChainEle.ele('ram:ApplicableHeaderTradeSettlement');
|
|
155
|
+
// Tax currency code, doc currency code, etc.
|
|
156
|
+
headerTradeSettlementEle.ele('ram:InvoiceCurrencyCode').txt(invoice.currency).up();
|
|
157
|
+
// Example single tax breakdown
|
|
158
|
+
const tradeTaxEle = headerTradeSettlementEle.ele('ram:ApplicableTradeTax');
|
|
159
|
+
tradeTaxEle.ele('ram:TypeCode').txt('VAT').up();
|
|
160
|
+
tradeTaxEle.ele('ram:CalculatedAmount').txt(this.sumAllVat(invoice).toFixed(2)).up();
|
|
161
|
+
tradeTaxEle
|
|
162
|
+
.ele('ram:RateApplicablePercent')
|
|
163
|
+
.txt(this.extractMainVatRate(invoice.items).toFixed(2))
|
|
164
|
+
.up();
|
|
165
|
+
tradeTaxEle.up(); // </ram:ApplicableTradeTax>
|
|
166
|
+
// Payment Terms
|
|
167
|
+
const paymentTermsEle = headerTradeSettlementEle.ele('ram:SpecifiedTradePaymentTerms');
|
|
168
|
+
// Payment description
|
|
169
|
+
paymentTermsEle.ele('ram:Description').txt(`Payment due in ${invoice.dueInDays} days.`).up();
|
|
170
|
+
// Due date calculation
|
|
171
|
+
const dueDate = new Date(letterArg.date);
|
|
172
|
+
dueDate.setDate(dueDate.getDate() + invoice.dueInDays);
|
|
173
|
+
// Add due date as per Factur-X spec
|
|
174
|
+
paymentTermsEle
|
|
175
|
+
.ele('ram:DueDateDateTime')
|
|
176
|
+
.ele('udt:DateTimeString', { format: '102' })
|
|
177
|
+
.txt(this.formatDate(dueDate.getTime()))
|
|
178
|
+
.up()
|
|
179
|
+
.up();
|
|
180
|
+
// Add payment means if available
|
|
181
|
+
if (invoice.billedBy.sepaConnection) {
|
|
182
|
+
// Add SEPA information as per Factur-X standard
|
|
183
|
+
const paymentMeans = headerTradeSettlementEle.ele('ram:SpecifiedTradeSettlementPaymentMeans');
|
|
184
|
+
paymentMeans.ele('ram:TypeCode').txt('58').up(); // 58 = SEPA credit transfer
|
|
185
|
+
// Payment reference (for bank statement reconciliation)
|
|
186
|
+
paymentMeans.ele('ram:Information').txt(`Reference: ${invoice.id}`).up();
|
|
187
|
+
// Payee account (IBAN)
|
|
188
|
+
if (invoice.billedBy.sepaConnection.iban) {
|
|
189
|
+
const payeeAccount = paymentMeans.ele('ram:PayeePartyCreditorFinancialAccount');
|
|
190
|
+
payeeAccount.ele('ram:IBANID').txt(invoice.billedBy.sepaConnection.iban).up();
|
|
191
|
+
payeeAccount.up();
|
|
192
|
+
}
|
|
193
|
+
// Bank BIC
|
|
194
|
+
if (invoice.billedBy.sepaConnection.bic) {
|
|
195
|
+
const payeeBank = paymentMeans.ele('ram:PayeeSpecifiedCreditorFinancialInstitution');
|
|
196
|
+
payeeBank.ele('ram:BICID').txt(invoice.billedBy.sepaConnection.bic).up();
|
|
197
|
+
payeeBank.up();
|
|
198
|
+
}
|
|
199
|
+
paymentMeans.up();
|
|
200
|
+
}
|
|
201
|
+
paymentTermsEle.up(); // </ram:SpecifiedTradePaymentTerms>
|
|
202
|
+
// Monetary Summation
|
|
203
|
+
const monetarySummationEle = headerTradeSettlementEle.ele('ram:SpecifiedTradeSettlementHeaderMonetarySummation');
|
|
204
|
+
monetarySummationEle
|
|
205
|
+
.ele('ram:LineTotalAmount')
|
|
206
|
+
.txt(this.calcLineTotalNet(invoice).toFixed(2))
|
|
207
|
+
.up();
|
|
208
|
+
monetarySummationEle
|
|
209
|
+
.ele('ram:TaxTotalAmount')
|
|
210
|
+
.txt(this.sumAllVat(invoice).toFixed(2))
|
|
211
|
+
.up();
|
|
212
|
+
monetarySummationEle
|
|
213
|
+
.ele('ram:GrandTotalAmount')
|
|
214
|
+
.txt(this.calcGrandTotal(invoice).toFixed(2))
|
|
215
|
+
.up();
|
|
216
|
+
monetarySummationEle.up(); // </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
|
217
|
+
headerTradeSettlementEle.up(); // </ram:ApplicableHeaderTradeSettlement>
|
|
218
|
+
supplyChainEle.up(); // </rsm:SupplyChainTradeTransaction>
|
|
219
|
+
doc.up(); // </rsm:CrossIndustryInvoice>
|
|
220
|
+
// 6) Return the final XML string
|
|
221
|
+
return doc.end({ prettyPrint: true });
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Helper: Determine if the letter is in draft or final.
|
|
225
|
+
*/
|
|
226
|
+
isDraft(letterArg) {
|
|
227
|
+
return letterArg.versionInfo?.type === 'draft';
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Helper: Format date to certain patterns (very minimal example).
|
|
231
|
+
* e.g. 'yyyyMMdd' => '20231231'
|
|
232
|
+
*/
|
|
233
|
+
formatDate(timestampMs) {
|
|
234
|
+
const date = new Date(timestampMs);
|
|
235
|
+
const yyyy = date.getFullYear();
|
|
236
|
+
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
237
|
+
const dd = String(date.getDate()).padStart(2, '0');
|
|
238
|
+
return `${yyyy}${mm}${dd}`;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Helper: Map your custom 'unitType' to an ISO code or similar.
|
|
242
|
+
*/
|
|
243
|
+
mapUnitType(unitType) {
|
|
244
|
+
switch (unitType.toLowerCase()) {
|
|
245
|
+
case 'hour':
|
|
246
|
+
return 'HUR';
|
|
247
|
+
case 'piece':
|
|
248
|
+
return 'C62';
|
|
249
|
+
default:
|
|
250
|
+
return 'C62'; // fallback
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Example: Sum all VAT amounts from items.
|
|
255
|
+
*/
|
|
256
|
+
sumAllVat(invoice) {
|
|
257
|
+
return invoice.items.reduce((acc, item) => {
|
|
258
|
+
const net = item.unitNetPrice * item.unitQuantity;
|
|
259
|
+
const vat = net * (item.vatPercentage / 100);
|
|
260
|
+
return acc + vat;
|
|
261
|
+
}, 0);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Example: Extract main (or highest) VAT rate from items as representative.
|
|
265
|
+
* In reality, you might list multiple 'ApplicableTradeTax' blocks by group.
|
|
266
|
+
*/
|
|
267
|
+
extractMainVatRate(items) {
|
|
268
|
+
let max = 0;
|
|
269
|
+
items.forEach((item) => {
|
|
270
|
+
if (item.vatPercentage > max)
|
|
271
|
+
max = item.vatPercentage;
|
|
272
|
+
});
|
|
273
|
+
return max;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Example: Sum net amounts (without VAT).
|
|
277
|
+
*/
|
|
278
|
+
calcLineTotalNet(invoice) {
|
|
279
|
+
return invoice.items.reduce((acc, item) => {
|
|
280
|
+
const net = item.unitNetPrice * item.unitQuantity;
|
|
281
|
+
return acc + net;
|
|
282
|
+
}, 0);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Example: net + VAT = grand total
|
|
286
|
+
*/
|
|
287
|
+
calcGrandTotal(invoice) {
|
|
288
|
+
const net = this.calcLineTotalNet(invoice);
|
|
289
|
+
const vat = this.sumAllVat(invoice);
|
|
290
|
+
return net + vat;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdHVyeC5lbmNvZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvZm9ybWF0cy9mYWN0dXJ4LmVuY29kZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekM7Ozs7Ozs7R0FPRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBRXpCLGdCQUFlLENBQUM7SUFFaEI7O09BRUc7SUFDSSxnQkFBZ0IsQ0FBQyxTQUEyQztRQUNqRSxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMxQyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksZ0JBQWdCLENBQUMsU0FBMkM7UUFDakUsbURBQW1EO1FBQ25ELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBRXpELElBQUksQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxDQUFDO1lBQ3JDLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQztRQUMzRCxDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQXFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDO1FBQ2hGLE1BQU0sUUFBUSxHQUFzQyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ3JFLE1BQU0sUUFBUSxHQUFzQyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBRXJFLGlDQUFpQztRQUNqQyxNQUFNLEdBQUcsR0FBRyxnQkFBZ0I7YUFDekIsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUM7YUFDN0MsR0FBRyxDQUFDLDBCQUEwQixFQUFFO1lBQy9CLFdBQVcsRUFBRSw4REFBOEQ7WUFDM0UsV0FBVyxFQUFFLDZEQUE2RDtZQUMxRSxXQUFXLEVBQUUsMkRBQTJEO1lBQ3hFLFdBQVcsRUFBRSxvRkFBb0Y7U0FDbEcsQ0FB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
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { BaseValidator } from './base.validator.js';
|
|
2
|
+
import { ValidationLevel } from '../interfaces.js';
|
|
3
|
+
import type { ValidationResult } from '../interfaces.js';
|
|
4
|
+
/**
|
|
5
|
+
* Validator for Factur-X/ZUGFeRD invoice format
|
|
6
|
+
* Implements validation rules according to EN16931 and Factur-X specification
|
|
7
|
+
*/
|
|
8
|
+
export declare class FacturXValidator extends BaseValidator {
|
|
9
|
+
private static NS_RSMT;
|
|
10
|
+
private static NS_RAM;
|
|
11
|
+
private static NS_UDT;
|
|
12
|
+
private xmlDoc;
|
|
13
|
+
private profile;
|
|
14
|
+
constructor(xml: string);
|
|
15
|
+
/**
|
|
16
|
+
* Validates the Factur-X invoice against the specified level
|
|
17
|
+
* @param level Validation level
|
|
18
|
+
* @returns Validation result
|
|
19
|
+
*/
|
|
20
|
+
validate(level?: ValidationLevel): ValidationResult;
|
|
21
|
+
/**
|
|
22
|
+
* Validates XML against schema
|
|
23
|
+
* @returns True if schema validation passed
|
|
24
|
+
*/
|
|
25
|
+
protected validateSchema(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Validates structure of the XML document
|
|
28
|
+
* @returns True if structure validation passed
|
|
29
|
+
*/
|
|
30
|
+
private validateStructure;
|
|
31
|
+
/**
|
|
32
|
+
* Validates business rules
|
|
33
|
+
* @returns True if business rule validation passed
|
|
34
|
+
*/
|
|
35
|
+
protected validateBusinessRules(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Detects Factur-X profile from the XML
|
|
38
|
+
*/
|
|
39
|
+
private detectProfile;
|
|
40
|
+
/**
|
|
41
|
+
* Validates amount calculations in the invoice
|
|
42
|
+
* @returns True if amount validation passed
|
|
43
|
+
*/
|
|
44
|
+
private validateAmounts;
|
|
45
|
+
/**
|
|
46
|
+
* Validates mutually exclusive fields
|
|
47
|
+
* @returns True if validation passed
|
|
48
|
+
*/
|
|
49
|
+
private validateMutuallyExclusiveFields;
|
|
50
|
+
/**
|
|
51
|
+
* Validates seller VAT identifier requirements
|
|
52
|
+
* @returns True if validation passed
|
|
53
|
+
*/
|
|
54
|
+
private validateSellerVatIdentifier;
|
|
55
|
+
/**
|
|
56
|
+
* Helper method to check if a node exists
|
|
57
|
+
* @param xpathExpression XPath to check
|
|
58
|
+
* @returns True if node exists
|
|
59
|
+
*/
|
|
60
|
+
private exists;
|
|
61
|
+
/**
|
|
62
|
+
* Helper method to get a number value from XPath
|
|
63
|
+
* @param xpathExpression XPath to get number from
|
|
64
|
+
* @returns Number value or NaN if not found
|
|
65
|
+
*/
|
|
66
|
+
private getNumberValue;
|
|
67
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { BaseValidator } from './base.validator.js';
|
|
2
|
+
import { ValidationLevel } from '../interfaces.js';
|
|
3
|
+
import * as xpath from 'xpath';
|
|
4
|
+
import { DOMParser } from 'xmldom';
|
|
5
|
+
/**
|
|
6
|
+
* Validator for Factur-X/ZUGFeRD invoice format
|
|
7
|
+
* Implements validation rules according to EN16931 and Factur-X specification
|
|
8
|
+
*/
|
|
9
|
+
export class FacturXValidator extends BaseValidator {
|
|
10
|
+
// XML namespaces for Factur-X/ZUGFeRD
|
|
11
|
+
static { this.NS_RSMT = 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'; }
|
|
12
|
+
static { this.NS_RAM = 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'; }
|
|
13
|
+
static { this.NS_UDT = 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'; }
|
|
14
|
+
constructor(xml) {
|
|
15
|
+
super(xml);
|
|
16
|
+
// XML document for processing
|
|
17
|
+
this.xmlDoc = null;
|
|
18
|
+
// Factur-X profile (BASIC, EN16931, EXTENDED, etc.)
|
|
19
|
+
this.profile = '';
|
|
20
|
+
try {
|
|
21
|
+
// Parse XML document
|
|
22
|
+
this.xmlDoc = new DOMParser().parseFromString(xml, 'application/xml');
|
|
23
|
+
// Determine Factur-X profile
|
|
24
|
+
this.detectProfile();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
this.addError('FX-PARSE', `Failed to parse XML: ${error}`, '/');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validates the Factur-X invoice against the specified level
|
|
32
|
+
* @param level Validation level
|
|
33
|
+
* @returns Validation result
|
|
34
|
+
*/
|
|
35
|
+
validate(level = ValidationLevel.SYNTAX) {
|
|
36
|
+
// Reset errors
|
|
37
|
+
this.errors = [];
|
|
38
|
+
// Check if document was parsed successfully
|
|
39
|
+
if (!this.xmlDoc) {
|
|
40
|
+
return {
|
|
41
|
+
valid: false,
|
|
42
|
+
errors: this.errors,
|
|
43
|
+
level: level
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Perform validation based on level
|
|
47
|
+
let valid = true;
|
|
48
|
+
if (level === ValidationLevel.SYNTAX) {
|
|
49
|
+
valid = this.validateSchema();
|
|
50
|
+
}
|
|
51
|
+
else if (level === ValidationLevel.SEMANTIC) {
|
|
52
|
+
valid = this.validateSchema() && this.validateStructure();
|
|
53
|
+
}
|
|
54
|
+
else if (level === ValidationLevel.BUSINESS) {
|
|
55
|
+
valid = this.validateSchema() &&
|
|
56
|
+
this.validateStructure() &&
|
|
57
|
+
this.validateBusinessRules();
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
valid,
|
|
61
|
+
errors: this.errors,
|
|
62
|
+
level
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validates XML against schema
|
|
67
|
+
* @returns True if schema validation passed
|
|
68
|
+
*/
|
|
69
|
+
validateSchema() {
|
|
70
|
+
// Basic schema validation (simplified for now)
|
|
71
|
+
if (!this.xmlDoc)
|
|
72
|
+
return false;
|
|
73
|
+
// Check for root element
|
|
74
|
+
const root = this.xmlDoc.documentElement;
|
|
75
|
+
if (!root || root.nodeName !== 'rsm:CrossIndustryInvoice') {
|
|
76
|
+
this.addError('FX-SCHEMA-1', 'Root element must be rsm:CrossIndustryInvoice', '/');
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
// Check for required namespaces
|
|
80
|
+
if (!root.lookupNamespaceURI('rsm') || !root.lookupNamespaceURI('ram')) {
|
|
81
|
+
this.addError('FX-SCHEMA-2', 'Required namespaces rsm and ram must be declared', '/');
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validates structure of the XML document
|
|
88
|
+
* @returns True if structure validation passed
|
|
89
|
+
*/
|
|
90
|
+
validateStructure() {
|
|
91
|
+
if (!this.xmlDoc)
|
|
92
|
+
return false;
|
|
93
|
+
let valid = true;
|
|
94
|
+
// Check for required main sections
|
|
95
|
+
const sections = [
|
|
96
|
+
'rsm:ExchangedDocumentContext',
|
|
97
|
+
'rsm:ExchangedDocument',
|
|
98
|
+
'rsm:SupplyChainTradeTransaction'
|
|
99
|
+
];
|
|
100
|
+
for (const section of sections) {
|
|
101
|
+
if (!this.exists(section)) {
|
|
102
|
+
this.addError('FX-STRUCT-1', `Required section ${section} is missing`, '/rsm:CrossIndustryInvoice');
|
|
103
|
+
valid = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Check for SupplyChainTradeTransaction sections
|
|
107
|
+
if (this.exists('rsm:SupplyChainTradeTransaction')) {
|
|
108
|
+
const tradeSubsections = [
|
|
109
|
+
'ram:ApplicableHeaderTradeAgreement',
|
|
110
|
+
'ram:ApplicableHeaderTradeDelivery',
|
|
111
|
+
'ram:ApplicableHeaderTradeSettlement'
|
|
112
|
+
];
|
|
113
|
+
for (const subsection of tradeSubsections) {
|
|
114
|
+
if (!this.exists(`rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeTransaction/${subsection}`)) {
|
|
115
|
+
this.addError('FX-STRUCT-2', `Required subsection ${subsection} is missing`, '/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeTransaction');
|
|
116
|
+
valid = false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return valid;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Validates business rules
|
|
124
|
+
* @returns True if business rule validation passed
|
|
125
|
+
*/
|
|
126
|
+
validateBusinessRules() {
|
|
127
|
+
if (!this.xmlDoc)
|
|
128
|
+
return false;
|
|
129
|
+
let valid = true;
|
|
130
|
+
// BR-16: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113)
|
|
131
|
+
valid = this.validateAmounts() && valid;
|
|
132
|
+
// BR-CO-3: Value added tax point date (BT-7) and Value added tax point date code (BT-8) are mutually exclusive
|
|
133
|
+
valid = this.validateMutuallyExclusiveFields() && valid;
|
|
134
|
+
// BR-S-1: An Invoice that contains a line (BG-25) where the Invoiced item VAT category code (BT-151) is "Standard rated"
|
|
135
|
+
// shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32)
|
|
136
|
+
// and/or the Seller tax representative VAT identifier (BT-63).
|
|
137
|
+
valid = this.validateSellerVatIdentifier() && valid;
|
|
138
|
+
return valid;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Detects Factur-X profile from the XML
|
|
142
|
+
*/
|
|
143
|
+
detectProfile() {
|
|
144
|
+
if (!this.xmlDoc)
|
|
145
|
+
return;
|
|
146
|
+
// Look for profile identifier
|
|
147
|
+
const profileNode = xpath.select1('string(//rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/ram:ID)', this.xmlDoc);
|
|
148
|
+
if (profileNode) {
|
|
149
|
+
const profileText = profileNode.toString();
|
|
150
|
+
if (profileText.includes('BASIC')) {
|
|
151
|
+
this.profile = 'BASIC';
|
|
152
|
+
}
|
|
153
|
+
else if (profileText.includes('EN16931')) {
|
|
154
|
+
this.profile = 'EN16931';
|
|
155
|
+
}
|
|
156
|
+
else if (profileText.includes('EXTENDED')) {
|
|
157
|
+
this.profile = 'EXTENDED';
|
|
158
|
+
}
|
|
159
|
+
else if (profileText.includes('MINIMUM')) {
|
|
160
|
+
this.profile = 'MINIMUM';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Validates amount calculations in the invoice
|
|
166
|
+
* @returns True if amount validation passed
|
|
167
|
+
*/
|
|
168
|
+
validateAmounts() {
|
|
169
|
+
if (!this.xmlDoc)
|
|
170
|
+
return false;
|
|
171
|
+
try {
|
|
172
|
+
// Extract amounts
|
|
173
|
+
const totalAmount = this.getNumberValue('//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:GrandTotalAmount');
|
|
174
|
+
const paidAmount = this.getNumberValue('//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:TotalPrepaidAmount') || 0;
|
|
175
|
+
const dueAmount = this.getNumberValue('//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:DuePayableAmount');
|
|
176
|
+
// Calculate expected due amount
|
|
177
|
+
const expectedDueAmount = totalAmount - paidAmount;
|
|
178
|
+
// Compare with a small tolerance for rounding errors
|
|
179
|
+
if (Math.abs(dueAmount - expectedDueAmount) > 0.01) {
|
|
180
|
+
this.addError('BR-16', `Amount due for payment (${dueAmount}) must equal Invoice total amount with VAT (${totalAmount}) - Paid amount (${paidAmount})`, '//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation');
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
this.addError('FX-AMOUNT', `Error validating amounts: ${error}`, '/');
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Validates mutually exclusive fields
|
|
192
|
+
* @returns True if validation passed
|
|
193
|
+
*/
|
|
194
|
+
validateMutuallyExclusiveFields() {
|
|
195
|
+
if (!this.xmlDoc)
|
|
196
|
+
return false;
|
|
197
|
+
try {
|
|
198
|
+
// Check for VAT point date and code (BR-CO-3)
|
|
199
|
+
const vatPointDate = this.exists('//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:TaxPointDate');
|
|
200
|
+
const vatPointDateCode = this.exists('//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:DueDateTypeCode');
|
|
201
|
+
if (vatPointDate && vatPointDateCode) {
|
|
202
|
+
this.addError('BR-CO-3', 'Value added tax point date and Value added tax point date code are mutually exclusive', '//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax');
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
this.addError('FX-MUTUAL', `Error validating mutually exclusive fields: ${error}`, '/');
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Validates seller VAT identifier requirements
|
|
214
|
+
* @returns True if validation passed
|
|
215
|
+
*/
|
|
216
|
+
validateSellerVatIdentifier() {
|
|
217
|
+
if (!this.xmlDoc)
|
|
218
|
+
return false;
|
|
219
|
+
try {
|
|
220
|
+
// Check if there are any standard rated line items
|
|
221
|
+
const standardRatedItems = this.exists('//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:CategoryCode[text()="S"]');
|
|
222
|
+
if (standardRatedItems) {
|
|
223
|
+
// Check for seller VAT identifier
|
|
224
|
+
const sellerVatId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]');
|
|
225
|
+
const sellerTaxId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]');
|
|
226
|
+
const sellerTaxRepId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTaxRepresentativeTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]');
|
|
227
|
+
if (!sellerVatId && !sellerTaxId && !sellerTaxRepId) {
|
|
228
|
+
this.addError('BR-S-1', 'An Invoice with standard rated items must contain the Seller VAT Identifier, Tax registration identifier or Tax representative VAT identifier', '//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty');
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
this.addError('FX-VAT', `Error validating seller VAT identifier: ${error}`, '/');
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Helper method to check if a node exists
|
|
241
|
+
* @param xpathExpression XPath to check
|
|
242
|
+
* @returns True if node exists
|
|
243
|
+
*/
|
|
244
|
+
exists(xpathExpression) {
|
|
245
|
+
if (!this.xmlDoc)
|
|
246
|
+
return false;
|
|
247
|
+
const nodes = xpath.select(xpathExpression, this.xmlDoc);
|
|
248
|
+
// Handle different return types from xpath.select()
|
|
249
|
+
if (Array.isArray(nodes)) {
|
|
250
|
+
return nodes.length > 0;
|
|
251
|
+
}
|
|
252
|
+
return nodes ? true : false;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Helper method to get a number value from XPath
|
|
256
|
+
* @param xpathExpression XPath to get number from
|
|
257
|
+
* @returns Number value or NaN if not found
|
|
258
|
+
*/
|
|
259
|
+
getNumberValue(xpathExpression) {
|
|
260
|
+
if (!this.xmlDoc)
|
|
261
|
+
return NaN;
|
|
262
|
+
const node = xpath.select1(`string(${xpathExpression})`, this.xmlDoc);
|
|
263
|
+
return node ? parseFloat(node.toString()) : NaN;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdHVyeC52YWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9mb3JtYXRzL2ZhY3R1cngudmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFbkQsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUVuQzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sZ0JBQWlCLFNBQVEsYUFBYTtJQUNqRCxzQ0FBc0M7YUFDdkIsWUFBTyxHQUFHLDhEQUE4RCxBQUFqRSxDQUFrRTthQUN6RSxXQUFNLEdBQUcsb0ZBQW9GLEFBQXZGLENBQXdGO2FBQzlGLFdBQU0sR0FBRyw2REFBNkQsQUFBaEUsQ0FBaUU7SUFRdEYsWUFBWSxHQUFXO1FBQ3JCLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQVBiLDhCQUE4QjtRQUN0QixXQUFNLEdBQW9CLElBQUksQ0FBQztRQUV2QyxvREFBb0Q7UUFDNUMsWUFBTyxHQUFXLEVBQUUsQ0FBQztRQUszQixJQUFJLENBQUM7WUFDSCxxQkFBcUI7WUFDckIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFNBQVMsRUFBRSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztZQUV0RSw2QkFBNkI7WUFDN0IsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3ZCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsd0JBQXdCLEtBQUssRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFFBQVEsQ0FBQyxRQUF5QixlQUFlLENBQUMsTUFBTTtRQUM3RCxlQUFlO1FBQ2YsSUFBSSxDQUFDLE1BQU0sR0FBRyxFQUFFLENBQUM7UUFFakIsNENBQTRDO1FBQzVDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakIsT0FBTztnQkFDTCxLQUFLLEVBQUUsS0FBSztnQkFDWixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07Z0JBQ25CLEtBQUssRUFBRSxLQUFLO2FBQ2IsQ0FBQztRQUNKLENBQUM7UUFFRCxvQ0FBb0M7UUFDcEMsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDO1FBRWpCLElBQUksS0FBSyxLQUFLLGVBQWUsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNyQyxLQUFLLEdBQUcsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ2hDLENBQUM7YUFBTSxJQUFJLEtBQUssS0FBSyxlQUFlLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDOUMsS0FBSyxHQUFHLElBQUksQ0FBQyxjQUFjLEVBQUUsSUFBSSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztRQUM1RCxDQUFDO2FBQU0sSUFBSSxLQUFLLEtBQUssZUFBZSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQzlDLEtBQUssR0FBRyxJQUFJLENBQUMsY0FBYyxFQUFFO2dCQUNyQixJQUFJLENBQUMsaUJBQWlCLEVBQUU7Z0JBQ3hCLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQ3ZDLENBQUM7UUFFRCxPQUFPO1lBQ0wsS0FBSztZQUNMLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTTtZQUNuQixLQUFLO1NBQ04sQ0FBQztJQUNKLENBQUM7SUFFRDs7O09BR0c7SUFDTyxjQUFjO1FBQ3RCLCtDQUErQztRQUMvQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEtBQUssQ0FBQztRQUUvQix5QkFBeUI7UUFDekIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUM7UUFDekMsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsUUFBUSxLQUFLLDBCQUEwQixFQUFFLENBQUM7WUFDMUQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsK0NBQStDLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDbkYsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBRUQsZ0NBQWdDO1FBQ2hDLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN2RSxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRSxrREFBa0QsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUN0RixPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7O09BR0c7SUFDSyxpQkFBaUI7UUFDdkIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFFL0IsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDO1FBRWpCLG1DQUFtQztRQUNuQyxNQUFNLFFBQVEsR0FBRztZQUNmLDhCQUE4QjtZQUM5Qix1QkFBdUI7WUFDdkIsaUNBQWlDO1NBQ2xDLENBQUM7UUFFRixLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQy9CLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQzFCLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxFQUFFLG9CQUFvQixPQUFPLGFBQWEsRUFBRSwyQkFBMkIsQ0FBQyxDQUFDO2dCQUNwRyxLQUFLLEdBQUcsS0FBSyxDQUFDO1lBQ2hCLENBQUM7UUFDSCxDQUFDO1FBRUQsaURBQWlEO1FBQ2pELElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxpQ0FBaUMsQ0FBQyxFQUFFLENBQUM7WUFDbkQsTUFBTSxnQkFBZ0IsR0FBRztnQkFDdkIsb0NBQW9DO2dCQUNwQyxtQ0FBbUM7Z0JBQ25DLHFDQUFxQzthQUN0QyxDQUFDO1lBRUYsS0FBSyxNQUFNLFVBQVUsSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO2dCQUMxQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyx3RUFBd0UsVUFBVSxFQUFFLENBQUMsRUFBRSxDQUFDO29CQUN2RyxJQUFJLENBQUMsUUFBUSxDQUFDLGFBQWEsRUFBRSx1QkFBdUIsVUFBVSxhQUFhLEVBQ3pFLGdHQUFnRyxDQUFDLENBQUM7b0JBQ3BHLEtBQUssR0FBRyxLQUFLLENBQUM7Z0JBQ2hCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVEOzs7T0FHRztJQUNPLHFCQUFxQjtRQUM3QixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEtBQUssQ0FBQztRQUUvQixJQUFJLEtBQUssR0FBRyxJQUFJLENBQUM7UUFFakIseUdBQXlHO1FBQ3pHLEtBQUssR0FBRyxJQUFJLENBQUMsZUFBZSxFQUFFLElBQUksS0FBSyxDQUFDO1FBRXhDLCtHQUErRztRQUMvRyxLQUFLLEdBQUcsSUFBSSxDQUFDLCtCQUErQixFQUFFLElBQUksS0FBSyxDQUFDO1FBRXhELDBIQUEwSDtRQUMxSCxtR0FBbUc7UUFDbkcsK0RBQStEO1FBQy9ELEtBQUssR0FBRyxJQUFJLENBQUMsMkJBQTJCLEVBQUUsSUFBSSxLQUFLLENBQUM7UUFFcEQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQ7O09BRUc7SUFDSyxhQUFhO1FBQ25CLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLE9BQU87UUFFekIsOEJBQThCO1FBQzlCLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQy9CLDhGQUE4RixFQUM5RixJQUFJLENBQUMsTUFBTSxDQUNaLENBQUM7UUFFRixJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ2hCLE1BQU0sV0FBVyxHQUFHLFdBQVcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUUzQyxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDbEMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7WUFDekIsQ0FBQztpQkFBTSxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDM0MsSUFBSSxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUM7WUFDM0IsQ0FBQztpQkFBTSxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztnQkFDNUMsSUFBSSxDQUFDLE9BQU8sR0FBRyxVQUFVLENBQUM7WUFDNUIsQ0FBQztpQkFBTSxJQUFJLFdBQVcsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDM0MsSUFBSSxDQUFDLE9BQU8sR0FBRyxTQUFTLENBQUM7WUFDM0IsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ssZUFBZTtRQUNyQixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEtBQUssQ0FBQztRQUUvQixJQUFJLENBQUM7WUFDSCxrQkFBa0I7WUFDbEIsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FDckMsZ0hBQWdILENBQ2pILENBQUM7WUFFRixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUNwQyxrSEFBa0gsQ0FDbkgsSUFBSSxDQUFDLENBQUM7WUFFUCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUNuQyxnSEFBZ0gsQ0FDakgsQ0FBQztZQUVGLGdDQUFnQztZQUNoQyxNQUFNLGlCQUFpQixHQUFHLFdBQVcsR0FBRyxVQUFVLENBQUM7WUFFbkQscURBQXFEO1lBQ3JELElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEdBQUcsaUJBQWlCLENBQUMsR0FBRyxJQUFJLEVBQUUsQ0FBQztnQkFDbkQsSUFBSSxDQUFDLFFBQVEsQ0FDWCxPQUFPLEVBQ1AsMkJBQTJCLFNBQVMsK0NBQStDLFdBQVcsb0JBQW9CLFVBQVUsR0FBRyxFQUMvSCwyRkFBMkYsQ0FDNUYsQ0FBQztnQkFDRixPQUFPLEtBQUssQ0FBQztZQUNmLENBQUM7WUFFRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsNkJBQTZCLEtBQUssRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3RFLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSywrQkFBK0I7UUFDckMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFFL0IsSUFBSSxDQUFDO1lBQ0gsOENBQThDO1lBQzlDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsK0VBQStFLENBQUMsQ0FBQztZQUNsSCxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsa0ZBQWtGLENBQUMsQ0FBQztZQUV6SCxJQUFJLFlBQVksSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO2dCQUNyQyxJQUFJLENBQUMsUUFBUSxDQUNYLFNBQVMsRUFDVCx1RkFBdUYsRUFDdkYsOERBQThELENBQy9ELENBQUM7Z0JBQ0YsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLCtDQUErQyxLQUFLLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUN4RixPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ssMkJBQTJCO1FBQ2pDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLE9BQU8sS0FBSyxDQUFDO1FBRS9CLElBQUksQ0FBQztZQUNILG1EQUFtRDtZQUNuRCxNQUFNLGtCQUFrQixHQUFHLElBQUksQ0FBQyxNQUFNLENBQ3BDLHdGQUF3RixDQUN6RixDQUFDO1lBRUYsSUFBSSxrQkFBa0IsRUFBRSxDQUFDO2dCQUN2QixrQ0FBa0M7Z0JBQ2xDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsK0dBQStHLENBQUMsQ0FBQztnQkFDakosTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQywrR0FBK0csQ0FBQyxDQUFDO2dCQUNqSixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGdJQUFnSSxDQUFDLENBQUM7Z0JBRXJLLElBQUksQ0FBQyxXQUFXLElBQUksQ0FBQyxXQUFXLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztvQkFDcEQsSUFBSSxDQUFDLFFBQVEsQ0FDWCxRQUFRLEVBQ1IsK0lBQStJLEVBQy9JLDJEQUEyRCxDQUM1RCxDQUFDO29CQUNGLE9BQU8sS0FBSyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1lBRUQsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLDJDQUEyQyxLQUFLLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUNqRixPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLE1BQU0sQ0FBQyxlQUF1QjtRQUNwQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEtBQUssQ0FBQztRQUMvQixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDekQsb0RBQW9EO1FBQ3BELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3pCLE9BQU8sS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDMUIsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztJQUM5QixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLGNBQWMsQ0FBQyxlQUF1QjtRQUM1QyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEdBQUcsQ0FBQztRQUM3QixNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQVUsZUFBZSxHQUFHLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3RFLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztJQUNsRCxDQUFDIn0=
|