@fin.cx/skr 1.1.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.
Files changed (46) hide show
  1. package/dist_ts/index.d.ts +6 -0
  2. package/dist_ts/index.js +7 -1
  3. package/dist_ts/plugins.d.ts +8 -1
  4. package/dist_ts/plugins.js +10 -2
  5. package/dist_ts/skr.api.d.ts +70 -0
  6. package/dist_ts/skr.api.js +348 -1
  7. package/dist_ts/skr.export.accounts.d.ts +53 -0
  8. package/dist_ts/skr.export.accounts.js +111 -0
  9. package/dist_ts/skr.export.balances.d.ts +59 -0
  10. package/dist_ts/skr.export.balances.js +205 -0
  11. package/dist_ts/skr.export.d.ts +110 -0
  12. package/dist_ts/skr.export.js +315 -0
  13. package/dist_ts/skr.export.ledger.d.ts +95 -0
  14. package/dist_ts/skr.export.ledger.js +164 -0
  15. package/dist_ts/skr.export.pdf.d.ts +82 -0
  16. package/dist_ts/skr.export.pdf.js +548 -0
  17. package/dist_ts/skr.invoice.adapter.d.ts +98 -0
  18. package/dist_ts/skr.invoice.adapter.js +476 -0
  19. package/dist_ts/skr.invoice.booking.d.ts +102 -0
  20. package/dist_ts/skr.invoice.booking.js +556 -0
  21. package/dist_ts/skr.invoice.entity.d.ts +287 -0
  22. package/dist_ts/skr.invoice.entity.js +2 -0
  23. package/dist_ts/skr.invoice.mapper.d.ts +69 -0
  24. package/dist_ts/skr.invoice.mapper.js +401 -0
  25. package/dist_ts/skr.invoice.storage.d.ts +140 -0
  26. package/dist_ts/skr.invoice.storage.js +529 -0
  27. package/dist_ts/skr.security.d.ts +65 -0
  28. package/dist_ts/skr.security.js +319 -0
  29. package/dist_ts/skr.types.d.ts +1 -0
  30. package/package.json +17 -12
  31. package/readme.md +207 -16
  32. package/ts/index.ts +6 -0
  33. package/ts/plugins.ts +22 -1
  34. package/ts/skr.api.ts +485 -0
  35. package/ts/skr.export.accounts.ts +154 -0
  36. package/ts/skr.export.balances.ts +270 -0
  37. package/ts/skr.export.ledger.ts +249 -0
  38. package/ts/skr.export.pdf.ts +601 -0
  39. package/ts/skr.export.ts +443 -0
  40. package/ts/skr.invoice.adapter.ts +581 -0
  41. package/ts/skr.invoice.booking.ts +738 -0
  42. package/ts/skr.invoice.entity.ts +351 -0
  43. package/ts/skr.invoice.mapper.ts +486 -0
  44. package/ts/skr.invoice.storage.ts +710 -0
  45. package/ts/skr.security.ts +405 -0
  46. package/ts/skr.types.ts +1 -0
