@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,315 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
export class SkrExport {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.manifest = {};
|
|
6
|
+
this.tagManifest = {};
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.logger = new plugins.smartlog.ConsoleLog();
|
|
9
|
+
this.exportDir = path.join(options.exportPath, `jahresabschluss_${options.fiscalYear}`);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates the BagIt directory structure for the export
|
|
13
|
+
*/
|
|
14
|
+
async createBagItStructure() {
|
|
15
|
+
this.logger.log('info', 'Creating BagIt directory structure...');
|
|
16
|
+
// Create main directories
|
|
17
|
+
await plugins.smartfile.fs.ensureDir(this.exportDir);
|
|
18
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data'));
|
|
19
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata'));
|
|
20
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata', 'schemas'));
|
|
21
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata', 'schemas', 'v1'));
|
|
22
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata', 'signatures'));
|
|
23
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'accounting'));
|
|
24
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'accounting', 'ebilanz'));
|
|
25
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'documents'));
|
|
26
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'documents', 'by-hash'));
|
|
27
|
+
await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'reports'));
|
|
28
|
+
// Create BagIt declaration file
|
|
29
|
+
await this.createBagItDeclaration();
|
|
30
|
+
// Create README
|
|
31
|
+
await this.createReadme();
|
|
32
|
+
this.logger.log('ok', 'BagIt structure created successfully');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates the bagit.txt declaration file
|
|
36
|
+
*/
|
|
37
|
+
async createBagItDeclaration() {
|
|
38
|
+
const bagitContent = `BagIt-Version: 1.0
|
|
39
|
+
Tag-File-Character-Encoding: UTF-8`;
|
|
40
|
+
const filePath = path.join(this.exportDir, 'bagit.txt');
|
|
41
|
+
await plugins.smartfile.memory.toFs(bagitContent, filePath);
|
|
42
|
+
// Add to tag manifest
|
|
43
|
+
const hash = await this.hashFile(filePath);
|
|
44
|
+
this.tagManifest['bagit.txt'] = hash;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates the README.txt file with Verfahrensdokumentation
|
|
48
|
+
*/
|
|
49
|
+
async createReadme() {
|
|
50
|
+
const readmeContent = `SKR Jahresabschluss Export - Verfahrensdokumentation
|
|
51
|
+
=====================================================
|
|
52
|
+
|
|
53
|
+
Dieses Archiv enthält einen revisionssicheren Export des Jahresabschlusses
|
|
54
|
+
gemäß den Grundsätzen ordnungsmäßiger Buchführung (GoBD).
|
|
55
|
+
|
|
56
|
+
Export-Datum: ${new Date().toISOString()}
|
|
57
|
+
Geschäftsjahr: ${this.options.fiscalYear}
|
|
58
|
+
Zeitraum: ${this.options.dateFrom.toISOString()} bis ${this.options.dateTo.toISOString()}
|
|
59
|
+
|
|
60
|
+
STRUKTUR DES ARCHIVS
|
|
61
|
+
--------------------
|
|
62
|
+
- /data/accounting/: Buchhaltungsdaten (Journale, Konten, Salden)
|
|
63
|
+
- /data/documents/: Belegdokumente (content-adressiert)
|
|
64
|
+
- /data/reports/: Finanzberichte (PDF/A-3)
|
|
65
|
+
- /data/metadata/: Export-Metadaten und Schemas
|
|
66
|
+
- /data/metadata/signatures/: Digitale Signaturen und Zeitstempel
|
|
67
|
+
|
|
68
|
+
INTEGRITÄTSSICHERUNG
|
|
69
|
+
--------------------
|
|
70
|
+
- Alle Dateien sind mit SHA-256 gehasht (siehe manifest-sha256.txt)
|
|
71
|
+
- Optional: Digitale Signatur (CAdES) über Manifest
|
|
72
|
+
- Optional: RFC 3161 Zeitstempel
|
|
73
|
+
|
|
74
|
+
AUFBEWAHRUNG
|
|
75
|
+
------------
|
|
76
|
+
Dieses Archiv muss gemäß § 147 AO für 10 Jahre revisionssicher aufbewahrt werden.
|
|
77
|
+
Empfohlen wird die Speicherung auf WORM-Medien.
|
|
78
|
+
|
|
79
|
+
REIMPORT
|
|
80
|
+
--------
|
|
81
|
+
Das Archiv kann mit der SKR-Software vollständig reimportiert werden.
|
|
82
|
+
Die Datenintegrität wird beim Import automatisch verifiziert.
|
|
83
|
+
|
|
84
|
+
COMPLIANCE
|
|
85
|
+
----------
|
|
86
|
+
- GoBD-konform
|
|
87
|
+
- E-Bilanz-fähig (XBRL)
|
|
88
|
+
- ZUGFeRD/Factur-X kompatibel
|
|
89
|
+
- PDF/A-3 für Langzeitarchivierung
|
|
90
|
+
|
|
91
|
+
© ${new Date().getFullYear()} ${this.options.companyInfo?.name || 'Export System'}`;
|
|
92
|
+
const filePath = path.join(this.exportDir, 'readme.txt');
|
|
93
|
+
await plugins.smartfile.memory.toFs(readmeContent, filePath);
|
|
94
|
+
// Add to tag manifest
|
|
95
|
+
const hash = await this.hashFile(filePath);
|
|
96
|
+
this.tagManifest['readme.txt'] = hash;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Creates the export metadata JSON file
|
|
100
|
+
*/
|
|
101
|
+
async createExportMetadata(skrType) {
|
|
102
|
+
const metadata = {
|
|
103
|
+
exportVersion: '1.0.0',
|
|
104
|
+
exportTimestamp: new Date().toISOString(),
|
|
105
|
+
generator: {
|
|
106
|
+
name: '@fin.cx/skr',
|
|
107
|
+
version: '1.1.0' // Should be read from package.json
|
|
108
|
+
},
|
|
109
|
+
company: this.options.companyInfo,
|
|
110
|
+
fiscalYear: this.options.fiscalYear,
|
|
111
|
+
dateRange: {
|
|
112
|
+
from: this.options.dateFrom.toISOString(),
|
|
113
|
+
to: this.options.dateTo.toISOString()
|
|
114
|
+
},
|
|
115
|
+
skrType: skrType,
|
|
116
|
+
schemaVersion: '1.0',
|
|
117
|
+
crypto: {
|
|
118
|
+
digestAlgorithms: ['sha256'],
|
|
119
|
+
signatureType: this.options.signExport ? 'CAdES' : undefined,
|
|
120
|
+
timestampPolicy: this.options.timestampExport ? 'RFC3161' : undefined,
|
|
121
|
+
merkleTree: true
|
|
122
|
+
},
|
|
123
|
+
options: {
|
|
124
|
+
packagedAs: 'bagit',
|
|
125
|
+
compression: 'none',
|
|
126
|
+
deduplication: true
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const filePath = path.join(this.exportDir, 'data', 'metadata', 'export.json');
|
|
130
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(metadata, null, 2), filePath);
|
|
131
|
+
// Add to manifest
|
|
132
|
+
const hash = await this.hashFile(filePath);
|
|
133
|
+
this.manifest['data/metadata/export.json'] = hash;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Creates JSON schemas for the export data structures
|
|
137
|
+
*/
|
|
138
|
+
async createSchemas() {
|
|
139
|
+
// Ledger schema
|
|
140
|
+
const ledgerSchema = {
|
|
141
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
142
|
+
"title": "Ledger Entry",
|
|
143
|
+
"type": "object",
|
|
144
|
+
"properties": {
|
|
145
|
+
"schema_version": { "type": "string" },
|
|
146
|
+
"entry_id": { "type": "string", "format": "uuid" },
|
|
147
|
+
"booking_date": { "type": "string", "format": "date" },
|
|
148
|
+
"posting_date": { "type": "string", "format": "date" },
|
|
149
|
+
"currency": { "type": "string" },
|
|
150
|
+
"journal": { "type": "string" },
|
|
151
|
+
"description": { "type": "string" },
|
|
152
|
+
"lines": {
|
|
153
|
+
"type": "array",
|
|
154
|
+
"items": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"properties": {
|
|
157
|
+
"posting_id": { "type": "string" },
|
|
158
|
+
"account_code": { "type": "string" },
|
|
159
|
+
"debit": { "type": "string" },
|
|
160
|
+
"credit": { "type": "string" },
|
|
161
|
+
"tax_code": { "type": "string" },
|
|
162
|
+
"document_refs": {
|
|
163
|
+
"type": "array",
|
|
164
|
+
"items": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"properties": {
|
|
167
|
+
"content_hash": { "type": "string" },
|
|
168
|
+
"doc_role": { "type": "string" },
|
|
169
|
+
"mime": { "type": "string" }
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"required": ["posting_id", "account_code", "debit", "credit"]
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"created_at": { "type": "string", "format": "date-time" },
|
|
178
|
+
"user": { "type": "string" }
|
|
179
|
+
},
|
|
180
|
+
"required": ["schema_version", "entry_id", "booking_date", "lines"]
|
|
181
|
+
};
|
|
182
|
+
// Accounts schema
|
|
183
|
+
const accountsSchema = {
|
|
184
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
185
|
+
"title": "Accounts CSV",
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"account_code": { "type": "string" },
|
|
189
|
+
"name": { "type": "string" },
|
|
190
|
+
"type": { "type": "string" },
|
|
191
|
+
"parent": { "type": "string" },
|
|
192
|
+
"skr_set": { "type": "string" },
|
|
193
|
+
"tax_code_default": { "type": "string" },
|
|
194
|
+
"active_from": { "type": "string", "format": "date" },
|
|
195
|
+
"active_to": { "type": "string", "format": "date" }
|
|
196
|
+
},
|
|
197
|
+
"required": ["account_code", "name", "type", "skr_set"]
|
|
198
|
+
};
|
|
199
|
+
// Save schemas
|
|
200
|
+
const schemasDir = path.join(this.exportDir, 'data', 'metadata', 'schemas', 'v1');
|
|
201
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(ledgerSchema, null, 2), path.join(schemasDir, 'ledger.schema.json'));
|
|
202
|
+
this.manifest['data/metadata/schemas/v1/ledger.schema.json'] = await this.hashFile(path.join(schemasDir, 'ledger.schema.json'));
|
|
203
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(accountsSchema, null, 2), path.join(schemasDir, 'accounts.schema.json'));
|
|
204
|
+
this.manifest['data/metadata/schemas/v1/accounts.schema.json'] = await this.hashFile(path.join(schemasDir, 'accounts.schema.json'));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Writes the BagIt manifest files
|
|
208
|
+
*/
|
|
209
|
+
async writeManifests() {
|
|
210
|
+
// Write data manifest
|
|
211
|
+
let manifestContent = '';
|
|
212
|
+
for (const [filePath, hash] of Object.entries(this.manifest)) {
|
|
213
|
+
manifestContent += `${hash} ${filePath}\n`;
|
|
214
|
+
}
|
|
215
|
+
const manifestPath = path.join(this.exportDir, 'manifest-sha256.txt');
|
|
216
|
+
await plugins.smartfile.memory.toFs(manifestContent, manifestPath);
|
|
217
|
+
// Add manifest to tag manifest
|
|
218
|
+
this.tagManifest['manifest-sha256.txt'] = await this.hashFile(manifestPath);
|
|
219
|
+
// Write tag manifest
|
|
220
|
+
let tagManifestContent = '';
|
|
221
|
+
for (const [filePath, hash] of Object.entries(this.tagManifest)) {
|
|
222
|
+
tagManifestContent += `${hash} ${filePath}\n`;
|
|
223
|
+
}
|
|
224
|
+
await plugins.smartfile.memory.toFs(tagManifestContent, path.join(this.exportDir, 'tagmanifest-sha256.txt'));
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Calculates SHA-256 hash of a file
|
|
228
|
+
*/
|
|
229
|
+
async hashFile(filePath) {
|
|
230
|
+
const fileContent = await plugins.smartfile.fs.toBuffer(filePath);
|
|
231
|
+
return await plugins.smarthash.sha256FromBuffer(fileContent);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Stores a document in content-addressed storage
|
|
235
|
+
*/
|
|
236
|
+
async storeDocument(content, originalFilename) {
|
|
237
|
+
const hash = await plugins.smarthash.sha256FromBuffer(content);
|
|
238
|
+
// Create path based on hash (first 2 chars as directory)
|
|
239
|
+
const hashPrefix = hash.substring(0, 2);
|
|
240
|
+
const hashDir = path.join(this.exportDir, 'data', 'documents', 'by-hash', hashPrefix);
|
|
241
|
+
await plugins.smartfile.fs.ensureDir(hashDir);
|
|
242
|
+
const docPath = path.join(hashDir, hash);
|
|
243
|
+
// Only store if not already exists (deduplication)
|
|
244
|
+
if (!(await plugins.smartfile.fs.fileExists(docPath))) {
|
|
245
|
+
await plugins.smartfile.memory.toFs(content, docPath);
|
|
246
|
+
this.manifest[`data/documents/by-hash/${hashPrefix}/${hash}`] = hash;
|
|
247
|
+
}
|
|
248
|
+
return hash;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Creates a Merkle tree from all file hashes
|
|
252
|
+
*/
|
|
253
|
+
async createMerkleTree() {
|
|
254
|
+
const leaves = Object.values(this.manifest).map(hash => Buffer.from(hash, 'hex'));
|
|
255
|
+
// Create a sync hash function wrapper for MerkleTree
|
|
256
|
+
const hashFn = (data) => {
|
|
257
|
+
// Convert async to sync by using crypto directly
|
|
258
|
+
const crypto = require('crypto');
|
|
259
|
+
return crypto.createHash('sha256').update(data).digest();
|
|
260
|
+
};
|
|
261
|
+
const tree = new plugins.MerkleTree(leaves, hashFn, {
|
|
262
|
+
sortPairs: true
|
|
263
|
+
});
|
|
264
|
+
const root = tree.getRoot().toString('hex');
|
|
265
|
+
// Save Merkle tree data
|
|
266
|
+
const merkleData = {
|
|
267
|
+
root: root,
|
|
268
|
+
leaves: Object.entries(this.manifest).map(([path, hash]) => ({
|
|
269
|
+
path,
|
|
270
|
+
hash
|
|
271
|
+
})),
|
|
272
|
+
timestamp: new Date().toISOString()
|
|
273
|
+
};
|
|
274
|
+
const merklePath = path.join(this.exportDir, 'data', 'metadata', 'merkle-tree.json');
|
|
275
|
+
await plugins.smartfile.memory.toFs(JSON.stringify(merkleData, null, 2), merklePath);
|
|
276
|
+
this.manifest['data/metadata/merkle-tree.json'] = await this.hashFile(merklePath);
|
|
277
|
+
return root;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Validates the BagIt structure
|
|
281
|
+
*/
|
|
282
|
+
async validateBagIt() {
|
|
283
|
+
this.logger.log('info', 'Validating BagIt structure...');
|
|
284
|
+
// Check required files exist
|
|
285
|
+
const requiredFiles = [
|
|
286
|
+
'bagit.txt',
|
|
287
|
+
'manifest-sha256.txt',
|
|
288
|
+
'tagmanifest-sha256.txt',
|
|
289
|
+
'readme.txt'
|
|
290
|
+
];
|
|
291
|
+
for (const file of requiredFiles) {
|
|
292
|
+
const filePath = path.join(this.exportDir, file);
|
|
293
|
+
if (!(await plugins.smartfile.fs.fileExists(filePath))) {
|
|
294
|
+
this.logger.log('error', `Required file missing: ${file}`);
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Verify all manifest entries
|
|
299
|
+
for (const [relPath, expectedHash] of Object.entries(this.manifest)) {
|
|
300
|
+
const fullPath = path.join(this.exportDir, relPath);
|
|
301
|
+
if (!(await plugins.smartfile.fs.fileExists(fullPath))) {
|
|
302
|
+
this.logger.log('error', `Manifest file missing: ${relPath}`);
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const actualHash = await this.hashFile(fullPath);
|
|
306
|
+
if (actualHash !== expectedHash) {
|
|
307
|
+
this.logger.log('error', `Hash mismatch for ${relPath}`);
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
this.logger.log('ok', 'BagIt validation successful');
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmV4cG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3Nrci5leHBvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFvRTdCLE1BQU0sT0FBTyxTQUFTO0lBT3BCLFlBQVksT0FBdUI7UUFIM0IsYUFBUSxHQUFtQixFQUFFLENBQUM7UUFDOUIsZ0JBQVcsR0FBbUIsRUFBRSxDQUFDO1FBR3ZDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2hELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLG1CQUFtQixPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUMxRixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsb0JBQW9CO1FBQy9CLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSx1Q0FBdUMsQ0FBQyxDQUFDO1FBRWpFLDBCQUEwQjtRQUMxQixNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDckQsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDeEUsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUFDO1FBQ3BGLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFDL0YsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDckcsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztRQUNsRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7UUFDdEYsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQztRQUNqRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7UUFDckYsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsU0FBUyxD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
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ITransactionData, IJournalEntry, IJournalEntryLine } from './skr.types.js';
|
|
2
|
+
export interface ITransactionDataExport extends ITransactionData {
|
|
3
|
+
_id?: string;
|
|
4
|
+
postingDate?: Date;
|
|
5
|
+
currency?: string;
|
|
6
|
+
createdAt?: Date | string;
|
|
7
|
+
modifiedAt?: Date | string;
|
|
8
|
+
reversalOf?: string;
|
|
9
|
+
reversedBy?: string;
|
|
10
|
+
taxCode?: string;
|
|
11
|
+
project?: string;
|
|
12
|
+
vatAccount?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IJournalEntryExport extends IJournalEntry {
|
|
15
|
+
_id?: string;
|
|
16
|
+
postingDate?: Date;
|
|
17
|
+
currency?: string;
|
|
18
|
+
journal?: string;
|
|
19
|
+
createdAt?: Date | string;
|
|
20
|
+
modifiedAt?: Date | string;
|
|
21
|
+
reversalOf?: string;
|
|
22
|
+
reversedBy?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface IJournalEntryLineExport extends IJournalEntryLine {
|
|
25
|
+
taxCode?: string;
|
|
26
|
+
project?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ILedgerEntry {
|
|
29
|
+
schema_version: string;
|
|
30
|
+
entry_id: string;
|
|
31
|
+
booking_date: string;
|
|
32
|
+
posting_date: string;
|
|
33
|
+
period?: string;
|
|
34
|
+
currency: string;
|
|
35
|
+
journal: string;
|
|
36
|
+
description: string;
|
|
37
|
+
reference?: string;
|
|
38
|
+
lines: ILedgerLine[];
|
|
39
|
+
document_refs?: IDocumentRef[];
|
|
40
|
+
created_at: string;
|
|
41
|
+
modified_at?: string;
|
|
42
|
+
user?: string;
|
|
43
|
+
reversal_of?: string;
|
|
44
|
+
reversed_by?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface ILedgerLine {
|
|
47
|
+
posting_id: string;
|
|
48
|
+
account_code: string;
|
|
49
|
+
debit: string;
|
|
50
|
+
credit: string;
|
|
51
|
+
tax_code?: string;
|
|
52
|
+
cost_center?: string;
|
|
53
|
+
project?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface IDocumentRef {
|
|
57
|
+
content_hash: string;
|
|
58
|
+
doc_role: 'invoice' | 'receipt' | 'contract' | 'bank-statement' | 'other';
|
|
59
|
+
doc_mime: string;
|
|
60
|
+
doc_original_name?: string;
|
|
61
|
+
}
|
|
62
|
+
export declare class LedgerExporter {
|
|
63
|
+
private exportPath;
|
|
64
|
+
private stream;
|
|
65
|
+
private entryCount;
|
|
66
|
+
constructor(exportPath: string);
|
|
67
|
+
/**
|
|
68
|
+
* Initializes the NDJSON export stream
|
|
69
|
+
*/
|
|
70
|
+
initialize(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Exports a transaction as a ledger entry
|
|
73
|
+
*/
|
|
74
|
+
exportTransaction(transaction: ITransactionDataExport): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Exports a journal entry
|
|
77
|
+
*/
|
|
78
|
+
exportJournalEntry(journalEntry: IJournalEntryExport): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Writes a single NDJSON line
|
|
81
|
+
*/
|
|
82
|
+
private writeLine;
|
|
83
|
+
/**
|
|
84
|
+
* Formats a date to ISO date string
|
|
85
|
+
*/
|
|
86
|
+
private formatDate;
|
|
87
|
+
/**
|
|
88
|
+
* Closes the export stream
|
|
89
|
+
*/
|
|
90
|
+
close(): Promise<number>;
|
|
91
|
+
/**
|
|
92
|
+
* Gets the number of exported entries
|
|
93
|
+
*/
|
|
94
|
+
getEntryCount(): number;
|
|
95
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { createWriteStream } from 'fs';
|
|
4
|
+
export class LedgerExporter {
|
|
5
|
+
constructor(exportPath) {
|
|
6
|
+
this.stream = null;
|
|
7
|
+
this.entryCount = 0;
|
|
8
|
+
this.exportPath = exportPath;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Initializes the NDJSON export stream
|
|
12
|
+
*/
|
|
13
|
+
async initialize() {
|
|
14
|
+
const ledgerPath = path.join(this.exportPath, 'data', 'accounting', 'ledger.ndjson');
|
|
15
|
+
await plugins.smartfile.fs.ensureDir(path.dirname(ledgerPath));
|
|
16
|
+
this.stream = createWriteStream(ledgerPath, {
|
|
17
|
+
encoding: 'utf8',
|
|
18
|
+
flags: 'w'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Exports a transaction as a ledger entry
|
|
23
|
+
*/
|
|
24
|
+
async exportTransaction(transaction) {
|
|
25
|
+
if (!this.stream) {
|
|
26
|
+
throw new Error('Ledger exporter not initialized');
|
|
27
|
+
}
|
|
28
|
+
const entry = {
|
|
29
|
+
schema_version: '1.0',
|
|
30
|
+
entry_id: transaction._id || plugins.smartunique.shortId(),
|
|
31
|
+
booking_date: this.formatDate(transaction.date),
|
|
32
|
+
posting_date: this.formatDate(transaction.postingDate || transaction.date),
|
|
33
|
+
currency: transaction.currency || 'EUR',
|
|
34
|
+
journal: 'GL',
|
|
35
|
+
description: transaction.description,
|
|
36
|
+
reference: transaction.reference,
|
|
37
|
+
lines: [],
|
|
38
|
+
created_at: transaction.createdAt ? new Date(transaction.createdAt).toISOString() : new Date().toISOString(),
|
|
39
|
+
modified_at: transaction.modifiedAt ? new Date(transaction.modifiedAt).toISOString() : undefined,
|
|
40
|
+
reversal_of: transaction.reversalOf,
|
|
41
|
+
reversed_by: transaction.reversedBy
|
|
42
|
+
};
|
|
43
|
+
// Add debit line
|
|
44
|
+
if (transaction.amount > 0) {
|
|
45
|
+
entry.lines.push({
|
|
46
|
+
posting_id: `${entry.entry_id}-1`,
|
|
47
|
+
account_code: transaction.debitAccount,
|
|
48
|
+
debit: transaction.amount.toFixed(2),
|
|
49
|
+
credit: '0.00',
|
|
50
|
+
tax_code: transaction.taxCode,
|
|
51
|
+
cost_center: transaction.costCenter,
|
|
52
|
+
project: transaction.project
|
|
53
|
+
});
|
|
54
|
+
// Add credit line
|
|
55
|
+
entry.lines.push({
|
|
56
|
+
posting_id: `${entry.entry_id}-2`,
|
|
57
|
+
account_code: transaction.creditAccount,
|
|
58
|
+
debit: '0.00',
|
|
59
|
+
credit: transaction.amount.toFixed(2)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Add VAT lines if applicable
|
|
63
|
+
if (transaction.vatAmount && transaction.vatAmount > 0) {
|
|
64
|
+
entry.lines.push({
|
|
65
|
+
posting_id: `${entry.entry_id}-3`,
|
|
66
|
+
account_code: transaction.vatAccount || '1576', // Default VAT account
|
|
67
|
+
debit: transaction.vatAmount.toFixed(2),
|
|
68
|
+
credit: '0.00',
|
|
69
|
+
description: 'Vorsteuer'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
await this.writeLine(entry);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Exports a journal entry
|
|
76
|
+
*/
|
|
77
|
+
async exportJournalEntry(journalEntry) {
|
|
78
|
+
if (!this.stream) {
|
|
79
|
+
throw new Error('Ledger exporter not initialized');
|
|
80
|
+
}
|
|
81
|
+
const entry = {
|
|
82
|
+
schema_version: '1.0',
|
|
83
|
+
entry_id: journalEntry._id || plugins.smartunique.shortId(),
|
|
84
|
+
booking_date: this.formatDate(journalEntry.date),
|
|
85
|
+
posting_date: this.formatDate(journalEntry.postingDate || journalEntry.date),
|
|
86
|
+
currency: journalEntry.currency || 'EUR',
|
|
87
|
+
journal: journalEntry.journal || 'GL',
|
|
88
|
+
description: journalEntry.description,
|
|
89
|
+
reference: journalEntry.reference,
|
|
90
|
+
lines: [],
|
|
91
|
+
created_at: journalEntry.createdAt ? new Date(journalEntry.createdAt).toISOString() : new Date().toISOString(),
|
|
92
|
+
modified_at: journalEntry.modifiedAt ? new Date(journalEntry.modifiedAt).toISOString() : undefined,
|
|
93
|
+
reversal_of: journalEntry.reversalOf,
|
|
94
|
+
reversed_by: journalEntry.reversedBy
|
|
95
|
+
};
|
|
96
|
+
// Convert journal entry lines
|
|
97
|
+
journalEntry.lines.forEach((line, index) => {
|
|
98
|
+
const extLine = line;
|
|
99
|
+
entry.lines.push({
|
|
100
|
+
posting_id: `${entry.entry_id}-${index + 1}`,
|
|
101
|
+
account_code: line.accountNumber,
|
|
102
|
+
debit: (line.debit || 0).toFixed(2),
|
|
103
|
+
credit: (line.credit || 0).toFixed(2),
|
|
104
|
+
tax_code: extLine.taxCode,
|
|
105
|
+
cost_center: line.costCenter,
|
|
106
|
+
project: extLine.project,
|
|
107
|
+
description: line.description
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
await this.writeLine(entry);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Writes a single NDJSON line
|
|
114
|
+
*/
|
|
115
|
+
async writeLine(entry) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
if (!this.stream) {
|
|
118
|
+
reject(new Error('Stream not initialized'));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const line = JSON.stringify(entry) + '\n';
|
|
122
|
+
this.stream.write(line, (error) => {
|
|
123
|
+
if (error) {
|
|
124
|
+
reject(error);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.entryCount++;
|
|
128
|
+
resolve();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Formats a date to ISO date string
|
|
135
|
+
*/
|
|
136
|
+
formatDate(date) {
|
|
137
|
+
if (typeof date === 'string') {
|
|
138
|
+
return date.split('T')[0];
|
|
139
|
+
}
|
|
140
|
+
return date.toISOString().split('T')[0];
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Closes the export stream
|
|
144
|
+
*/
|
|
145
|
+
async close() {
|
|
146
|
+
return new Promise((resolve) => {
|
|
147
|
+
if (this.stream) {
|
|
148
|
+
this.stream.end(() => {
|
|
149
|
+
resolve(this.entryCount);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
resolve(this.entryCount);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Gets the number of exported entries
|
|
159
|
+
*/
|
|
160
|
+
getEntryCount() {
|
|
161
|
+
return this.entryCount;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmV4cG9ydC5sZWRnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9za3IuZXhwb3J0LmxlZGdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQUUsaUJBQWlCLEVBQW9CLE1BQU0sSUFBSSxDQUFDO0FBcUV6RCxNQUFNLE9BQU8sY0FBYztJQUt6QixZQUFZLFVBQWtCO1FBSHRCLFdBQU0sR0FBdUIsSUFBSSxDQUFDO1FBQ2xDLGVBQVUsR0FBVyxDQUFDLENBQUM7UUFHN0IsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7SUFDL0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFVBQVU7UUFDckIsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDckYsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO1FBRS9ELElBQUksQ0FBQyxNQUFNLEdBQUcsaUJBQWlCLENBQUMsVUFBVSxFQUFFO1lBQzFDLFFBQVEsRUFBRSxNQUFNO1lBQ2hCLEtBQUssRUFBRSxHQUFHO1NBQ1gsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLGlCQUFpQixDQUFDLFdBQW1DO1FBQ2hFLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFFRCxNQUFNLEtBQUssR0FBaUI7WUFDMUIsY0FBYyxFQUFFLEtBQUs7WUFDckIsUUFB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
|