UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

529 lines 44.5 kB
import * as plugins from './plugins.js'; import * as path from 'path'; /** * Content-addressed storage for invoices * Integrates with BagIt archive structure for GoBD compliance */ export class InvoiceStorage { constructor(exportPath) { this.MAX_CACHE_SIZE = 10000; // Maximum number of cached entries this.cacheAccessOrder = []; // Track access order for LRU eviction this.MAX_PDF_SIZE = 50 * 1024 * 1024; // 50MB max this.exportPath = exportPath; this.logger = new plugins.smartlog.ConsoleLog(); this.registryPath = path.join(exportPath, 'data', 'documents', 'invoices', 'registry.ndjson'); this.metadataCache = new Map(); } /** * Manage cache size using LRU eviction */ manageCacheSize() { if (this.metadataCache.size > this.MAX_CACHE_SIZE) { // Remove least recently used entries const entriesToRemove = Math.min(100, Math.floor(this.MAX_CACHE_SIZE * 0.1)); // Remove 10% or 100 entries const keysToRemove = this.cacheAccessOrder.splice(0, entriesToRemove); for (const key of keysToRemove) { this.metadataCache.delete(key); } this.logger.log('info', `Evicted ${entriesToRemove} entries from metadata cache`); } } /** * Update cache access order for LRU */ touchCacheEntry(key) { const index = this.cacheAccessOrder.indexOf(key); if (index > -1) { this.cacheAccessOrder.splice(index, 1); } this.cacheAccessOrder.push(key); } /** * Initialize storage directories */ async initialize() { const dirs = [ path.join(this.exportPath, 'data', 'documents', 'invoices', 'inbound'), path.join(this.exportPath, 'data', 'documents', 'invoices', 'inbound', 'metadata'), path.join(this.exportPath, 'data', 'documents', 'invoices', 'outbound'), path.join(this.exportPath, 'data', 'documents', 'invoices', 'outbound', 'metadata'), path.join(this.exportPath, 'data', 'validation') ]; for (const dir of dirs) { await plugins.smartfile.fs.ensureDir(dir); } // Load existing registry if it exists await this.loadRegistry(); } /** * Store an invoice with content addressing */ async storeInvoice(invoice, pdfBuffer) { try { // Validate PDF size if provided if (pdfBuffer && pdfBuffer.length > this.MAX_PDF_SIZE) { throw new Error(`PDF file too large: ${pdfBuffer.length} bytes (max ${this.MAX_PDF_SIZE} bytes)`); } // Calculate hashes const xmlHash = await this.calculateHash(invoice.xmlContent || ''); const pdfHash = pdfBuffer ? await this.calculateHash(pdfBuffer) : undefined; const contentHash = xmlHash; // Primary content hash is XML // Check for duplicates const duplicateCheck = await this.checkDuplicate(invoice, contentHash); if (duplicateCheck.isDuplicate) { this.logger.log('warn', `Duplicate invoice detected: ${invoice.invoiceNumber}`); return duplicateCheck.matchedContentHash || contentHash; } // Determine storage path const direction = invoice.direction; const basePath = path.join(this.exportPath, 'data', 'documents', 'invoices', direction); // Create filename with content hash const dateStr = invoice.issueDate.toISOString().split('T')[0]; const sanitizedNumber = invoice.invoiceNumber.replace(/[^a-zA-Z0-9-_]/g, '_'); const xmlFilename = `${contentHash.substring(0, 8)}_${dateStr}_${sanitizedNumber}.xml`; const xmlPath = path.join(basePath, xmlFilename); // Store XML await plugins.smartfile.memory.toFs(invoice.xmlContent || '', xmlPath); // Store PDF if available let pdfFilename; if (pdfBuffer) { pdfFilename = xmlFilename.replace('.xml', '.pdf'); const pdfPath = path.join(basePath, pdfFilename); await plugins.smartfile.memory.toFs(pdfBuffer, pdfPath); // Also store PDF/A-3 with embedded XML if supported if (invoice.format === 'zugferd' || invoice.format === 'facturx') { const pdfA3Filename = xmlFilename.replace('.xml', '_pdfa3.pdf'); const pdfA3Path = path.join(basePath, pdfA3Filename); // The PDF should already have embedded XML if it's ZUGFeRD/Factur-X await plugins.smartfile.memory.toFs(pdfBuffer, pdfA3Path); } } // Create and store metadata const metadata = { invoiceId: invoice.id, invoiceNumber: invoice.invoiceNumber, direction: invoice.direction, issueDate: invoice.issueDate.toISOString(), supplierName: invoice.supplier.name, customerName: invoice.customer.name, totalAmount: invoice.payableAmount, currency: invoice.currencyCode, contentHash, pdfHash, xmlHash, journalEntryId: invoice.bookingInfo?.journalEntryId, transactionIds: invoice.bookingInfo?.transactionIds, validationResult: { isValid: invoice.validationResult?.isValid || false, errors: this.countErrors(invoice.validationResult), warnings: this.countWarnings(invoice.validationResult) }, parserVersion: invoice.metadata?.parserVersion || '5.1.4', storedAt: new Date().toISOString(), storedBy: invoice.createdBy }; const metadataPath = path.join(basePath, 'metadata', `${contentHash}.json`); await plugins.smartfile.memory.toFs(JSON.stringify(metadata, null, 2), metadataPath); // Update registry await this.updateRegistry(invoice.id, contentHash, metadata); // Cache metadata with LRU management this.setCacheEntry(contentHash, metadata); this.logger.log('info', `Invoice stored: ${invoice.invoiceNumber} (${contentHash})`); return contentHash; } catch (error) { this.logger.log('error', `Failed to store invoice: ${error}`); throw new Error(`Invoice storage failed: ${error.message}`); } } /** * Retrieve an invoice by content hash */ async retrieveInvoice(contentHash) { try { // Check cache first const metadata = this.getCacheEntry(contentHash); if (!metadata) { this.logger.log('warn', `Invoice not found: ${contentHash}`); return null; } // Load XML content const xmlPath = await this.findInvoiceFile(contentHash, '.xml'); if (!xmlPath) { throw new Error(`XML file not found for invoice ${contentHash}`); } const xmlContent = await plugins.smartfile.fs.toStringSync(xmlPath); // Load PDF if exists let pdfContent; const pdfPath = await this.findInvoiceFile(contentHash, '.pdf'); if (pdfPath) { pdfContent = await plugins.smartfile.fs.toBuffer(pdfPath); } // Reconstruct invoice object (partial) const invoice = { id: metadata.invoiceId, invoiceNumber: metadata.invoiceNumber, direction: metadata.direction, issueDate: new Date(metadata.issueDate), supplier: { name: metadata.supplierName, id: '', address: { countryCode: 'DE' } }, customer: { name: metadata.customerName, id: '', address: { countryCode: 'DE' } }, payableAmount: metadata.totalAmount, currencyCode: metadata.currency, contentHash: metadata.contentHash, xmlContent, pdfContent, pdfHash: metadata.pdfHash }; return invoice; } catch (error) { this.logger.log('error', `Failed to retrieve invoice: ${error}`); return null; } } /** * Check for duplicate invoices */ async checkDuplicate(invoice, contentHash) { // Check by content hash (exact match) const existing = this.getCacheEntry(contentHash); if (existing) { return { isDuplicate: true, matchedInvoiceId: existing.invoiceId, matchedContentHash: contentHash, matchedFields: ['contentHash'], confidence: 100 }; } // Check by invoice number and supplier/customer for (const [hash, metadata] of this.metadataCache.entries()) { if (metadata.invoiceNumber === invoice.invoiceNumber && metadata.direction === invoice.direction) { // Same invoice number and direction if (invoice.direction === 'inbound' && metadata.supplierName === invoice.supplier.name) { // Same supplier return { isDuplicate: true, matchedInvoiceId: metadata.invoiceId, matchedContentHash: hash, matchedFields: ['invoiceNumber', 'supplier'], confidence: 95 }; } else if (invoice.direction === 'outbound' && metadata.customerName === invoice.customer.name) { // Same customer return { isDuplicate: true, matchedInvoiceId: metadata.invoiceId, matchedContentHash: hash, matchedFields: ['invoiceNumber', 'customer'], confidence: 95 }; } } // Check by amount and date within tolerance const dateTolerance = 7 * 24 * 60 * 60 * 1000; // 7 days const amountTolerance = 0.01; if (Math.abs(metadata.totalAmount - invoice.payableAmount) < amountTolerance && Math.abs(new Date(metadata.issueDate).getTime() - invoice.issueDate.getTime()) < dateTolerance && metadata.direction === invoice.direction) { if ((invoice.direction === 'inbound' && metadata.supplierName === invoice.supplier.name) || (invoice.direction === 'outbound' && metadata.customerName === invoice.customer.name)) { return { isDuplicate: true, matchedInvoiceId: metadata.invoiceId, matchedContentHash: hash, matchedFields: ['amount', 'date', 'party'], confidence: 85 }; } } } return { isDuplicate: false, confidence: 0 }; } /** * Search invoices by filter */ async searchInvoices(filter) { const results = []; for (const metadata of this.metadataCache.values()) { if (this.matchesFilter(metadata, filter)) { results.push(metadata); } } // Sort by date descending results.sort((a, b) => new Date(b.issueDate).getTime() - new Date(a.issueDate).getTime()); return results; } /** * Get storage statistics */ async getStatistics() { let totalSize = 0; let inboundCount = 0; let outboundCount = 0; for (const metadata of this.metadataCache.values()) { if (metadata.direction === 'inbound') { inboundCount++; } else { outboundCount++; } // Estimate size (would need actual file sizes in production) totalSize += 50000; // Rough estimate } return { totalInvoices: this.metadataCache.size, inboundCount, outboundCount, totalSize, duplicatesDetected: 0, // Would track this in production lastUpdate: new Date() }; } /** * Create EN16931 compliance report */ async createComplianceReport() { const report = { timestamp: new Date().toISOString(), totalInvoices: this.metadataCache.size, validInvoices: 0, invalidInvoices: 0, warnings: 0, byFormat: {}, byDirection: { inbound: 0, outbound: 0 }, validationErrors: [], complianceLevel: 'EN16931', validatorVersion: '5.1.4' }; for (const metadata of this.metadataCache.values()) { if (metadata.validationResult.isValid) { report.validInvoices++; } else { report.invalidInvoices++; } report.warnings += metadata.validationResult.warnings; if (metadata.direction === 'inbound') { report.byDirection.inbound++; } else { report.byDirection.outbound++; } } const reportPath = path.join(this.exportPath, 'data', 'validation', 'en16931_compliance.json'); await plugins.smartfile.memory.toFs(JSON.stringify(report, null, 2), reportPath); } /** * Load registry from disk */ async loadRegistry() { try { if (await plugins.smartfile.fs.fileExists(this.registryPath)) { const content = await plugins.smartfile.fs.toStringSync(this.registryPath); const lines = content.split('\n').filter(line => line.trim()); for (const line of lines) { try { const entry = JSON.parse(line); this.setCacheEntry(entry.hash, entry.metadata); } catch (e) { this.logger.log('warn', `Invalid registry entry: ${line}`); } } this.logger.log('info', `Loaded ${this.metadataCache.size} invoices from registry`); } } catch (error) { this.logger.log('error', `Failed to load registry: ${error}`); } } /** * Update registry with new entry */ async updateRegistry(invoiceId, contentHash, metadata) { try { const entry = { id: invoiceId, hash: contentHash, metadata }; // Append to NDJSON file const line = JSON.stringify(entry) + '\n'; await plugins.smartfile.fs.ensureDir(path.dirname(this.registryPath)); // Use native fs for atomic append (better performance and concurrency safety) const fs = await import('fs/promises'); await fs.appendFile(this.registryPath, line, 'utf8'); } catch (error) { this.logger.log('error', `Failed to update registry: ${error}`); } } /** * Find invoice file by hash and extension */ async findInvoiceFile(contentHash, extension) { const dirs = [ path.join(this.exportPath, 'data', 'documents', 'invoices', 'inbound'), path.join(this.exportPath, 'data', 'documents', 'invoices', 'outbound') ]; for (const dir of dirs) { const files = await plugins.smartfile.fs.listFileTree(dir, '**/*' + extension); for (const file of files) { if (file.includes(contentHash.substring(0, 8))) { return path.join(dir, file); } } } return null; } /** * Calculate SHA-256 hash */ async calculateHash(data) { if (typeof data === 'string') { return await plugins.smarthash.sha256FromString(data); } else { return await plugins.smarthash.sha256FromBuffer(data); } } /** * Check if metadata matches filter */ matchesFilter(metadata, filter) { if (filter.direction && metadata.direction !== filter.direction) { return false; } if (filter.dateFrom && new Date(metadata.issueDate) < filter.dateFrom) { return false; } if (filter.dateTo && new Date(metadata.issueDate) > filter.dateTo) { return false; } if (filter.minAmount && metadata.totalAmount < filter.minAmount) { return false; } if (filter.maxAmount && metadata.totalAmount > filter.maxAmount) { return false; } if (filter.invoiceNumber && !metadata.invoiceNumber.includes(filter.invoiceNumber)) { return false; } if (filter.supplierId && !metadata.supplierName.includes(filter.supplierId)) { return false; } if (filter.customerId && !metadata.customerName.includes(filter.customerId)) { return false; } return true; } /** * Count errors in validation result */ countErrors(validationResult) { if (!validationResult) return 0; return (validationResult.syntax.errors.length + validationResult.semantic.errors.length + validationResult.businessRules.errors.length + (validationResult.countrySpecific?.errors.length || 0)); } /** * Count warnings in validation result */ countWarnings(validationResult) { if (!validationResult) return 0; return (validationResult.syntax.warnings.length + validationResult.semantic.warnings.length + validationResult.businessRules.warnings.length + (validationResult.countrySpecific?.warnings.length || 0)); } /** * Clean up old invoices (for testing only) */ async cleanup(olderThanDays = 365) { let removed = 0; const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - olderThanDays); for (const [hash, metadata] of this.metadataCache.entries()) { if (new Date(metadata.issueDate) < cutoffDate) { this.metadataCache.delete(hash); removed++; } } this.logger.log('info', `Removed ${removed} old invoices from cache`); return removed; } /** * Set cache entry with LRU eviction */ setCacheEntry(key, value) { // Remove from access order if already exists const existingIndex = this.cacheAccessOrder.indexOf(key); if (existingIndex > -1) { this.cacheAccessOrder.splice(existingIndex, 1); } // Add to end (most recently used) this.cacheAccessOrder.push(key); this.metadataCache.set(key, value); // Evict oldest if cache is too large while (this.metadataCache.size > this.MAX_CACHE_SIZE) { const oldestKey = this.cacheAccessOrder.shift(); if (oldestKey) { this.metadataCache.delete(oldestKey); this.logger.log('debug', `Evicted invoice from cache: ${oldestKey}`); } } } /** * Get cache entry and update access order */ getCacheEntry(key) { const value = this.metadataCache.get(key); if (value) { // Move to end (most recently used) const index = this.cacheAccessOrder.indexOf(key); if (index > -1) { this.cacheAccessOrder.splice(index, 1); } this.cacheAccessOrder.push(key); } return value; } /** * Update metadata in storage and cache */ async updateMetadata(contentHash, updates) { const metadata = this.getCacheEntry(contentHash); if (!metadata) { this.logger.log('warn', `Cannot update metadata - invoice not found: ${contentHash}`); return; } // Update metadata const updatedMetadata = { ...metadata, ...updates }; this.setCacheEntry(contentHash, updatedMetadata); // Persist to disk const metadataPath = path.join(this.exportPath, 'data', 'documents', 'invoices', metadata.direction, 'metadata', `${contentHash}.json`); await plugins.smartfile.memory.toFs(JSON.stringify(updatedMetadata, null, 2), metadataPath); this.logger.log('info', `Updated metadata for invoice: ${contentHash}`); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmludm9pY2Uuc3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3Nrci5pbnZvaWNlLnN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUF1RDdCOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBUXpCLFlBQVksVUFBa0I7UUFIYixtQkFBYyxHQUFHLEtBQUssQ0FBQyxDQUFDLG1DQUFtQztRQUNwRSxxQkFBZ0IsR0FBYSxFQUFFLENBQUMsQ0FBQyxzQ0FBc0M7UUF5RDlELGlCQUFZLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQyxXQUFXO1FBdEQzRCxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQztRQUM3QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFDOUYsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7T0FFRztJQUNLLGVBQWU7UUFDckIsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDbEQscUNBQXFDO1lBQ3JDLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsNEJBQTRCO1lBQzFHLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FB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