UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

315 lines (307 loc) 28.4 kB
import * as plugins from './plugins.js'; import * as path from 'path'; export class SkrExport { constructor(options) { this.manifest = {}; this.tagManifest = {}; this.options = options; this.logger = new plugins.smartlog.ConsoleLog(); this.exportDir = path.join(options.exportPath, `jahresabschluss_${options.fiscalYear}`); } /** * Creates the BagIt directory structure for the export */ async createBagItStructure() { this.logger.log('info', 'Creating BagIt directory structure...'); // Create main directories await plugins.smartfile.fs.ensureDir(this.exportDir); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata', 'schemas')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata', 'schemas', 'v1')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'metadata', 'signatures')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'accounting')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'accounting', 'ebilanz')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'documents')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'documents', 'by-hash')); await plugins.smartfile.fs.ensureDir(path.join(this.exportDir, 'data', 'reports')); // Create BagIt declaration file await this.createBagItDeclaration(); // Create README await this.createReadme(); this.logger.log('ok', 'BagIt structure created successfully'); } /** * Creates the bagit.txt declaration file */ async createBagItDeclaration() { const bagitContent = `BagIt-Version: 1.0 Tag-File-Character-Encoding: UTF-8`; const filePath = path.join(this.exportDir, 'bagit.txt'); await plugins.smartfile.memory.toFs(bagitContent, filePath); // Add to tag manifest const hash = await this.hashFile(filePath); this.tagManifest['bagit.txt'] = hash; } /** * Creates the README.txt file with Verfahrensdokumentation */ async createReadme() { const readmeContent = `SKR Jahresabschluss Export - Verfahrensdokumentation ===================================================== Dieses Archiv enthält einen revisionssicheren Export des Jahresabschlusses gemäß den Grundsätzen ordnungsmäßiger Buchführung (GoBD). Export-Datum: ${new Date().toISOString()} Geschäftsjahr: ${this.options.fiscalYear} Zeitraum: ${this.options.dateFrom.toISOString()} bis ${this.options.dateTo.toISOString()} STRUKTUR DES ARCHIVS -------------------- - /data/accounting/: Buchhaltungsdaten (Journale, Konten, Salden) - /data/documents/: Belegdokumente (content-adressiert) - /data/reports/: Finanzberichte (PDF/A-3) - /data/metadata/: Export-Metadaten und Schemas - /data/metadata/signatures/: Digitale Signaturen und Zeitstempel INTEGRITÄTSSICHERUNG -------------------- - Alle Dateien sind mit SHA-256 gehasht (siehe manifest-sha256.txt) - Optional: Digitale Signatur (CAdES) über Manifest - Optional: RFC 3161 Zeitstempel AUFBEWAHRUNG ------------ Dieses Archiv muss gemäß § 147 AO für 10 Jahre revisionssicher aufbewahrt werden. Empfohlen wird die Speicherung auf WORM-Medien. REIMPORT -------- Das Archiv kann mit der SKR-Software vollständig reimportiert werden. Die Datenintegrität wird beim Import automatisch verifiziert. COMPLIANCE ---------- - GoBD-konform - E-Bilanz-fähig (XBRL) - ZUGFeRD/Factur-X kompatibel - PDF/A-3 für Langzeitarchivierung © ${new Date().getFullYear()} ${this.options.companyInfo?.name || 'Export System'}`; const filePath = path.join(this.exportDir, 'readme.txt'); await plugins.smartfile.memory.toFs(readmeContent, filePath); // Add to tag manifest const hash = await this.hashFile(filePath); this.tagManifest['readme.txt'] = hash; } /** * Creates the export metadata JSON file */ async createExportMetadata(skrType) { const metadata = { exportVersion: '1.0.0', exportTimestamp: new Date().toISOString(), generator: { name: '@fin.cx/skr', version: '1.1.0' // Should be read from package.json }, company: this.options.companyInfo, fiscalYear: this.options.fiscalYear, dateRange: { from: this.options.dateFrom.toISOString(), to: this.options.dateTo.toISOString() }, skrType: skrType, schemaVersion: '1.0', crypto: { digestAlgorithms: ['sha256'], signatureType: this.options.signExport ? 'CAdES' : undefined, timestampPolicy: this.options.timestampExport ? 'RFC3161' : undefined, merkleTree: true }, options: { packagedAs: 'bagit', compression: 'none', deduplication: true } }; const filePath = path.join(this.exportDir, 'data', 'metadata', 'export.json'); await plugins.smartfile.memory.toFs(JSON.stringify(metadata, null, 2), filePath); // Add to manifest const hash = await this.hashFile(filePath); this.manifest['data/metadata/export.json'] = hash; } /** * Creates JSON schemas for the export data structures */ async createSchemas() { // Ledger schema const ledgerSchema = { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Ledger Entry", "type": "object", "properties": { "schema_version": { "type": "string" }, "entry_id": { "type": "string", "format": "uuid" }, "booking_date": { "type": "string", "format": "date" }, "posting_date": { "type": "string", "format": "date" }, "currency": { "type": "string" }, "journal": { "type": "string" }, "description": { "type": "string" }, "lines": { "type": "array", "items": { "type": "object", "properties": { "posting_id": { "type": "string" }, "account_code": { "type": "string" }, "debit": { "type": "string" }, "credit": { "type": "string" }, "tax_code": { "type": "string" }, "document_refs": { "type": "array", "items": { "type": "object", "properties": { "content_hash": { "type": "string" }, "doc_role": { "type": "string" }, "mime": { "type": "string" } } } } }, "required": ["posting_id", "account_code", "debit", "credit"] } }, "created_at": { "type": "string", "format": "date-time" }, "user": { "type": "string" } }, "required": ["schema_version", "entry_id", "booking_date", "lines"] }; // Accounts schema const accountsSchema = { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Accounts CSV", "type": "object", "properties": { "account_code": { "type": "string" }, "name": { "type": "string" }, "type": { "type": "string" }, "parent": { "type": "string" }, "skr_set": { "type": "string" }, "tax_code_default": { "type": "string" }, "active_from": { "type": "string", "format": "date" }, "active_to": { "type": "string", "format": "date" } }, "required": ["account_code", "name", "type", "skr_set"] }; // Save schemas const schemasDir = path.join(this.exportDir, 'data', 'metadata', 'schemas', 'v1'); await plugins.smartfile.memory.toFs(JSON.stringify(ledgerSchema, null, 2), path.join(schemasDir, 'ledger.schema.json')); this.manifest['data/metadata/schemas/v1/ledger.schema.json'] = await this.hashFile(path.join(schemasDir, 'ledger.schema.json')); await plugins.smartfile.memory.toFs(JSON.stringify(accountsSchema, null, 2), path.join(schemasDir, 'accounts.schema.json')); this.manifest['data/metadata/schemas/v1/accounts.schema.json'] = await this.hashFile(path.join(schemasDir, 'accounts.schema.json')); } /** * Writes the BagIt manifest files */ async writeManifests() { // Write data manifest let manifestContent = ''; for (const [filePath, hash] of Object.entries(this.manifest)) { manifestContent += `${hash} ${filePath}\n`; } const manifestPath = path.join(this.exportDir, 'manifest-sha256.txt'); await plugins.smartfile.memory.toFs(manifestContent, manifestPath); // Add manifest to tag manifest this.tagManifest['manifest-sha256.txt'] = await this.hashFile(manifestPath); // Write tag manifest let tagManifestContent = ''; for (const [filePath, hash] of Object.entries(this.tagManifest)) { tagManifestContent += `${hash} ${filePath}\n`; } await plugins.smartfile.memory.toFs(tagManifestContent, path.join(this.exportDir, 'tagmanifest-sha256.txt')); } /** * Calculates SHA-256 hash of a file */ async hashFile(filePath) { const fileContent = await plugins.smartfile.fs.toBuffer(filePath); return await plugins.smarthash.sha256FromBuffer(fileContent); } /** * Stores a document in content-addressed storage */ async storeDocument(content, originalFilename) { const hash = await plugins.smarthash.sha256FromBuffer(content); // Create path based on hash (first 2 chars as directory) const hashPrefix = hash.substring(0, 2); const hashDir = path.join(this.exportDir, 'data', 'documents', 'by-hash', hashPrefix); await plugins.smartfile.fs.ensureDir(hashDir); const docPath = path.join(hashDir, hash); // Only store if not already exists (deduplication) if (!(await plugins.smartfile.fs.fileExists(docPath))) { await plugins.smartfile.memory.toFs(content, docPath); this.manifest[`data/documents/by-hash/${hashPrefix}/${hash}`] = hash; } return hash; } /** * Creates a Merkle tree from all file hashes */ async createMerkleTree() { const leaves = Object.values(this.manifest).map(hash => Buffer.from(hash, 'hex')); // Create a sync hash function wrapper for MerkleTree const hashFn = (data) => { // Convert async to sync by using crypto directly const crypto = require('crypto'); return crypto.createHash('sha256').update(data).digest(); }; const tree = new plugins.MerkleTree(leaves, hashFn, { sortPairs: true }); const root = tree.getRoot().toString('hex'); // Save Merkle tree data const merkleData = { root: root, leaves: Object.entries(this.manifest).map(([path, hash]) => ({ path, hash })), timestamp: new Date().toISOString() }; const merklePath = path.join(this.exportDir, 'data', 'metadata', 'merkle-tree.json'); await plugins.smartfile.memory.toFs(JSON.stringify(merkleData, null, 2), merklePath); this.manifest['data/metadata/merkle-tree.json'] = await this.hashFile(merklePath); return root; } /** * Validates the BagIt structure */ async validateBagIt() { this.logger.log('info', 'Validating BagIt structure...'); // Check required files exist const requiredFiles = [ 'bagit.txt', 'manifest-sha256.txt', 'tagmanifest-sha256.txt', 'readme.txt' ]; for (const file of requiredFiles) { const filePath = path.join(this.exportDir, file); if (!(await plugins.smartfile.fs.fileExists(filePath))) { this.logger.log('error', `Required file missing: ${file}`); return false; } } // Verify all manifest entries for (const [relPath, expectedHash] of Object.entries(this.manifest)) { const fullPath = path.join(this.exportDir, relPath); if (!(await plugins.smartfile.fs.fileExists(fullPath))) { this.logger.log('error', `Manifest file missing: ${relPath}`); return false; } const actualHash = await this.hashFile(fullPath); if (actualHash !== expectedHash) { this.logger.log('error', `Hash mismatch for ${relPath}`); return false; } } this.logger.log('ok', 'BagIt validation successful'); return true; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmV4cG9ydC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3Nrci5leHBvcnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFvRTdCLE1BQU0sT0FBTyxTQUFTO0lBT3BCLFlBQVksT0FBdUI7UUFIM0IsYUFBUSxHQUFtQixFQUFFLENBQUM7UUFDOUIsZ0JBQVcsR0FBbUIsRUFBRSxDQUFDO1FBR3ZDLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2hELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLG1CQUFtQixPQUFPLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUMxRixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsb0JBQW9CO1FBQy9CLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSx1Q0FBdUMsQ0FBQyxDQUFDO1FBRWpFLDBCQUEwQjtRQUMxQixNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDckQsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDeEUsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxVQUFVLENBQUMsQ0FBQyxDQUFDO1FBQ3BGLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFDL0YsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDckcsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxVQUFVLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FBQztRQUNsRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7UUFDdEYsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQztRQUNqRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDLENBQUM7UUFDckYsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsU0FBUyxD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