Belege zu Buchungen: Durchsuchen, Ziehen-und-Ablegen, Einfügen
Zu jeder Buchung lässt sich ein Beleg hinterlegen, im Formular oder nachträglich über die Buchungsliste. Der Medientyp wird aus der Dateisignatur bestimmt, nicht aus Endung oder Browserangabe: eine als rechnung.pdf benannte Textdatei wird abgewiesen. SVG ist ausgeschlossen, weil es Skripte enthalten kann, die beim Anzeigen ausgeführt würden. Ausgeliefert wird mit nosniff und enger Content-Security-Policy. Zur Aufbewahrungspflicht nach §147 AO werden Belege nie überschrieben und nie gelöscht. Beim Ersetzen bleibt die alte Datei liegen; das Lösen der Verknüpfung entfernt nur den Verweis. Der Dateiname trägt den SHA-256-Anfang, die Buchung den vollständigen Hash. Ablage unter data/belege/<mandant>/<jahr>/ – je Mandant getrennt und im selben Bind-Mount wie die Datenbanken, sodass ein Backup des data-Ordners Buchungen und Belege gemeinsam erfasst. Bisher fehlte jede Möglichkeit, bestehende Mandantendateien um neue Spalten zu erweitern: CREATE TABLE IF NOT EXISTS lässt vorhandene Tabellen unverändert. Neu hinzugekommene Spalten werden jetzt beim Öffnen additiv ergänzt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
49ddaeccff
commit
b343e89fbf
+33
-1
@@ -64,6 +64,33 @@ export function ausProjektwurzel(pfad: string): string {
|
||||
return isAbsolute(pfad) ? pfad : resolve(projektWurzel, pfad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nachträglich ergänzte Spalten.
|
||||
*
|
||||
* `CREATE TABLE IF NOT EXISTS` lässt bestehende Tabellen unverändert – ohne
|
||||
* diesen Schritt fehlten in bereits angelegten Mandantendateien alle später
|
||||
* hinzugekommenen Spalten. Die Ergänzung ist bewusst additiv: es wird nie eine
|
||||
* Spalte entfernt oder umbenannt, damit ältere Stände lesbar bleiben.
|
||||
*/
|
||||
const NACHTRAEGLICHE_SPALTEN: { tabelle: string; spalte: string; definition: string }[] = [
|
||||
{ tabelle: 'buchungen', spalte: 'beleg_name', definition: "TEXT NOT NULL DEFAULT ''" },
|
||||
{ tabelle: 'buchungen', spalte: 'beleg_typ', definition: "TEXT NOT NULL DEFAULT ''" },
|
||||
{ tabelle: 'buchungen', spalte: 'beleg_hash', definition: "TEXT NOT NULL DEFAULT ''" },
|
||||
{ tabelle: 'buchungen', spalte: 'beleg_groesse', definition: 'INTEGER NOT NULL DEFAULT 0' },
|
||||
{ tabelle: 'konten', spalte: 'ustva_kz', definition: 'TEXT' },
|
||||
];
|
||||
|
||||
function ergaenzeSpalten(verbindung: Database.Database): void {
|
||||
for (const { tabelle, spalte, definition } of NACHTRAEGLICHE_SPALTEN) {
|
||||
const vorhanden = (
|
||||
verbindung.prepare(`PRAGMA table_info(${tabelle})`).all() as { name: string }[]
|
||||
).some((s) => s.name === spalte);
|
||||
if (!vorhanden) {
|
||||
verbindung.exec(`ALTER TABLE ${tabelle} ADD COLUMN ${spalte} ${definition}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Öffnet eine Mandantendatei und legt das Schema an, falls es noch fehlt. */
|
||||
export function oeffneDatenbank(datei: string): Database.Database {
|
||||
const pfad = ausProjektwurzel(datei);
|
||||
@@ -72,6 +99,7 @@ export function oeffneDatenbank(datei: string): Database.Database {
|
||||
verbindung.pragma('journal_mode = WAL');
|
||||
verbindung.pragma('foreign_keys = ON');
|
||||
verbindung.exec(SCHEMA);
|
||||
ergaenzeSpalten(verbindung);
|
||||
verbindung.prepare('INSERT OR IGNORE INTO mandant (id) VALUES (1)').run();
|
||||
return verbindung;
|
||||
}
|
||||
@@ -172,7 +200,11 @@ CREATE TABLE IF NOT EXISTS buchungen (
|
||||
partner TEXT NOT NULL DEFAULT '',
|
||||
privatanteil_bp INTEGER NOT NULL DEFAULT 0,
|
||||
anlagegut_id INTEGER REFERENCES anlagegueter(id),
|
||||
beleg_datei TEXT,
|
||||
beleg_datei TEXT, -- Pfad relativ zu data/, siehe belege.ts
|
||||
beleg_name TEXT NOT NULL DEFAULT '', -- ursprünglicher Dateiname
|
||||
beleg_typ TEXT NOT NULL DEFAULT '', -- erkannter Medientyp
|
||||
beleg_hash TEXT NOT NULL DEFAULT '', -- SHA-256, GoBD-Unveränderbarkeit
|
||||
beleg_groesse INTEGER NOT NULL DEFAULT 0,
|
||||
-- GoBD: keine Löschung, nur Storno; festgeschriebene Sätze sind unveränderbar
|
||||
storniert INTEGER NOT NULL DEFAULT 0,
|
||||
storno_von INTEGER REFERENCES buchungen(id),
|
||||
|
||||
Reference in New Issue
Block a user