@fin.cx/skr 1.2.1 → 1.2.2
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/{npmextra.json → .smartconfig.json} +12 -6
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +21 -15
- package/dist_ts/index.js +16 -16
- package/dist_ts/plugins.d.ts +15 -5
- package/dist_ts/plugins.js +42 -6
- package/dist_ts/skr.api.js +20 -13
- package/dist_ts/skr.classes.account.d.ts +8 -3
- package/dist_ts/skr.classes.account.js +358 -285
- package/dist_ts/skr.classes.chartofaccounts.d.ts +1 -1
- package/dist_ts/skr.classes.chartofaccounts.js +13 -6
- package/dist_ts/skr.classes.journalentry.d.ts +9 -4
- package/dist_ts/skr.classes.journalentry.js +424 -355
- package/dist_ts/skr.classes.ledger.js +3 -1
- package/dist_ts/skr.classes.reports.js +4 -1
- package/dist_ts/skr.classes.transaction.d.ts +9 -4
- package/dist_ts/skr.classes.transaction.js +315 -244
- package/dist_ts/skr.export.accounts.js +3 -2
- package/dist_ts/skr.export.balances.js +4 -2
- package/dist_ts/skr.export.js +6 -3
- package/dist_ts/skr.export.ledger.js +4 -3
- package/dist_ts/skr.export.pdf.js +6 -3
- package/dist_ts/skr.invoice.adapter.d.ts +2 -0
- package/dist_ts/skr.invoice.adapter.js +22 -10
- package/dist_ts/skr.invoice.booking.js +8 -3
- package/dist_ts/skr.invoice.mapper.js +84 -82
- package/dist_ts/skr.invoice.storage.js +10 -5
- package/dist_ts/skr.security.js +24 -21
- package/license.md +21 -0
- package/package.json +31 -23
- package/readme.md +215 -632
- package/readme.plan.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +43 -15
- package/ts/plugins.ts +52 -15
- package/ts/skr.api.ts +9 -5
- package/ts/skr.classes.account.ts +39 -21
- package/ts/skr.classes.chartofaccounts.ts +8 -3
- package/ts/skr.classes.journalentry.ts +40 -22
- package/ts/skr.classes.reports.ts +22 -2
- package/ts/skr.classes.transaction.ts +40 -22
- package/ts/skr.export.pdf.ts +4 -3
- package/ts/skr.invoice.adapter.ts +31 -11
- package/ts/skr.invoice.booking.ts +5 -3
- package/ts/skr.invoice.storage.ts +3 -2
- package/ts/skr.security.ts +23 -22
|
@@ -18,11 +18,25 @@ import type {
|
|
|
18
18
|
*/
|
|
19
19
|
export class InvoiceAdapter {
|
|
20
20
|
private logger: plugins.smartlog.ConsoleLog;
|
|
21
|
+
private readonly einvoiceModuleName = '@fin.cx/einvoice';
|
|
21
22
|
|
|
22
23
|
constructor() {
|
|
23
24
|
this.logger = new plugins.smartlog.ConsoleLog();
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
private async getEInvoiceClass(): Promise<{
|
|
28
|
+
new (): any;
|
|
29
|
+
fromXml(xmlString: string): Promise<any>;
|
|
30
|
+
}> {
|
|
31
|
+
const { EInvoice } = (await import(this.einvoiceModuleName)) as {
|
|
32
|
+
EInvoice: {
|
|
33
|
+
new (): any;
|
|
34
|
+
fromXml(xmlString: string): Promise<any>;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
return EInvoice;
|
|
38
|
+
}
|
|
39
|
+
|
|
26
40
|
private readonly MAX_XML_SIZE = 10 * 1024 * 1024; // 10MB max
|
|
27
41
|
private readonly MAX_PDF_SIZE = 50 * 1024 * 1024; // 50MB max
|
|
28
42
|
|
|
@@ -44,13 +58,14 @@ export class InvoiceAdapter {
|
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
// Parse the invoice using @fin.cx/einvoice
|
|
47
|
-
|
|
61
|
+
const EInvoice = await this.getEInvoiceClass();
|
|
62
|
+
let einvoice: any;
|
|
48
63
|
if (typeof file === 'string') {
|
|
49
|
-
einvoice = await
|
|
64
|
+
einvoice = await EInvoice.fromXml(file);
|
|
50
65
|
} else {
|
|
51
66
|
// Convert buffer to string first
|
|
52
67
|
const xmlString = file.toString('utf-8');
|
|
53
|
-
einvoice = await
|
|
68
|
+
einvoice = await EInvoice.fromXml(xmlString);
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
// Get detected format
|
|
@@ -74,7 +89,7 @@ export class InvoiceAdapter {
|
|
|
74
89
|
invoice.xmlContent = einvoice.getXml();
|
|
75
90
|
|
|
76
91
|
// Calculate content hash
|
|
77
|
-
invoice.contentHash = await this.calculateContentHash(invoice.xmlContent);
|
|
92
|
+
invoice.contentHash = await this.calculateContentHash(invoice.xmlContent!);
|
|
78
93
|
|
|
79
94
|
// Classify tax scenario
|
|
80
95
|
invoice.taxScenario = this.classifyTaxScenario(invoice);
|
|
@@ -82,7 +97,8 @@ export class InvoiceAdapter {
|
|
|
82
97
|
return invoice;
|
|
83
98
|
} catch (error) {
|
|
84
99
|
this.logger.log('error', `Failed to parse invoice: ${error}`);
|
|
85
|
-
|
|
100
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
101
|
+
throw new Error(`Invoice parsing failed: ${errorMessage}`);
|
|
86
102
|
}
|
|
87
103
|
}
|
|
88
104
|
|
|
@@ -310,7 +326,7 @@ export class InvoiceAdapter {
|
|
|
310
326
|
* Get exemption reason for VAT category
|
|
311
327
|
*/
|
|
312
328
|
private getExemptionReason(categoryCode: string): string | undefined {
|
|
313
|
-
const exemptionReasons: Record<string, string> = {
|
|
329
|
+
const exemptionReasons: Record<string, string | undefined> = {
|
|
314
330
|
'E': 'Tax exempt',
|
|
315
331
|
'Z': 'Zero rated',
|
|
316
332
|
'AE': 'Reverse charge (§13b UStG)',
|
|
@@ -516,7 +532,8 @@ export class InvoiceAdapter {
|
|
|
516
532
|
): Promise<string> {
|
|
517
533
|
try {
|
|
518
534
|
// Load from existing XML
|
|
519
|
-
const
|
|
535
|
+
const EInvoice = await this.getEInvoiceClass();
|
|
536
|
+
const einvoice: any = await EInvoice.fromXml(invoice.xmlContent!);
|
|
520
537
|
|
|
521
538
|
// Convert to target format (takes ~0.6ms)
|
|
522
539
|
const convertedXml = await einvoice.exportXml(targetFormat as any);
|
|
@@ -524,7 +541,8 @@ export class InvoiceAdapter {
|
|
|
524
541
|
return convertedXml;
|
|
525
542
|
} catch (error) {
|
|
526
543
|
this.logger.log('error', `Failed to convert invoice format: ${error}`);
|
|
527
|
-
|
|
544
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
545
|
+
throw new Error(`Format conversion failed: ${errorMessage}`);
|
|
528
546
|
}
|
|
529
547
|
}
|
|
530
548
|
|
|
@@ -537,7 +555,8 @@ export class InvoiceAdapter {
|
|
|
537
555
|
): Promise<{ xml: string; pdf?: Buffer }> {
|
|
538
556
|
try {
|
|
539
557
|
// Create a new invoice instance
|
|
540
|
-
const
|
|
558
|
+
const EInvoice = await this.getEInvoiceClass();
|
|
559
|
+
const einvoice: any = new EInvoice();
|
|
541
560
|
|
|
542
561
|
// Set invoice data
|
|
543
562
|
const businessTerms = this.mapToBusinessTerms(invoiceData);
|
|
@@ -558,7 +577,8 @@ export class InvoiceAdapter {
|
|
|
558
577
|
return { xml, pdf };
|
|
559
578
|
} catch (error) {
|
|
560
579
|
this.logger.log('error', `Failed to generate invoice: ${error}`);
|
|
561
|
-
|
|
580
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
581
|
+
throw new Error(`Invoice generation failed: ${errorMessage}`);
|
|
562
582
|
}
|
|
563
583
|
}
|
|
564
584
|
|
|
@@ -578,4 +598,4 @@ export class InvoiceAdapter {
|
|
|
578
598
|
// This would be a comprehensive mapping in production
|
|
579
599
|
};
|
|
580
600
|
}
|
|
581
|
-
}
|
|
601
|
+
}
|
|
@@ -131,10 +131,11 @@ export class InvoiceBookingEngine {
|
|
|
131
131
|
};
|
|
132
132
|
} catch (error) {
|
|
133
133
|
this.logger.log('error', `Failed to book invoice: ${error}`);
|
|
134
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
134
135
|
return {
|
|
135
136
|
success: false,
|
|
136
137
|
confidence: 0,
|
|
137
|
-
errors: [`Booking failed: ${
|
|
138
|
+
errors: [`Booking failed: ${errorMessage}`]
|
|
138
139
|
};
|
|
139
140
|
}
|
|
140
141
|
}
|
|
@@ -574,10 +575,11 @@ export class InvoiceBookingEngine {
|
|
|
574
575
|
};
|
|
575
576
|
} catch (error) {
|
|
576
577
|
this.logger.log('error', `Failed to book payment: ${error}`);
|
|
578
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
577
579
|
return {
|
|
578
580
|
success: false,
|
|
579
581
|
confidence: 0,
|
|
580
|
-
errors: [`Payment booking failed: ${
|
|
582
|
+
errors: [`Payment booking failed: ${errorMessage}`]
|
|
581
583
|
};
|
|
582
584
|
}
|
|
583
585
|
}
|
|
@@ -757,4 +759,4 @@ export class InvoiceBookingEngine {
|
|
|
757
759
|
|
|
758
760
|
return Array.from(accounts);
|
|
759
761
|
}
|
|
760
|
-
}
|
|
762
|
+
}
|
|
@@ -224,7 +224,8 @@ export class InvoiceStorage {
|
|
|
224
224
|
return contentHash;
|
|
225
225
|
} catch (error) {
|
|
226
226
|
this.logger.log('error', `Failed to store invoice: ${error}`);
|
|
227
|
-
|
|
227
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
228
|
+
throw new Error(`Invoice storage failed: ${errorMessage}`);
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
231
|
|
|
@@ -707,4 +708,4 @@ export class InvoiceStorage {
|
|
|
707
708
|
|
|
708
709
|
this.logger.log('info', `Updated metadata for invoice: ${contentHash}`);
|
|
709
710
|
}
|
|
710
|
-
}
|
|
711
|
+
}
|
package/ts/skr.security.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as plugins from './plugins.js';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
4
|
import * as https from 'https';
|
|
5
|
+
import * as nodeForge from 'node-forge';
|
|
5
6
|
|
|
6
7
|
export interface ISigningOptions {
|
|
7
8
|
certificatePem?: string;
|
|
@@ -57,19 +58,19 @@ export class SecurityManager {
|
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
60
|
// Parse certificate and key
|
|
60
|
-
const certificate =
|
|
61
|
+
const certificate = nodeForge.pki.certificateFromPem(cert);
|
|
61
62
|
const privateKey = this.options.privateKeyPassphrase
|
|
62
|
-
?
|
|
63
|
-
:
|
|
63
|
+
? nodeForge.pki.decryptRsaPrivateKey(key, this.options.privateKeyPassphrase)
|
|
64
|
+
: nodeForge.pki.privateKeyFromPem(key);
|
|
64
65
|
|
|
65
66
|
// Create PKCS#7 signed data (CMS)
|
|
66
|
-
const p7 =
|
|
67
|
+
const p7 = nodeForge.pkcs7.createSignedData();
|
|
67
68
|
|
|
68
69
|
// Add content
|
|
69
70
|
if (typeof data === 'string') {
|
|
70
|
-
p7.content =
|
|
71
|
+
p7.content = nodeForge.util.createBuffer(data, 'utf8');
|
|
71
72
|
} else {
|
|
72
|
-
p7.content =
|
|
73
|
+
p7.content = nodeForge.util.createBuffer(data.toString('latin1'));
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
// Add certificate
|
|
@@ -79,17 +80,17 @@ export class SecurityManager {
|
|
|
79
80
|
p7.addSigner({
|
|
80
81
|
key: privateKey,
|
|
81
82
|
certificate: certificate,
|
|
82
|
-
digestAlgorithm:
|
|
83
|
+
digestAlgorithm: nodeForge.pki.oids.sha256,
|
|
83
84
|
authenticatedAttributes: [
|
|
84
85
|
{
|
|
85
|
-
type:
|
|
86
|
-
value:
|
|
86
|
+
type: nodeForge.pki.oids.contentType,
|
|
87
|
+
value: nodeForge.pki.oids.data
|
|
87
88
|
},
|
|
88
89
|
{
|
|
89
|
-
type:
|
|
90
|
+
type: nodeForge.pki.oids.messageDigest
|
|
90
91
|
},
|
|
91
92
|
{
|
|
92
|
-
type:
|
|
93
|
+
type: nodeForge.pki.oids.signingTime,
|
|
93
94
|
value: new Date().toISOString()
|
|
94
95
|
}
|
|
95
96
|
]
|
|
@@ -99,7 +100,7 @@ export class SecurityManager {
|
|
|
99
100
|
p7.sign({ detached: true });
|
|
100
101
|
|
|
101
102
|
// Convert to PEM
|
|
102
|
-
const pem =
|
|
103
|
+
const pem = nodeForge.pkcs7.messageToPem(p7);
|
|
103
104
|
|
|
104
105
|
// Extract base64 signature
|
|
105
106
|
const signature = pem
|
|
@@ -237,14 +238,14 @@ export class SecurityManager {
|
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
// Parse the PKCS#7 message
|
|
240
|
-
const p7 =
|
|
241
|
+
const p7 = nodeForge.pkcs7.messageFromPem(pemSignature);
|
|
241
242
|
|
|
242
243
|
// Prepare content for verification
|
|
243
|
-
let content:
|
|
244
|
+
let content: nodeForge.util.ByteStringBuffer;
|
|
244
245
|
if (typeof data === 'string') {
|
|
245
|
-
content =
|
|
246
|
+
content = nodeForge.util.createBuffer(data, 'utf8');
|
|
246
247
|
} else {
|
|
247
|
-
content =
|
|
248
|
+
content = nodeForge.util.createBuffer(data.toString('latin1'));
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
// Verify the signature
|
|
@@ -267,8 +268,8 @@ export class SecurityManager {
|
|
|
267
268
|
commonName: string = 'SKR Export System',
|
|
268
269
|
validDays: number = 365
|
|
269
270
|
): Promise<{ certificate: string; privateKey: string }> {
|
|
270
|
-
const keys =
|
|
271
|
-
const cert =
|
|
271
|
+
const keys = nodeForge.pki.rsa.generateKeyPair(2048);
|
|
272
|
+
const cert = nodeForge.pki.createCertificate();
|
|
272
273
|
|
|
273
274
|
cert.publicKey = keys.publicKey;
|
|
274
275
|
cert.serialNumber = '01';
|
|
@@ -326,11 +327,11 @@ export class SecurityManager {
|
|
|
326
327
|
]);
|
|
327
328
|
|
|
328
329
|
// Self-sign certificate
|
|
329
|
-
cert.sign(keys.privateKey,
|
|
330
|
+
cert.sign(keys.privateKey, nodeForge.md.sha256.create());
|
|
330
331
|
|
|
331
332
|
// Convert to PEM
|
|
332
|
-
const certificatePem =
|
|
333
|
-
const privateKeyPem =
|
|
333
|
+
const certificatePem = nodeForge.pki.certificateToPem(cert);
|
|
334
|
+
const privateKeyPem = nodeForge.pki.privateKeyToPem(keys.privateKey);
|
|
334
335
|
|
|
335
336
|
return {
|
|
336
337
|
certificate: certificatePem,
|
|
@@ -402,4 +403,4 @@ export class SecurityManager {
|
|
|
402
403
|
|
|
403
404
|
return ltv;
|
|
404
405
|
}
|
|
405
|
-
}
|
|
406
|
+
}
|