UNPKG

@fin.cx/einvoice

Version:

A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.

306 lines 23.6 kB
import { BaseValidator } from './base.validator.js'; import { ValidationLevel } from '../interfaces.js'; import * as xpath from 'xpath'; import { DOMParser } from 'xmldom'; /** * Validator for UBL (Universal Business Language) invoice format * Implements validation rules according to EN16931 and UBL 2.1 specification */ export class UBLValidator extends BaseValidator { // XML namespaces for UBL static { this.NS_INVOICE = 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'; } static { this.NS_CAC = 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'; } static { this.NS_CBC = 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2'; } constructor(xml) { super(xml); // XML document for processing this.xmlDoc = null; // UBL profile or customization ID this.customizationId = ''; try { // Parse XML document this.xmlDoc = new DOMParser().parseFromString(xml, 'application/xml'); // Determine UBL customization ID (e.g. EN16931, XRechnung) this.detectCustomizationId(); } catch (error) { this.addError('UBL-PARSE', `Failed to parse XML: ${error}`, '/'); } } /** * Validates the UBL invoice against the specified level * @param level Validation level * @returns Validation result */ validate(level = ValidationLevel.SYNTAX) { // Reset errors this.errors = []; // Check if document was parsed successfully if (!this.xmlDoc) { return { valid: false, errors: this.errors, level: level }; } // Perform validation based on level let valid = true; if (level === ValidationLevel.SYNTAX) { valid = this.validateSchema(); } else if (level === ValidationLevel.SEMANTIC) { valid = this.validateSchema() && this.validateStructure(); } else if (level === ValidationLevel.BUSINESS) { valid = this.validateSchema() && this.validateStructure() && this.validateBusinessRules(); } return { valid, errors: this.errors, level }; } /** * Validates XML against schema * @returns True if schema validation passed */ validateSchema() { // Basic schema validation (simplified for now) if (!this.xmlDoc) return false; // Check for root element const root = this.xmlDoc.documentElement; if (!root || (root.nodeName !== 'Invoice' && root.nodeName !== 'CreditNote')) { this.addError('UBL-SCHEMA-1', 'Root element must be Invoice or CreditNote', '/'); return false; } // Check for required namespaces if (!root.lookupNamespaceURI('cac') || !root.lookupNamespaceURI('cbc')) { this.addError('UBL-SCHEMA-2', 'Required namespaces cac and cbc must be declared', '/'); return false; } return true; } /** * Validates structure of the XML document * @returns True if structure validation passed */ validateStructure() { if (!this.xmlDoc) return false; let valid = true; // Check for required main sections const sections = [ 'cbc:ID', 'cbc:IssueDate', 'cac:AccountingSupplierParty', 'cac:AccountingCustomerParty', 'cac:LegalMonetaryTotal' ]; for (const section of sections) { if (!this.exists(`/${this.getRootNodeName()}/${section}`)) { this.addError('UBL-STRUCT-1', `Required section ${section} is missing`, `/${this.getRootNodeName()}`); valid = false; } } // Check for TaxTotal section if (this.exists(`/${this.getRootNodeName()}/cac:TaxTotal`)) { const taxSubsections = [ 'cbc:TaxAmount', 'cac:TaxSubtotal' ]; for (const subsection of taxSubsections) { if (!this.exists(`/${this.getRootNodeName()}/cac:TaxTotal/${subsection}`)) { this.addError('UBL-STRUCT-2', `Required subsection ${subsection} is missing`, `/${this.getRootNodeName()}/cac:TaxTotal`); valid = false; } } } return valid; } /** * Validates business rules * @returns True if business rule validation passed */ validateBusinessRules() { if (!this.xmlDoc) return false; let valid = true; // BR-16: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113) valid = this.validateAmounts() && valid; // BR-CO-3: Value added tax point date (BT-7) and Value added tax point date code (BT-8) are mutually exclusive valid = this.validateMutuallyExclusiveFields() && valid; // BR-S-1: An Invoice that contains a line where the VAT category code is "Standard rated" // shall contain the Seller VAT Identifier or the Seller tax representative VAT identifier valid = this.validateSellerVatIdentifier() && valid; // XRechnung specific rules when customization ID matches if (this.isXRechnung()) { valid = this.validateXRechnungRules() && valid; } return valid; } /** * Gets the root node name (Invoice or CreditNote) * @returns Root node name */ getRootNodeName() { if (!this.xmlDoc || !this.xmlDoc.documentElement) return 'Invoice'; return this.xmlDoc.documentElement.nodeName; } /** * Detects UBL customization ID from the XML */ detectCustomizationId() { if (!this.xmlDoc) return; // Look for customization ID const customizationNode = xpath.select1(`string(/${this.getRootNodeName()}/cbc:CustomizationID)`, this.xmlDoc); if (customizationNode) { this.customizationId = customizationNode.toString(); } } /** * Checks if invoice is an XRechnung * @returns True if XRechnung customization ID is present */ isXRechnung() { return this.customizationId.includes('xrechnung') || this.customizationId.includes('XRechnung'); } /** * Validates amount calculations in the invoice * @returns True if amount validation passed */ validateAmounts() { if (!this.xmlDoc) return false; try { // Extract amounts const totalAmount = this.getNumberValue(`/${this.getRootNodeName()}/cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount`); const paidAmount = this.getNumberValue(`/${this.getRootNodeName()}/cac:LegalMonetaryTotal/cbc:PrepaidAmount`) || 0; const dueAmount = this.getNumberValue(`/${this.getRootNodeName()}/cac:LegalMonetaryTotal/cbc:PayableAmount`); // Calculate expected due amount const expectedDueAmount = totalAmount - paidAmount; // Compare with a small tolerance for rounding errors if (Math.abs(dueAmount - expectedDueAmount) > 0.01) { this.addError('BR-16', `Amount due for payment (${dueAmount}) must equal Invoice total amount with VAT (${totalAmount}) - Paid amount (${paidAmount})`, `/${this.getRootNodeName()}/cac:LegalMonetaryTotal`); return false; } return true; } catch (error) { this.addError('UBL-AMOUNT', `Error validating amounts: ${error}`, '/'); return false; } } /** * Validates mutually exclusive fields * @returns True if validation passed */ validateMutuallyExclusiveFields() { if (!this.xmlDoc) return false; try { // Check for VAT point date and code (BR-CO-3) const vatPointDate = this.exists(`/${this.getRootNodeName()}/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:TaxPointDate`); const vatPointDateCode = this.exists(`/${this.getRootNodeName()}/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:TaxExemptionReasonCode`); if (vatPointDate && vatPointDateCode) { this.addError('BR-CO-3', 'Value added tax point date and Value added tax point date code are mutually exclusive', `/${this.getRootNodeName()}/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory`); return false; } return true; } catch (error) { this.addError('UBL-MUTUAL', `Error validating mutually exclusive fields: ${error}`, '/'); return false; } } /** * Validates seller VAT identifier requirements * @returns True if validation passed */ validateSellerVatIdentifier() { if (!this.xmlDoc) return false; try { // Check if there are any standard rated line items const standardRatedItems = this.exists(`/${this.getRootNodeName()}/cac:InvoiceLine/cac:Item/cac:ClassifiedTaxCategory/cbc:ID[text()="S"]`); if (standardRatedItems) { // Check for seller VAT identifier const sellerVatId = this.exists(`/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID`); const sellerTaxRepId = this.exists(`/${this.getRootNodeName()}/cac:TaxRepresentativeParty/cac:PartyTaxScheme/cbc:CompanyID`); if (!sellerVatId && !sellerTaxRepId) { this.addError('BR-S-1', 'An Invoice with standard rated items must contain the Seller VAT Identifier or Tax representative VAT identifier', `/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party`); return false; } } return true; } catch (error) { this.addError('UBL-VAT', `Error validating seller VAT identifier: ${error}`, '/'); return false; } } /** * Validates XRechnung specific rules * @returns True if validation passed */ validateXRechnungRules() { if (!this.xmlDoc) return false; let valid = true; try { // BR-DE-1: Buyer reference must be present for German VAT compliance if (!this.exists(`/${this.getRootNodeName()}/cbc:BuyerReference`)) { this.addError('BR-DE-1', 'BuyerReference is mandatory for XRechnung', `/${this.getRootNodeName()}`); valid = false; } // BR-DE-15: Contact information must be present if (!this.exists(`/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party/cac:Contact`)) { this.addError('BR-DE-15', 'Supplier contact information is mandatory for XRechnung', `/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party`); valid = false; } // BR-DE-16: Electronic address identifier scheme (e.g. PEPPOL) must be present if (!this.exists(`/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID`) || !this.exists(`/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID/@schemeID`)) { this.addError('BR-DE-16', 'Supplier electronic address with scheme identifier is mandatory for XRechnung', `/${this.getRootNodeName()}/cac:AccountingSupplierParty/cac:Party`); valid = false; } return valid; } catch (error) { this.addError('UBL-XRECHNUNG', `Error validating XRechnung rules: ${error}`, '/'); return false; } } /** * Helper method to check if a node exists * @param xpathExpression XPath to check * @returns True if node exists */ exists(xpathExpression) { if (!this.xmlDoc) return false; const nodes = xpath.select(xpathExpression, this.xmlDoc); // Handle different return types from xpath.select() if (Array.isArray(nodes)) { return nodes.length > 0; } return nodes ? true : false; } /** * Helper method to get a number value from XPath * @param xpathExpression XPath to get number from * @returns Number value or NaN if not found */ getNumberValue(xpathExpression) { if (!this.xmlDoc) return NaN; const node = xpath.select1(`string(${xpathExpression})`, this.xmlDoc); return node ? parseFloat(node.toString()) : NaN; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidWJsLnZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2Zvcm1hdHMvdWJsLnZhbGlkYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDcEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRW5ELE9BQU8sS0FBSyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBQy9CLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFbkM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFlBQWEsU0FBUSxhQUFhO0lBQzdDLHlCQUF5QjthQUNWLGVBQVUsR0FBRyx3REFBd0QsQUFBM0QsQ0FBNEQ7YUFDdEUsV0FBTSxHQUFHLDBFQUEwRSxBQUE3RSxDQUE4RTthQUNwRixXQUFNLEdBQUcsc0VBQXNFLEFBQXpFLENBQTBFO0lBUS9GLFlBQVksR0FBVztRQUNyQixLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFQYiw4QkFBOEI7UUFDdEIsV0FBTSxHQUFvQixJQUFJLENBQUM7UUFFdkMsa0NBQWtDO1FBQzFCLG9CQUFlLEdBQVcsRUFBRSxDQUFDO1FBS25DLElBQUksQ0FBQztZQUNILHFCQUFxQjtZQUNyQixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksU0FBUyxFQUFFLENBQUMsZUFBZSxDQUFDLEdBQUcsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1lBRXRFLDJEQUEyRDtZQUMzRCxJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMvQixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxRQUFRLENBQUMsV0FBVyxFQUFFLHdCQUF3QixLQUFLLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNuRSxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxRQUFRLENBQUMsUUFBeUIsZUFBZSxD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