@fin.cx/skr 1.0.0 → 1.2.0
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/index.d.ts +6 -0
- package/dist_ts/index.js +7 -1
- package/dist_ts/plugins.d.ts +8 -1
- package/dist_ts/plugins.js +10 -2
- package/dist_ts/skr.api.d.ts +70 -0
- package/dist_ts/skr.api.js +354 -3
- package/dist_ts/skr.classes.journalentry.d.ts +1 -0
- package/dist_ts/skr.classes.journalentry.js +55 -15
- package/dist_ts/skr.classes.ledger.d.ts +4 -0
- package/dist_ts/skr.classes.ledger.js +72 -1
- package/dist_ts/skr.classes.reports.js +56 -22
- package/dist_ts/skr.export.accounts.d.ts +53 -0
- package/dist_ts/skr.export.accounts.js +111 -0
- package/dist_ts/skr.export.balances.d.ts +59 -0
- package/dist_ts/skr.export.balances.js +205 -0
- package/dist_ts/skr.export.d.ts +110 -0
- package/dist_ts/skr.export.js +315 -0
- package/dist_ts/skr.export.ledger.d.ts +95 -0
- package/dist_ts/skr.export.ledger.js +164 -0
- package/dist_ts/skr.export.pdf.d.ts +82 -0
- package/dist_ts/skr.export.pdf.js +548 -0
- package/dist_ts/skr.invoice.adapter.d.ts +98 -0
- package/dist_ts/skr.invoice.adapter.js +476 -0
- package/dist_ts/skr.invoice.booking.d.ts +102 -0
- package/dist_ts/skr.invoice.booking.js +556 -0
- package/dist_ts/skr.invoice.entity.d.ts +287 -0
- package/dist_ts/skr.invoice.entity.js +2 -0
- package/dist_ts/skr.invoice.mapper.d.ts +69 -0
- package/dist_ts/skr.invoice.mapper.js +401 -0
- package/dist_ts/skr.invoice.storage.d.ts +140 -0
- package/dist_ts/skr.invoice.storage.js +529 -0
- package/dist_ts/skr.security.d.ts +65 -0
- package/dist_ts/skr.security.js +319 -0
- package/dist_ts/skr.types.d.ts +1 -0
- package/package.json +18 -12
- package/readme.md +461 -132
- package/ts/index.ts +6 -0
- package/ts/plugins.ts +22 -1
- package/ts/skr.api.ts +489 -2
- package/ts/skr.classes.journalentry.ts +63 -14
- package/ts/skr.classes.ledger.ts +85 -0
- package/ts/skr.classes.reports.ts +64 -21
- package/ts/skr.export.accounts.ts +154 -0
- package/ts/skr.export.balances.ts +270 -0
- package/ts/skr.export.ledger.ts +249 -0
- package/ts/skr.export.pdf.ts +601 -0
- package/ts/skr.export.ts +443 -0
- package/ts/skr.invoice.adapter.ts +581 -0
- package/ts/skr.invoice.booking.ts +738 -0
- package/ts/skr.invoice.entity.ts +351 -0
- package/ts/skr.invoice.mapper.ts +486 -0
- package/ts/skr.invoice.storage.ts +710 -0
- package/ts/skr.security.ts +405 -0
- package/ts/skr.types.ts +1 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Content-addressed storage for invoices
|
|
5
|
+
* Integrates with BagIt archive structure for GoBD compliance
|
|
6
|
+
*/
|
|
7
|
+
export class InvoiceStorage {
|
|
8
|
+
constructor(exportPath) {
|
|
9
|
+
this.MAX_CACHE_SIZE = 10000; // Maximum number of cached entries
|
|
10
|
+
this.cacheAccessOrder = []; // Track access order for LRU eviction
|
|
11
|
+
this.MAX_PDF_SIZE = 50 * 1024 * 1024; // 50MB max
|
|
12
|
+
this.exportPath = exportPath;
|
|
13
|
+
this.logger = new plugins.smartlog.ConsoleLog();
|
|
14
|
+
this.registryPath = path.join(exportPath, 'data', 'documents', 'invoices', 'registry.ndjson');
|
|
15
|
+
this.metadataCache = new Map();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Manage cache size using LRU eviction
|
|
19
|
+
*/
|
|
20
|
+
manageCacheSize() {
|
|
21
|
+
if (this.metadataCache.size > this.MAX_CACHE_SIZE) {
|
|
22
|
+
// Remove least recently used entries
|
|
23
|
+
const entriesToRemove = Math.min(100, Math.floor(this.MAX_CACHE_SIZE * 0.1)); // Remove 10% or 100 entries
|
|
24
|
+
const keysToRemove = this.cacheAccessOrder.splice(0, entriesToRemove);
|
|
25
|
+
for (const key of keysToRemove) {
|
|
26
|
+
this.metadataCache.delete(key);
|
|
27
|
+
}
|
|
28
|
+
this.logger.log('info', `Evicted ${entriesToRemove} entries from metadata cache`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Update cache access order for LRU
|
|
33
|
+
*/
|
|
34
|
+
touchCacheEntry(key) {
|
|
35
|
+
const index = this.cacheAccessOrder.indexOf(key);
|
|
36
|
+
if (index > -1) {
|
|
37
|
+
this.cacheAccessOrder.splice(index, 1);
|
|
38
|
+
}
|
|
39
|
+
this.cacheAccessOrder.push(key);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Initialize storage directories
|
|
43
|
+
*/
|
|
44
|
+
async initialize() {
|
|
45
|
+
const dirs = [
|
|
46
|
+
path.join(this.exportPath, 'data', 'documents', 'invoices', 'inbound'),
|
|
47
|
+
path.join(this.exportPath, 'data', 'documents', 'invoices', 'inbound', 'metadata'),
|
|
48
|
+
path.join(this.exportPath, 'data', 'documents', 'invoices', 'outbound'),
|
|
49
|
+
path.join(this.exportPath, 'data', 'documents', 'invoices', 'outbound', 'metadata'),
|
|
50
|
+
path.join(this.exportPath, 'data', 'validation')
|
|
51
|
+
];
|
|
52
|
+
for (const dir of dirs) {
|
|
53
|
+
await plugins.smartfile.fs.ensureDir(dir);
|
|
54
|
+
}
|
|
55
|
+
// Load existing registry if it exists
|
|
56
|
+
await this.loadRegistry();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Store an invoice with content addressing
|
|
60
|
+
*/
|
|
61
|
+
async storeInvoice(invoice, pdfBuffer) {
|
|
62
|
+
try {
|
|
63
|
+
// Validate PDF size if provided
|
|
64
|
+
if (pdfBuffer && pdfBuffer.length > this.MAX_PDF_SIZE) {
|
|
65
|
+
throw new Error(`PDF file too large: ${pdfBuffer.length} bytes (max ${this.MAX_PDF_SIZE} bytes)`);
|
|
66
|
+
}
|
|
67
|
+
// Calculate hashes
|
|
68
|
+
const xmlHash = await this.calculateHash(invoice.xmlContent || '');
|
|
69
|
+
const pdfHash = pdfBuffer ? await this.calculateHash(pdfBuffer) : undefined;
|
|
70
|
+
const contentHash = xmlHash; // Primary content hash is XML
|
|
71
|
+
// Check for duplicates
|
|
72
|
+
const duplicateCheck = await this.checkDuplicate(invoice, contentHash);
|
|
73
|
+
if (duplicateCheck.isDuplicate) {
|
|
74
|
+
this.logger.log('warn', `Duplicate invoice detected: ${invoice.invoiceNumber}`);
|
|
75
|
+
return duplicateCheck.matchedContentHash || contentHash;
|
|
76
|
+
}
|
|
77
|
+
// Determine storage path
|
|
78
|
+
const direction = invoice.direction;
|
|
79
|
+
const basePath = path.join(this.exportPath, 'data', 'documents', 'invoices', direction);
|
|
80
|
+
// Create filename with content hash
|
|
81
|
+
const dateStr = invoice.issueDate.toISOString().split('T')[0];
|
|
82
|
+
const sanitizedNumber = invoice.invoiceNumber.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
83
|
+
const xmlFilename = `${contentHash.substring(0, 8)}_${dateStr}_${sanitizedNumber}.xml`;
|
|
84
|
+
const xmlPath = path.join(basePath, xmlFilename);
|
|
85
|
+
// Store XML
|
|
86
|
+
await plugins.smartfile.memory.toFs(invoice.xmlContent || '', xmlPath);
|
|
87
|
+
// Store PDF if available
|
|
88
|
+
let pdfFilename;
|
|
89
|
+
if (pdfBuffer) {
|
|
90
|
+
pdfFilename = xmlFilename.replace('.xml', '.pdf');
|
|
91
|
+
const pdfPath = path.join(basePath, pdfFilename);
|
|
92
|
+
await plugins.smartfile.memory.toFs(pdfBuffer, pdfPath);
|
|
93
|
+
// Also store PDF/A-3 with embedded XML if supported
|
|
94
|
+
if (invoice.format === 'zugferd' || invoice.format === 'facturx') {
|
|
95
|
+
const pdfA3Filename = xmlFilename.replace('.xml', '_pdfa3.pdf');
|
|
96
|
+
const pdfA3Path = path.join(basePath, pdfA3Filename);
|
|
97
|
+
// The PDF should already have embedded XML if it's ZUGFeRD/Factur-X
|
|
98
|
+
await plugins.smartfile.memory.toFs(pdfBuffer, pdfA3Path);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Create and store metadata
|
|
102
|
+
const metadata = {
|
|
103
|
+
invoiceId: invoice.id,
|
|
104
|
+
invoiceNumber: invoice.invoiceNumber,
|
|
105
|
+
direction: invoice.direction,
|
|
106
|
+
issueDate: invoice.issueDate.toISOString(),
|
|
107
|
+
supplierName: invoice.supplier.name,
|
|
108
|
+
customerName: invoice.customer.name,
|
|
109
|
+
totalAmount: invoice.payableAmount,
|
|
110
|
+
currency: invoice.currencyCode,
|
|
111
|
+
contentHash,
|
|
112
|
+
pdfHash,
|
|
113
|
+
xmlHash,
|
|
114
|
+
journalEntryId: invoice.bookingInfo?.journalEntryId,
|
|
115
|
+
transactionIds: invoice.bookingInfo?.transactionIds,
|
|
116
|
+
validationResult: {
|
|
117
|
+
isValid: invoice.validationResult?.isValid || false,
|
|
118
|
+
errors: this.countErrors(invoice.validationResult),
|
|
119
|
+
warnings: this.countWarnings(invoice.validationResult)
|
|
120
|
+
},
|
|
121
|
+
parserVersion: invoice.metadata?.parserVersion || '5.1.4',
|
|
122
|
+
storedAt: new Date().toISOString(),
|
|
123
|
+
storedBy: invoice.createdBy
|
|
124
|
+
};
|
|
125
|
+
const metadataPath = path.join(basePath, 'metadata', `${contentHash}.json`);
|
|
126
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(metadata, null, 2), metadataPath);
|
|
127
|
+
// Update registry
|
|
128
|
+
await this.updateRegistry(invoice.id, contentHash, metadata);
|
|
129
|
+
// Cache metadata with LRU management
|
|
130
|
+
this.setCacheEntry(contentHash, metadata);
|
|
131
|
+
this.logger.log('info', `Invoice stored: ${invoice.invoiceNumber} (${contentHash})`);
|
|
132
|
+
return contentHash;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
this.logger.log('error', `Failed to store invoice: ${error}`);
|
|
136
|
+
throw new Error(`Invoice storage failed: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Retrieve an invoice by content hash
|
|
141
|
+
*/
|
|
142
|
+
async retrieveInvoice(contentHash) {
|
|
143
|
+
try {
|
|
144
|
+
// Check cache first
|
|
145
|
+
const metadata = this.getCacheEntry(contentHash);
|
|
146
|
+
if (!metadata) {
|
|
147
|
+
this.logger.log('warn', `Invoice not found: ${contentHash}`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// Load XML content
|
|
151
|
+
const xmlPath = await this.findInvoiceFile(contentHash, '.xml');
|
|
152
|
+
if (!xmlPath) {
|
|
153
|
+
throw new Error(`XML file not found for invoice ${contentHash}`);
|
|
154
|
+
}
|
|
155
|
+
const xmlContent = await plugins.smartfile.fs.toStringSync(xmlPath);
|
|
156
|
+
// Load PDF if exists
|
|
157
|
+
let pdfContent;
|
|
158
|
+
const pdfPath = await this.findInvoiceFile(contentHash, '.pdf');
|
|
159
|
+
if (pdfPath) {
|
|
160
|
+
pdfContent = await plugins.smartfile.fs.toBuffer(pdfPath);
|
|
161
|
+
}
|
|
162
|
+
// Reconstruct invoice object (partial)
|
|
163
|
+
const invoice = {
|
|
164
|
+
id: metadata.invoiceId,
|
|
165
|
+
invoiceNumber: metadata.invoiceNumber,
|
|
166
|
+
direction: metadata.direction,
|
|
167
|
+
issueDate: new Date(metadata.issueDate),
|
|
168
|
+
supplier: {
|
|
169
|
+
name: metadata.supplierName,
|
|
170
|
+
id: '',
|
|
171
|
+
address: { countryCode: 'DE' }
|
|
172
|
+
},
|
|
173
|
+
customer: {
|
|
174
|
+
name: metadata.customerName,
|
|
175
|
+
id: '',
|
|
176
|
+
address: { countryCode: 'DE' }
|
|
177
|
+
},
|
|
178
|
+
payableAmount: metadata.totalAmount,
|
|
179
|
+
currencyCode: metadata.currency,
|
|
180
|
+
contentHash: metadata.contentHash,
|
|
181
|
+
xmlContent,
|
|
182
|
+
pdfContent,
|
|
183
|
+
pdfHash: metadata.pdfHash
|
|
184
|
+
};
|
|
185
|
+
return invoice;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
this.logger.log('error', `Failed to retrieve invoice: ${error}`);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Check for duplicate invoices
|
|
194
|
+
*/
|
|
195
|
+
async checkDuplicate(invoice, contentHash) {
|
|
196
|
+
// Check by content hash (exact match)
|
|
197
|
+
const existing = this.getCacheEntry(contentHash);
|
|
198
|
+
if (existing) {
|
|
199
|
+
return {
|
|
200
|
+
isDuplicate: true,
|
|
201
|
+
matchedInvoiceId: existing.invoiceId,
|
|
202
|
+
matchedContentHash: contentHash,
|
|
203
|
+
matchedFields: ['contentHash'],
|
|
204
|
+
confidence: 100
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// Check by invoice number and supplier/customer
|
|
208
|
+
for (const [hash, metadata] of this.metadataCache.entries()) {
|
|
209
|
+
if (metadata.invoiceNumber === invoice.invoiceNumber &&
|
|
210
|
+
metadata.direction === invoice.direction) {
|
|
211
|
+
// Same invoice number and direction
|
|
212
|
+
if (invoice.direction === 'inbound' && metadata.supplierName === invoice.supplier.name) {
|
|
213
|
+
// Same supplier
|
|
214
|
+
return {
|
|
215
|
+
isDuplicate: true,
|
|
216
|
+
matchedInvoiceId: metadata.invoiceId,
|
|
217
|
+
matchedContentHash: hash,
|
|
218
|
+
matchedFields: ['invoiceNumber', 'supplier'],
|
|
219
|
+
confidence: 95
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
else if (invoice.direction === 'outbound' && metadata.customerName === invoice.customer.name) {
|
|
223
|
+
// Same customer
|
|
224
|
+
return {
|
|
225
|
+
isDuplicate: true,
|
|
226
|
+
matchedInvoiceId: metadata.invoiceId,
|
|
227
|
+
matchedContentHash: hash,
|
|
228
|
+
matchedFields: ['invoiceNumber', 'customer'],
|
|
229
|
+
confidence: 95
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Check by amount and date within tolerance
|
|
234
|
+
const dateTolerance = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
235
|
+
const amountTolerance = 0.01;
|
|
236
|
+
if (Math.abs(metadata.totalAmount - invoice.payableAmount) < amountTolerance &&
|
|
237
|
+
Math.abs(new Date(metadata.issueDate).getTime() - invoice.issueDate.getTime()) < dateTolerance &&
|
|
238
|
+
metadata.direction === invoice.direction) {
|
|
239
|
+
if ((invoice.direction === 'inbound' && metadata.supplierName === invoice.supplier.name) ||
|
|
240
|
+
(invoice.direction === 'outbound' && metadata.customerName === invoice.customer.name)) {
|
|
241
|
+
return {
|
|
242
|
+
isDuplicate: true,
|
|
243
|
+
matchedInvoiceId: metadata.invoiceId,
|
|
244
|
+
matchedContentHash: hash,
|
|
245
|
+
matchedFields: ['amount', 'date', 'party'],
|
|
246
|
+
confidence: 85
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
isDuplicate: false,
|
|
253
|
+
confidence: 0
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Search invoices by filter
|
|
258
|
+
*/
|
|
259
|
+
async searchInvoices(filter) {
|
|
260
|
+
const results = [];
|
|
261
|
+
for (const metadata of this.metadataCache.values()) {
|
|
262
|
+
if (this.matchesFilter(metadata, filter)) {
|
|
263
|
+
results.push(metadata);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Sort by date descending
|
|
267
|
+
results.sort((a, b) => new Date(b.issueDate).getTime() - new Date(a.issueDate).getTime());
|
|
268
|
+
return results;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Get storage statistics
|
|
272
|
+
*/
|
|
273
|
+
async getStatistics() {
|
|
274
|
+
let totalSize = 0;
|
|
275
|
+
let inboundCount = 0;
|
|
276
|
+
let outboundCount = 0;
|
|
277
|
+
for (const metadata of this.metadataCache.values()) {
|
|
278
|
+
if (metadata.direction === 'inbound') {
|
|
279
|
+
inboundCount++;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
outboundCount++;
|
|
283
|
+
}
|
|
284
|
+
// Estimate size (would need actual file sizes in production)
|
|
285
|
+
totalSize += 50000; // Rough estimate
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
totalInvoices: this.metadataCache.size,
|
|
289
|
+
inboundCount,
|
|
290
|
+
outboundCount,
|
|
291
|
+
totalSize,
|
|
292
|
+
duplicatesDetected: 0, // Would track this in production
|
|
293
|
+
lastUpdate: new Date()
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Create EN16931 compliance report
|
|
298
|
+
*/
|
|
299
|
+
async createComplianceReport() {
|
|
300
|
+
const report = {
|
|
301
|
+
timestamp: new Date().toISOString(),
|
|
302
|
+
totalInvoices: this.metadataCache.size,
|
|
303
|
+
validInvoices: 0,
|
|
304
|
+
invalidInvoices: 0,
|
|
305
|
+
warnings: 0,
|
|
306
|
+
byFormat: {},
|
|
307
|
+
byDirection: {
|
|
308
|
+
inbound: 0,
|
|
309
|
+
outbound: 0
|
|
310
|
+
},
|
|
311
|
+
validationErrors: [],
|
|
312
|
+
complianceLevel: 'EN16931',
|
|
313
|
+
validatorVersion: '5.1.4'
|
|
314
|
+
};
|
|
315
|
+
for (const metadata of this.metadataCache.values()) {
|
|
316
|
+
if (metadata.validationResult.isValid) {
|
|
317
|
+
report.validInvoices++;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
report.invalidInvoices++;
|
|
321
|
+
}
|
|
322
|
+
report.warnings += metadata.validationResult.warnings;
|
|
323
|
+
if (metadata.direction === 'inbound') {
|
|
324
|
+
report.byDirection.inbound++;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
report.byDirection.outbound++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
const reportPath = path.join(this.exportPath, 'data', 'validation', 'en16931_compliance.json');
|
|
331
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(report, null, 2), reportPath);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Load registry from disk
|
|
335
|
+
*/
|
|
336
|
+
async loadRegistry() {
|
|
337
|
+
try {
|
|
338
|
+
if (await plugins.smartfile.fs.fileExists(this.registryPath)) {
|
|
339
|
+
const content = await plugins.smartfile.fs.toStringSync(this.registryPath);
|
|
340
|
+
const lines = content.split('\n').filter(line => line.trim());
|
|
341
|
+
for (const line of lines) {
|
|
342
|
+
try {
|
|
343
|
+
const entry = JSON.parse(line);
|
|
344
|
+
this.setCacheEntry(entry.hash, entry.metadata);
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
this.logger.log('warn', `Invalid registry entry: ${line}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
this.logger.log('info', `Loaded ${this.metadataCache.size} invoices from registry`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
this.logger.log('error', `Failed to load registry: ${error}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Update registry with new entry
|
|
359
|
+
*/
|
|
360
|
+
async updateRegistry(invoiceId, contentHash, metadata) {
|
|
361
|
+
try {
|
|
362
|
+
const entry = {
|
|
363
|
+
id: invoiceId,
|
|
364
|
+
hash: contentHash,
|
|
365
|
+
metadata
|
|
366
|
+
};
|
|
367
|
+
// Append to NDJSON file
|
|
368
|
+
const line = JSON.stringify(entry) + '\n';
|
|
369
|
+
await plugins.smartfile.fs.ensureDir(path.dirname(this.registryPath));
|
|
370
|
+
// Use native fs for atomic append (better performance and concurrency safety)
|
|
371
|
+
const fs = await import('fs/promises');
|
|
372
|
+
await fs.appendFile(this.registryPath, line, 'utf8');
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
this.logger.log('error', `Failed to update registry: ${error}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Find invoice file by hash and extension
|
|
380
|
+
*/
|
|
381
|
+
async findInvoiceFile(contentHash, extension) {
|
|
382
|
+
const dirs = [
|
|
383
|
+
path.join(this.exportPath, 'data', 'documents', 'invoices', 'inbound'),
|
|
384
|
+
path.join(this.exportPath, 'data', 'documents', 'invoices', 'outbound')
|
|
385
|
+
];
|
|
386
|
+
for (const dir of dirs) {
|
|
387
|
+
const files = await plugins.smartfile.fs.listFileTree(dir, '**/*' + extension);
|
|
388
|
+
for (const file of files) {
|
|
389
|
+
if (file.includes(contentHash.substring(0, 8))) {
|
|
390
|
+
return path.join(dir, file);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Calculate SHA-256 hash
|
|
398
|
+
*/
|
|
399
|
+
async calculateHash(data) {
|
|
400
|
+
if (typeof data === 'string') {
|
|
401
|
+
return await plugins.smarthash.sha256FromString(data);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
return await plugins.smarthash.sha256FromBuffer(data);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Check if metadata matches filter
|
|
409
|
+
*/
|
|
410
|
+
matchesFilter(metadata, filter) {
|
|
411
|
+
if (filter.direction && metadata.direction !== filter.direction) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
if (filter.dateFrom && new Date(metadata.issueDate) < filter.dateFrom) {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if (filter.dateTo && new Date(metadata.issueDate) > filter.dateTo) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
if (filter.minAmount && metadata.totalAmount < filter.minAmount) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
if (filter.maxAmount && metadata.totalAmount > filter.maxAmount) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
if (filter.invoiceNumber && !metadata.invoiceNumber.includes(filter.invoiceNumber)) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (filter.supplierId && !metadata.supplierName.includes(filter.supplierId)) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
if (filter.customerId && !metadata.customerName.includes(filter.customerId)) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Count errors in validation result
|
|
439
|
+
*/
|
|
440
|
+
countErrors(validationResult) {
|
|
441
|
+
if (!validationResult)
|
|
442
|
+
return 0;
|
|
443
|
+
return (validationResult.syntax.errors.length +
|
|
444
|
+
validationResult.semantic.errors.length +
|
|
445
|
+
validationResult.businessRules.errors.length +
|
|
446
|
+
(validationResult.countrySpecific?.errors.length || 0));
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Count warnings in validation result
|
|
450
|
+
*/
|
|
451
|
+
countWarnings(validationResult) {
|
|
452
|
+
if (!validationResult)
|
|
453
|
+
return 0;
|
|
454
|
+
return (validationResult.syntax.warnings.length +
|
|
455
|
+
validationResult.semantic.warnings.length +
|
|
456
|
+
validationResult.businessRules.warnings.length +
|
|
457
|
+
(validationResult.countrySpecific?.warnings.length || 0));
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Clean up old invoices (for testing only)
|
|
461
|
+
*/
|
|
462
|
+
async cleanup(olderThanDays = 365) {
|
|
463
|
+
let removed = 0;
|
|
464
|
+
const cutoffDate = new Date();
|
|
465
|
+
cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);
|
|
466
|
+
for (const [hash, metadata] of this.metadataCache.entries()) {
|
|
467
|
+
if (new Date(metadata.issueDate) < cutoffDate) {
|
|
468
|
+
this.metadataCache.delete(hash);
|
|
469
|
+
removed++;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
this.logger.log('info', `Removed ${removed} old invoices from cache`);
|
|
473
|
+
return removed;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Set cache entry with LRU eviction
|
|
477
|
+
*/
|
|
478
|
+
setCacheEntry(key, value) {
|
|
479
|
+
// Remove from access order if already exists
|
|
480
|
+
const existingIndex = this.cacheAccessOrder.indexOf(key);
|
|
481
|
+
if (existingIndex > -1) {
|
|
482
|
+
this.cacheAccessOrder.splice(existingIndex, 1);
|
|
483
|
+
}
|
|
484
|
+
// Add to end (most recently used)
|
|
485
|
+
this.cacheAccessOrder.push(key);
|
|
486
|
+
this.metadataCache.set(key, value);
|
|
487
|
+
// Evict oldest if cache is too large
|
|
488
|
+
while (this.metadataCache.size > this.MAX_CACHE_SIZE) {
|
|
489
|
+
const oldestKey = this.cacheAccessOrder.shift();
|
|
490
|
+
if (oldestKey) {
|
|
491
|
+
this.metadataCache.delete(oldestKey);
|
|
492
|
+
this.logger.log('debug', `Evicted invoice from cache: ${oldestKey}`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Get cache entry and update access order
|
|
498
|
+
*/
|
|
499
|
+
getCacheEntry(key) {
|
|
500
|
+
const value = this.metadataCache.get(key);
|
|
501
|
+
if (value) {
|
|
502
|
+
// Move to end (most recently used)
|
|
503
|
+
const index = this.cacheAccessOrder.indexOf(key);
|
|
504
|
+
if (index > -1) {
|
|
505
|
+
this.cacheAccessOrder.splice(index, 1);
|
|
506
|
+
}
|
|
507
|
+
this.cacheAccessOrder.push(key);
|
|
508
|
+
}
|
|
509
|
+
return value;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Update metadata in storage and cache
|
|
513
|
+
*/
|
|
514
|
+
async updateMetadata(contentHash, updates) {
|
|
515
|
+
const metadata = this.getCacheEntry(contentHash);
|
|
516
|
+
if (!metadata) {
|
|
517
|
+
this.logger.log('warn', `Cannot update metadata - invoice not found: ${contentHash}`);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
// Update metadata
|
|
521
|
+
const updatedMetadata = { ...metadata, ...updates };
|
|
522
|
+
this.setCacheEntry(contentHash, updatedMetadata);
|
|
523
|
+
// Persist to disk
|
|
524
|
+
const metadataPath = path.join(this.exportPath, 'data', 'documents', 'invoices', metadata.direction, 'metadata', `${contentHash}.json`);
|
|
525
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(updatedMetadata, null, 2), metadataPath);
|
|
526
|
+
this.logger.log('info', `Updated metadata for invoice: ${contentHash}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmludm9pY2Uuc3RvcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3Nrci5pbnZvaWNlLnN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUF1RDdCOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBUXpCLFlBQVksVUFBa0I7UUFIYixtQkFBYyxHQUFHLEtBQUssQ0FBQyxDQUFDLG1DQUFtQztRQUNwRSxxQkFBZ0IsR0FBYSxFQUFFLENBQUMsQ0FBQyxzQ0FBc0M7UUF5RDlELGlCQUFZLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQyxXQUFXO1FBdEQzRCxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQztRQUM3QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLGlCQUFpQixDQUFDLENBQUM7UUFDOUYsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7T0FFRztJQUNLLGVBQWU7UUFDckIsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDbEQscUNBQXFDO1lBQ3JDLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsNEJBQTRCO1lBQzFHLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FB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
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface ISigningOptions {
|
|
2
|
+
certificatePem?: string;
|
|
3
|
+
privateKeyPem?: string;
|
|
4
|
+
privateKeyPassphrase?: string;
|
|
5
|
+
timestampServerUrl?: string;
|
|
6
|
+
includeTimestamp?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface ISignatureResult {
|
|
9
|
+
signature: string;
|
|
10
|
+
signatureFormat: 'CAdES-B' | 'CAdES-T' | 'CAdES-LT';
|
|
11
|
+
signingTime: string;
|
|
12
|
+
certificateChain?: string[];
|
|
13
|
+
timestampToken?: string;
|
|
14
|
+
timestampTime?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ITimestampResponse {
|
|
17
|
+
token: string;
|
|
18
|
+
time: string;
|
|
19
|
+
serverUrl: string;
|
|
20
|
+
hashAlgorithm: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class SecurityManager {
|
|
23
|
+
private options;
|
|
24
|
+
private logger;
|
|
25
|
+
constructor(options?: ISigningOptions);
|
|
26
|
+
/**
|
|
27
|
+
* Creates a CAdES-B (Basic) signature for data
|
|
28
|
+
*/
|
|
29
|
+
createCadesSignature(data: Buffer | string, certificatePem?: string, privateKeyPem?: string): Promise<ISignatureResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Requests an RFC 3161 timestamp from a TSA
|
|
32
|
+
*/
|
|
33
|
+
requestTimestamp(dataHash: string | Buffer): Promise<ITimestampResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a timestamp request (simplified version)
|
|
36
|
+
*/
|
|
37
|
+
private createTimestampRequest;
|
|
38
|
+
/**
|
|
39
|
+
* Sends timestamp request to TSA server
|
|
40
|
+
*/
|
|
41
|
+
private sendTimestampRequest;
|
|
42
|
+
/**
|
|
43
|
+
* Verifies a CAdES signature
|
|
44
|
+
*/
|
|
45
|
+
verifyCadesSignature(data: Buffer | string, signature: string, certificatePem?: string): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Generates a self-signed certificate for testing
|
|
48
|
+
*/
|
|
49
|
+
generateSelfSignedCertificate(commonName?: string, validDays?: number): Promise<{
|
|
50
|
+
certificate: string;
|
|
51
|
+
privateKey: string;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Creates a detached signature file
|
|
55
|
+
*/
|
|
56
|
+
createDetachedSignature(dataPath: string, outputPath: string): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Verifies a detached signature file
|
|
59
|
+
*/
|
|
60
|
+
verifyDetachedSignature(dataPath: string, signaturePath: string): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Adds Long-Term Validation (LTV) information
|
|
63
|
+
*/
|
|
64
|
+
addLtvInformation(signature: ISignatureResult, ocspResponse?: Buffer, crlData?: Buffer): Promise<ISignatureResult>;
|
|
65
|
+
}
|