@fin.cx/skr
Version:
SKR03 and SKR04 German accounting standards for double-entry bookkeeping
529 lines • 44.5 kB
JavaScript
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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