@@ -0,0 +1,401 @@
1
+ import * as plugins from './plugins.js';
2
+ /**
3
+ * Maps invoice data to SKR accounts
4
+ * Handles both SKR03 and SKR04 account mappings
5
+ */
6
+ export class SKRInvoiceMapper {
7
+ constructor(skrType) {
8
+ // SKR03 account mappings
9
+ this.SKR03_ACCOUNTS = {
10
+ // Control accounts
11
+ vendorControl: '1600', // Verbindlichkeiten aus Lieferungen und Leistungen
12
+ customerControl: '1200', // Forderungen aus Lieferungen und Leistungen
13
+ // VAT accounts
14
+ inputVAT19: '1576', // Abziehbare Vorsteuer 19%
15
+ inputVAT7: '1571', // Abziehbare Vorsteuer 7%
16
+ outputVAT19: '1776', // Umsatzsteuer 19%
17
+ outputVAT7: '1771', // Umsatzsteuer 7%
18
+ reverseChargeVAT: '1577', // Abziehbare Vorsteuer §13b UStG
19
+ reverseChargePayable: '1787', // Umsatzsteuer §13b UStG
20
+ // Default expense/revenue accounts
21
+ defaultExpense: '4610', // Werbekosten
22
+ defaultRevenue: '8400', // Erlöse 19% USt
23
+ revenueReduced: '8300', // Erlöse 7% USt
24
+ revenueTaxFree: '8120', // Steuerfreie Umsätze
25
+ // Common expense accounts by category
26
+ materialExpense: '5000', // Aufwendungen für Roh-, Hilfs- und Betriebsstoffe
27
+ merchandiseExpense: '5400', // Aufwendungen für Waren
28
+ personnelExpense: '6000', // Löhne und Gehälter
29
+ rentExpense: '4200', // Miete
30
+ officeExpense: '4930', // Bürobedarf
31
+ travelExpense: '4670', // Reisekosten
32
+ vehicleExpense: '4530', // Kfz-Kosten
33
+ // Skonto accounts
34
+ skontoExpense: '4736', // Erhaltene Skonti 19% USt
35
+ skontoRevenue: '8736', // Gewährte Skonti 19% USt
36
+ // Intra-EU accounts
37
+ intraEUAcquisition: '8125', // Steuerfreie innergemeinschaftliche Erwerbe
38
+ intraEUSupply: '8125' // Steuerfreie innergemeinschaftliche Lieferungen
39
+ };
40
+ // SKR04 account mappings
41
+ this.SKR04_ACCOUNTS = {
42
+ // Control accounts
43
+ vendorControl: '3300', // Verbindlichkeiten aus Lieferungen und Leistungen
44
+ customerControl: '1400', // Forderungen aus Lieferungen und Leistungen
45
+ // VAT accounts
46
+ inputVAT19: '1406', // Abziehbare Vorsteuer 19%
47
+ inputVAT7: '1401', // Abziehbare Vorsteuer 7%
48
+ outputVAT19: '3806', // Umsatzsteuer 19%
49
+ outputVAT7: '3801', // Umsatzsteuer 7%
50
+ reverseChargeVAT: '1407', // Abziehbare Vorsteuer §13b UStG
51
+ reverseChargePayable: '3837', // Umsatzsteuer §13b UStG
52
+ // Default expense/revenue accounts
53
+ defaultExpense: '6300', // Sonstige betriebliche Aufwendungen
54
+ defaultRevenue: '4400', // Erlöse 19% USt
55
+ revenueReduced: '4300', // Erlöse 7% USt
56
+ revenueTaxFree: '4120', // Steuerfreie Umsätze
57
+ // Common expense accounts by category
58
+ materialExpense: '5000', // Aufwendungen für Roh-, Hilfs- und Betriebsstoffe
59
+ merchandiseExpense: '5400', // Aufwendungen für Waren
60
+ personnelExpense: '6000', // Löhne
61
+ rentExpense: '6310', // Miete
62
+ officeExpense: '6815', // Bürobedarf
63
+ travelExpense: '6670', // Reisekosten
64
+ vehicleExpense: '6530', // Kfz-Kosten
65
+ // Skonto accounts
66
+ skontoExpense: '4736', // Erhaltene Skonti 19% USt
67
+ skontoRevenue: '8736', // Gewährte Skonti 19% USt
68
+ // Intra-EU accounts
69
+ intraEUAcquisition: '4125', // Steuerfreie innergemeinschaftliche Erwerbe
70
+ intraEUSupply: '4125' // Steuerfreie innergemeinschaftliche Lieferungen
71
+ };
72
+ // Product category to account mappings
73
+ this.CATEGORY_MAPPINGS = {
74
+ 'MATERIAL': { skr03: '5000', skr04: '5000' },
75
+ 'MERCHANDISE': { skr03: '5400', skr04: '5400' },
76
+ 'SERVICE': { skr03: '4610', skr04: '6300' },
77
+ 'OFFICE': { skr03: '4930', skr04: '6815' },
78
+ 'IT': { skr03: '4940', skr04: '6825' },
79
+ 'TRAVEL': { skr03: '4670', skr04: '6670' },
80
+ 'VEHICLE': { skr03: '4530', skr04: '6530' },
81
+ 'RENT': { skr03: '4200', skr04: '6310' },
82
+ 'UTILITIES': { skr03: '4240', skr04: '6320' },
83
+ 'INSURANCE': { skr03: '4360', skr04: '6420' },
84
+ 'MARKETING': { skr03: '4610', skr04: '6600' },
85
+ 'CONSULTING': { skr03: '4640', skr04: '6650' },
86
+ 'LEGAL': { skr03: '4790', skr04: '6790' },
87
+ 'TELECOMMUNICATION': { skr03: '4920', skr04: '6805' }
88
+ };
89
+ this.skrType = skrType;
90
+ this.logger = new plugins.smartlog.ConsoleLog();
91
+ }
92
+ /**
93
+ * Get account mappings for current SKR type
94
+ */
95
+ getAccounts() {
96
+ return this.skrType === 'SKR03' ? this.SKR03_ACCOUNTS : this.SKR04_ACCOUNTS;
97
+ }
98
+ /**
99
+ * Map invoice to booking rules
100
+ */
101
+ mapInvoiceToSKR(invoice, customMappings) {
102
+ const accounts = this.getAccounts();
103
+ const taxScenario = invoice.taxScenario || 'domestic_taxed';
104
+ // Base booking rules
105
+ const bookingRules = {
106
+ skrType: this.skrType,
107
+ // Control accounts
108
+ vendorControlAccount: customMappings?.vendorControlAccount || accounts.vendorControl,
109
+ customerControlAccount: customMappings?.customerControlAccount || accounts.customerControl,
110
+ // VAT accounts
111
+ vatAccounts: {
112
+ inputVAT19: accounts.inputVAT19,
113
+ inputVAT7: accounts.inputVAT7,
114
+ outputVAT19: accounts.outputVAT19,
115
+ outputVAT7: accounts.outputVAT7,
116
+ reverseChargeVAT: accounts.reverseChargeVAT
117
+ },
118
+ // Default accounts
119
+ defaultExpenseAccount: accounts.defaultExpense,
120
+ defaultRevenueAccount: accounts.defaultRevenue,
121
+ // Skonto
122
+ skontoMethod: customMappings?.skontoMethod || 'gross',
123
+ skontoExpenseAccount: accounts.skontoExpense,
124
+ skontoRevenueAccount: accounts.skontoRevenue,
125
+ // Custom mappings
126
+ productCategoryMapping: customMappings?.productCategoryMapping || {},
127
+ vendorMapping: customMappings?.vendorMapping || {},
128
+ customerMapping: customMappings?.customerMapping || {}
129
+ };
130
+ return bookingRules;
131
+ }
132
+ /**
133
+ * Map invoice line to SKR account
134
+ */
135
+ mapInvoiceLineToAccount(line, invoice, bookingRules) {
136
+ // Check if account is already specified
137
+ if (line.accountNumber) {
138
+ return line.accountNumber;
139
+ }
140
+ // For revenue (outbound invoices)
141
+ if (invoice.direction === 'outbound') {
142
+ return this.mapRevenueAccount(line, invoice, bookingRules);
143
+ }
144
+ // For expenses (inbound invoices)
145
+ return this.mapExpenseAccount(line, invoice, bookingRules);
146
+ }
147
+ /**
148
+ * Map revenue account based on VAT rate and scenario
149
+ */
150
+ mapRevenueAccount(line, invoice, bookingRules) {
151
+ const accounts = this.getAccounts();
152
+ const vatRate = line.vatCategory.rate;
153
+ // Check tax scenario
154
+ switch (invoice.taxScenario) {
155
+ case 'intra_eu_supply':
156
+ return accounts.intraEUSupply;
157
+ case 'export':
158
+ case 'domestic_exempt':
159
+ return accounts.revenueTaxFree;
160
+ case 'domestic_taxed':
161
+ default:
162
+ // Map by VAT rate
163
+ if (vatRate === 19) {
164
+ return accounts.defaultRevenue;
165
+ }
166
+ else if (vatRate === 7) {
167
+ return accounts.revenueReduced;
168
+ }
169
+ else if (vatRate === 0) {
170
+ return accounts.revenueTaxFree;
171
+ }
172
+ return accounts.defaultRevenue;
173
+ }
174
+ }
175
+ /**
176
+ * Map expense account based on product category and vendor
177
+ */
178
+ mapExpenseAccount(line, invoice, bookingRules) {
179
+ const accounts = this.getAccounts();
180
+ // Check vendor-specific mapping
181
+ const vendorId = invoice.supplier.id;
182
+ if (bookingRules.vendorMapping && bookingRules.vendorMapping[vendorId]) {
183
+ return bookingRules.vendorMapping[vendorId];
184
+ }
185
+ // Try to determine category from line description
186
+ const category = this.detectProductCategory(line.description);
187
+ if (category) {
188
+ const mapping = this.CATEGORY_MAPPINGS[category];
189
+ if (mapping) {
190
+ return this.skrType === 'SKR03' ? mapping.skr03 : mapping.skr04;
191
+ }
192
+ }
193
+ // Check product category mapping
194
+ if (line.productCode && bookingRules.productCategoryMapping) {
195
+ const mappedAccount = bookingRules.productCategoryMapping[line.productCode];
196
+ if (mappedAccount) {
197
+ return mappedAccount;
198
+ }
199
+ }
200
+ // Default expense account
201
+ return bookingRules.defaultExpenseAccount;
202
+ }
203
+ /**
204
+ * Detect product category from description
205
+ */
206
+ detectProductCategory(description) {
207
+ const lowerDesc = description.toLowerCase();
208
+ const categoryKeywords = {
209
+ 'MATERIAL': ['material', 'rohstoff', 'raw material', 'component'],
210
+ 'MERCHANDISE': ['ware', 'merchandise', 'product', 'artikel'],
211
+ 'SERVICE': ['service', 'dienstleistung', 'beratung', 'support'],
212
+ 'OFFICE': ['büro', 'office', 'papier', 'stationery'],
213
+ 'IT': ['software', 'hardware', 'computer', 'lizenz', 'license'],
214
+ 'TRAVEL': ['reise', 'travel', 'hotel', 'flug', 'flight'],
215
+ 'VEHICLE': ['kfz', 'vehicle', 'auto', 'benzin', 'fuel'],
216
+ 'RENT': ['miete', 'rent', 'lease', 'pacht'],
217
+ 'UTILITIES': ['strom', 'wasser', 'gas', 'energie', 'electricity', 'water'],
218
+ 'INSURANCE': ['versicherung', 'insurance'],
219
+ 'MARKETING': ['werbung', 'marketing', 'advertising', 'kampagne'],
220
+ 'CONSULTING': ['beratung', 'consulting', 'advisory'],
221
+ 'LEGAL': ['rechts', 'legal', 'anwalt', 'lawyer', 'notar'],
222
+ 'TELECOMMUNICATION': ['telefon', 'internet', 'mobilfunk', 'telekom']
223
+ };
224
+ for (const [category, keywords] of Object.entries(categoryKeywords)) {
225
+ if (keywords.some(keyword => lowerDesc.includes(keyword))) {
226
+ return category;
227
+ }
228
+ }
229
+ return undefined;
230
+ }
231
+ /**
232
+ * Get VAT account for given VAT category and rate
233
+ */
234
+ getVATAccount(vatCategory, direction, taxScenario) {
235
+ const accounts = this.getAccounts();
236
+ // Handle reverse charge
237
+ if (taxScenario === 'reverse_charge' || vatCategory.code === 'AE') {
238
+ return direction === 'input'
239
+ ? accounts.reverseChargeVAT
240
+ : accounts.reverseChargePayable;
241
+ }
242
+ // Standard VAT accounts by rate
243
+ if (direction === 'input') {
244
+ if (vatCategory.rate === 19) {
245
+ return accounts.inputVAT19;
246
+ }
247
+ else if (vatCategory.rate === 7) {
248
+ return accounts.inputVAT7;
249
+ }
250
+ }
251
+ else {
252
+ if (vatCategory.rate === 19) {
253
+ return accounts.outputVAT19;
254
+ }
255
+ else if (vatCategory.rate === 7) {
256
+ return accounts.outputVAT7;
257
+ }
258
+ }
259
+ // Default to 19% if rate is not standard
260
+ return direction === 'input' ? accounts.inputVAT19 : accounts.outputVAT19;
261
+ }
262
+ /**
263
+ * Get control account for party
264
+ */
265
+ getControlAccount(invoice, bookingRules) {
266
+ if (invoice.direction === 'inbound') {
267
+ // Check vendor-specific control account
268
+ const vendorId = invoice.supplier.id;
269
+ if (bookingRules.vendorMapping && bookingRules.vendorMapping[vendorId]) {
270
+ const customAccount = bookingRules.vendorMapping[vendorId];
271
+ // Check if it's a control account (starts with 16 for SKR03 or 33 for SKR04)
272
+ if (this.isControlAccount(customAccount)) {
273
+ return customAccount;
274
+ }
275
+ }
276
+ return bookingRules.vendorControlAccount;
277
+ }
278
+ else {
279
+ // Check customer-specific control account
280
+ const customerId = invoice.customer.id;
281
+ if (bookingRules.customerMapping && bookingRules.customerMapping[customerId]) {
282
+ const customAccount = bookingRules.customerMapping[customerId];
283
+ // Check if it's a control account (starts with 12 for SKR03 or 14 for SKR04)
284
+ if (this.isControlAccount(customAccount)) {
285
+ return customAccount;
286
+ }
287
+ }
288
+ return bookingRules.customerControlAccount;
289
+ }
290
+ }
291
+ /**
292
+ * Check if account is a control account
293
+ */
294
+ isControlAccount(accountNumber) {
295
+ if (this.skrType === 'SKR03') {
296
+ return accountNumber.startsWith('12') || accountNumber.startsWith('16');
297
+ }
298
+ else {
299
+ return accountNumber.startsWith('14') || accountNumber.startsWith('33');
300
+ }
301
+ }
302
+ /**
303
+ * Get skonto accounts
304
+ */
305
+ getSkontoAccounts(invoice) {
306
+ const accounts = this.getAccounts();
307
+ if (invoice.direction === 'inbound') {
308
+ // Received skonto (expense reduction)
309
+ return {
310
+ skontoAccount: accounts.skontoExpense,
311
+ vatCorrectionAccount: accounts.inputVAT19 // VAT correction
312
+ };
313
+ }
314
+ else {
315
+ // Granted skonto (revenue reduction)
316
+ return {
317
+ skontoAccount: accounts.skontoRevenue,
318
+ vatCorrectionAccount: accounts.outputVAT19 // VAT correction
319
+ };
320
+ }
321
+ }
322
+ /**
323
+ * Validate account number format
324
+ */
325
+ validateAccountNumber(accountNumber) {
326
+ // SKR accounts are typically 4 digits, sometimes with sub-accounts
327
+ const accountPattern = /^\d{4}(\d{0,2})?$/;
328
+ return accountPattern.test(accountNumber);
329
+ }
330
+ /**
331
+ * Get account description
332
+ */
333
+ getAccountDescription(accountNumber) {
334
+ // This would typically look up from a complete SKR account database
335
+ // For now, return a basic description
336
+ const commonAccounts = {
337
+ // SKR03
338
+ '1200': 'Forderungen aus Lieferungen und Leistungen',
339
+ '1600': 'Verbindlichkeiten aus Lieferungen und Leistungen',
340
+ '1576': 'Abziehbare Vorsteuer 19%',
341
+ '1571': 'Abziehbare Vorsteuer 7%',
342
+ '1776': 'Umsatzsteuer 19%',
343
+ '1771': 'Umsatzsteuer 7%',
344
+ '4610': 'Werbekosten',
345
+ '8400': 'Erlöse 19% USt',
346
+ '8300': 'Erlöse 7% USt',
347
+ // SKR04
348
+ '1400': 'Forderungen aus Lieferungen und Leistungen',
349
+ '3300': 'Verbindlichkeiten aus Lieferungen und Leistungen',
350
+ '1406': 'Abziehbare Vorsteuer 19%',
351
+ '1401': 'Abziehbare Vorsteuer 7%',
352
+ '3806': 'Umsatzsteuer 19%',
353
+ '3801': 'Umsatzsteuer 7%',
354
+ '6300': 'Sonstige betriebliche Aufwendungen',
355
+ '4400': 'Erlöse 19% USt',
356
+ '4300': 'Erlöse 7% USt'
357
+ };
358
+ return commonAccounts[accountNumber] || `Account ${accountNumber}`;
359
+ }
360
+ /**
361
+ * Calculate booking confidence score
362
+ */
363
+ calculateConfidence(invoice, bookingRules) {
364
+ let confidence = 100;
365
+ // Reduce confidence for missing or uncertain mappings
366
+ invoice.lines.forEach(line => {
367
+ if (!line.accountNumber) {
368
+ confidence -= 10; // No explicit account mapping
369
+ }
370
+ if (!line.productCode) {
371
+ confidence -= 5; // No product code for mapping
372
+ }
373
+ });
374
+ // Reduce confidence for complex tax scenarios
375
+ if (invoice.taxScenario === 'reverse_charge' ||
376
+ invoice.taxScenario === 'intra_eu_acquisition') {
377
+ confidence -= 15;
378
+ }
379
+ // Reduce confidence for mixed VAT rates
380
+ if (invoice.vatBreakdown.length > 1) {
381
+ confidence -= 10;
382
+ }
383
+ // Reduce confidence if no vendor/customer mapping exists
384
+ if (invoice.direction === 'inbound') {
385
+ if (!bookingRules.vendorMapping?.[invoice.supplier.id]) {
386
+ confidence -= 10;
387
+ }
388
+ }
389
+ else {
390
+ if (!bookingRules.customerMapping?.[invoice.customer.id]) {
391
+ confidence -= 10;
392
+ }
393
+ }
394
+ // Reduce confidence for credit notes
395
+ if (invoice.invoiceTypeCode === '381') {
396
+ confidence -= 10;
397
+ }
398
+ return Math.max(0, confidence);
399
+ }
400
+ }
401
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmludm9pY2UubWFwcGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc2tyLmludm9pY2UubWFwcGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBVXhDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxnQkFBZ0I7SUFrRzNCLFlBQVksT0FBaUI7UUE5RjdCLHlCQUF5QjtRQUNSLG1CQUFjLEdBQUc7WUFDaEMsbUJBQW1CO1lBQ25CLGFBQWEsRUFBRSxNQUFNLEVBQVksbURBQW1EO1lBQ3BGLGVBQWUsRUFBRSxNQUFNLEVBQVUsNkNBQTZDO1lBRTlFLGVBQWU7WUFDZixVQUFVLEVBQUUsTUFBTSxFQUFlLDJCQUEyQjtZQUM1RCxTQUFTLEVBQUUsTUFBTSxFQUFnQiwwQkFBMEI7WUFDM0QsV0FBVyxFQUFFLE1BQU0sRUFBYyxtQkFBbUI7WUFDcEQsVUFBVSxFQUFFLE1BQU0sRUFBZSxrQkFBa0I7WUFDbkQsZ0JBQWdCLEVBQUUsTUFBTSxFQUFTLGlDQUFpQztZQUNsRSxvQkFBb0IsRUFBRSxNQUFNLEVBQUkseUJBQXlCO1lBRXpELG1DQUFtQztZQUNuQyxjQUFjLEVBQUUsTUFBTSxFQUFXLGNBQWM7WUFDL0MsY0FBYyxFQUFFLE1BQU0sRUFBVyxpQkFBaUI7WUFDbEQsY0FBYyxFQUFFLE1BQU0sRUFBVyxnQkFBZ0I7WUFDakQsY0FBYyxFQUFFLE1BQU0sRUFBVyxzQkFBc0I7WUFFdkQsc0NBQXNDO1lBQ3RDLGVBQWUsRUFBRSxNQUFNLEVBQVUsbURBQW1EO1lBQ3BGLGtCQUFrQixFQUFFLE1BQU0sRUFBTyx5QkFBeUI7WUFDMUQsZ0JBQWdCLEVBQUUsTUFBTSxFQUFTLHFCQUFxQjtZQUN0RCxXQUFXLEVBQUUsTUFBTSxFQUFjLFFBQVE7WUFDekMsYUFBYSxFQUFFLE1BQU0sRUFBWSxhQUFhO1lBQzlDLGFBQWEsRUFBRSxNQUFNLEVBQVksY0FBYztZQUMvQyxjQUFjLEVB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
@@ -0,0 +1,140 @@
1
+ import type { IInvoice, IInvoiceFilter, IDuplicateCheckResult } from './skr.invoice.entity.js';
2
+ /**
3
+ * Invoice storage metadata
4
+ */
5
+ export interface IInvoiceMetadata {
6
+ invoiceId: string;
7
+ invoiceNumber: string;
8
+ direction: 'inbound' | 'outbound';
9
+ issueDate: string;
10
+ supplierName: string;
11
+ customerName: string;
12
+ totalAmount: number;
13
+ currency: string;
14
+ contentHash: string;
15
+ pdfHash?: string;
16
+ xmlHash: string;
17
+ journalEntryId?: string;
18
+ transactionIds?: string[];
19
+ validationResult: {
20
+ isValid: boolean;
21
+ errors: number;
22
+ warnings: number;
23
+ };
24
+ parserVersion: string;
25
+ storedAt: string;
26
+ storedBy: string;
27
+ }
28
+ /**
29
+ * Invoice registry entry (for NDJSON streaming)
30
+ */
31
+ export interface IInvoiceRegistryEntry {
32
+ id: string;
33
+ hash: string;
34
+ metadata: IInvoiceMetadata;
35
+ }
36
+ /**
37
+ * Storage statistics
38
+ */
39
+ export interface IStorageStats {
40
+ totalInvoices: number;
41
+ inboundCount: number;
42
+ outboundCount: number;
43
+ totalSize: number;
44
+ duplicatesDetected: number;
45
+ lastUpdate: Date;
46
+ }
47
+ /**
48
+ * Content-addressed storage for invoices
49
+ * Integrates with BagIt archive structure for GoBD compliance
50
+ */
51
+ export declare class InvoiceStorage {
52
+ private exportPath;
53
+ private logger;
54
+ private registryPath;
55
+ private metadataCache;
56
+ private readonly MAX_CACHE_SIZE;
57
+ private cacheAccessOrder;
58
+ constructor(exportPath: string);
59
+ /**
60
+ * Manage cache size using LRU eviction
61
+ */
62
+ private manageCacheSize;
63
+ /**
64
+ * Update cache access order for LRU
65
+ */
66
+ private touchCacheEntry;
67
+ /**
68
+ * Initialize storage directories
69
+ */
70
+ initialize(): Promise<void>;
71
+ private readonly MAX_PDF_SIZE;
72
+ /**
73
+ * Store an invoice with content addressing
74
+ */
75
+ storeInvoice(invoice: IInvoice, pdfBuffer?: Buffer): Promise<string>;
76
+ /**
77
+ * Retrieve an invoice by content hash
78
+ */
79
+ retrieveInvoice(contentHash: string): Promise<IInvoice | null>;
80
+ /**
81
+ * Check for duplicate invoices
82
+ */
83
+ checkDuplicate(invoice: IInvoice, contentHash: string): Promise<IDuplicateCheckResult>;
84
+ /**
85
+ * Search invoices by filter
86
+ */
87
+ searchInvoices(filter: IInvoiceFilter): Promise<IInvoiceMetadata[]>;
88
+ /**
89
+ * Get storage statistics
90
+ */
91
+ getStatistics(): Promise<IStorageStats>;
92
+ /**
93
+ * Create EN16931 compliance report
94
+ */
95
+ createComplianceReport(): Promise<void>;
96
+ /**
97
+ * Load registry from disk
98
+ */
99
+ private loadRegistry;
100
+ /**
101
+ * Update registry with new entry
102
+ */
103
+ private updateRegistry;
104
+ /**
105
+ * Find invoice file by hash and extension
106
+ */
107
+ private findInvoiceFile;
108
+ /**
109
+ * Calculate SHA-256 hash
110
+ */
111
+ private calculateHash;
112
+ /**
113
+ * Check if metadata matches filter
114
+ */
115
+ private matchesFilter;
116
+ /**
117
+ * Count errors in validation result
118
+ */
119
+ private countErrors;
120
+ /**
121
+ * Count warnings in validation result
122
+ */
123
+ private countWarnings;
124
+ /**
125
+ * Clean up old invoices (for testing only)
126
+ */
127
+ cleanup(olderThanDays?: number): Promise<number>;
128
+ /**
129
+ * Set cache entry with LRU eviction
130
+ */
131
+ private setCacheEntry;
132
+ /**
133
+ * Get cache entry and update access order
134
+ */
135
+ private getCacheEntry;
136
+ /**
137
+ * Update metadata in storage and cache
138
+ */
139
+ updateMetadata(contentHash: string, updates: Partial<IInvoiceMetadata>): Promise<void>;
140
+ }