diff --git a/README.md b/README.md index 7c0f9b1..98a04a9 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Anwendung aufrufen: `http://localhost:8080` | [Anlagevermögen](docs/anlagevermögen.md) | Anlagenspiegel, Buchungsworkflow, Nummernkreis | | [Update & Backup](docs/update-backup.md) | Update-Anleitung, Backup, Datenpersistenz | | [Automatisierung](docs/automatisierung.md) | Automatisierungsagent, Ideenagent, Support | +| [Datenbankstruktur](docs/datenbankstruktur.md) | Tabellen, Spalten, Fremdschlüssel, GoBD-Mechanismen | | [Berechnungslogik](docs/berechnungslogik.md) | UStVA-Kennzahlen und EÜR-Zuordnungsregeln | | [Kontenrahmen](docs/kontenrahmen.md) | Vollständige SKR03-Kontenliste | diff --git a/docs/datenbankstruktur.md b/docs/datenbankstruktur.md new file mode 100644 index 0000000..b823d68 --- /dev/null +++ b/docs/datenbankstruktur.md @@ -0,0 +1,284 @@ +# Datenbankstruktur + +MariaDB 11, Engine InnoDB, Zeichensatz `utf8mb4_unicode_ci`. Das Schema wird durch nummerierte Migrations-Dateien in `migrations/` verwaltet und beim Container-Start automatisch eingespielt. + +--- + +## Übersicht + +| Tabelle | Zweck | +|---|---| +| `fiscal_years` | Geschäftsjahre | +| `accounts` | Kontenrahmen SKR03 | +| `company_settings` | Firmendaten und Einstellungen (Singleton) | +| `customers` | Kundenstammdaten | +| `belege` | Belegdokumente | +| `bookings` | Buchungszeilen (EÜR-Journal) | +| `invoices` | Ausgangsrechnungen | +| `invoice_lines` | Rechnungspositionen | +| `users` | Benutzerkonten | +| `ustva_submissions` | UStVA-Einreichungen | +| `audit_log` | GoBD-Audit-Log | +| `schema_migrations` | Protokoll angewendeter Migrationen | + +--- + +## Tabellen im Detail + +### `fiscal_years` – Geschäftsjahre + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `year` | SMALLINT UNSIGNED UNIQUE | Jahreszahl (z.B. 2026) | +| `start_date` | DATE | Beginn des Geschäftsjahres | +| `end_date` | DATE | Ende des Geschäftsjahres | +| `locked` | TINYINT(1) | `1` = Jahr abgeschlossen, keine neuen Buchungen möglich | +| `created_at` | DATETIME | | + +--- + +### `accounts` – Kontenrahmen SKR03 + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `kontonummer` | VARCHAR(10) UNIQUE | DATEV-Kontonummer | +| `bezeichnung` | VARCHAR(255) | Kontobezeichnung | +| `kontenart` | ENUM | `Betriebseinnahme`, `Betriebsausgabe`, `Anlagevermögen`, `Zahlungskonto`, `Verbindlichkeit`, `Privat` | +| `beschreibung` | TEXT | Erläuterungstext | +| `steuercode` | VARCHAR(20) | `USt19`, `USt7`, `VSt19`, `VSt7` oder leer | +| `ustva_kz` | VARCHAR(10) | ELSTER-Kennzahl (z.B. `81`, `35`) | +| `euer_zeile` | VARCHAR(5) | Zeilennummer Anlage EÜR (z.B. `14`, `51`) | +| `is_system` | TINYINT(1) | `1` = SKR03-Standardkonto | +| `aktiv` | TINYINT(1) | `0` = deaktiviert | +| `created_at` / `updated_at` | DATETIME | | + +--- + +### `company_settings` – Firmendaten (Singleton, id=1) + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | Immer `1` | +| `firma` | VARCHAR(255) | Firmenname | +| `inhaber` | VARCHAR(255) | Inhabername | +| `strasse`, `plz`, `ort` | VARCHAR | Adresse | +| `telefon`, `email`, `website` | VARCHAR | Kontaktdaten | +| `ust_id` | VARCHAR(20) | Umsatzsteuer-ID (DE...) | +| `steuernummer` | VARCHAR(30) | Steuernummer beim Finanzamt | +| `finanzamt` | VARCHAR(255) | Zuständiges Finanzamt | +| `iban`, `bic`, `bank_name` | VARCHAR | Bankverbindung | +| `logo_path` | VARCHAR(500) | Pfad zum Logo-PNG | +| `ustva_steuernummer` | VARCHAR(20) | ELSTER-Format: `FFFBBBBBBUUUUP` | +| `ustva_bundesland` | VARCHAR(2) | Bundesland-Kürzel (z.B. `BY`) | +| `invoice_nr_prefix` | VARCHAR(10) | Präfix Rechnungsnummer (Standard: `AR`) | +| `invoice_nr_next` | INT UNSIGNED | Nächste Rechnungsnummer | +| `invoice_nr_year` | SMALLINT UNSIGNED | Jahresstand des Rechnungszählers | +| `beleg_nr_prefix` | VARCHAR(10) | Präfix Belegnummer (Standard: `BLG`) | +| `beleg_nr_next` | INT UNSIGNED | Nächste Belegnummer | +| `anlage_nr_prefix` | VARCHAR(10) | Präfix Anlagen-Nummer (Standard: `ANL`) | +| `anlage_nr_next` | INT UNSIGNED | Nächste Anlagen-Nummer | +| `payment_days_default` | INT UNSIGNED | Standard-Zahlungsziel in Tagen | +| `ki_vorschlag_aktiv` | TINYINT(1) | KI-Kontierungsvorschlag aktiviert | +| `nextcloud_url` | VARCHAR(500) | WebDAV-URL der Nextcloud-Instanz | +| `nextcloud_user` | VARCHAR(255) | Nextcloud-Benutzername | +| `nextcloud_pass` | VARCHAR(500) | Nextcloud-Passwort / App-Passwort | +| `nextcloud_folder` | VARCHAR(500) | Import-Quellordner (Standard: `/Belege/Import`) | +| `nextcloud_done` | VARCHAR(500) | Zielordner nach erfolgreichem Import | +| `updated_at` | DATETIME | | + +--- + +### `customers` – Kundenstammdaten + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `kundennummer` | VARCHAR(20) UNIQUE | | +| `firma` | VARCHAR(255) | Firmenname (optional) | +| `anrede`, `vorname`, `nachname` | VARCHAR | Ansprechpartner | +| `strasse`, `plz`, `ort`, `land` | VARCHAR | Adresse | +| `email`, `telefon` | VARCHAR | Kontakt | +| `ust_id` | VARCHAR(20) | USt-ID des Kunden (B2B) | +| `steuernummer` | VARCHAR(30) | | +| `payment_days` | INT UNSIGNED | Zahlungsziel in Tagen | +| `notes` | TEXT | Interne Notizen | +| `archived` | TINYINT(1) | `1` = archiviert | +| `created_at` / `updated_at` | DATETIME | | + +--- + +### `belege` – Belegdokumente + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `beleg_nr` | VARCHAR(30) UNIQUE | Automatisch vergeben (z.B. `BLG-2026-0001`) | +| `filename` | VARCHAR(500) | Originaldateiname | +| `storage_path` | VARCHAR(500) | Pfad unter `public/static/uploads/` | +| `mime_type` | VARCHAR(100) | MIME-Typ der Datei | +| `file_hash` | VARCHAR(64) | SHA-256 für GoBD-Integritätsprüfung | +| `beleg_datum` | DATE | Datum auf dem Dokument | +| `rechnungs_nr` | VARCHAR(100) | Rechnungsnummer des Lieferanten | +| `betrag_brutto` | DECIMAL(12,2) | Bruttobetrag laut Dokument | +| `belegtyp` | ENUM | `Eingangsrechnung`, `Ausgangsrechnung`, `Quittung`, `Kontoauszug`, `Sonstiges` | +| `notes` | TEXT | Interne Notizen | +| `bewirtungsgrund` | TEXT | Anlass der Bewirtung (§ 4 Abs. 5 Nr. 2 EStG) | +| `teilnehmerliste` | TEXT | Teilnehmer der Bewirtung | +| `eigenanteil_30` | TINYINT(1) | `1` = 30%-Eigenanteil beim Buchen markiert | +| `ki_vorschlag` | JSON | KI-Kontierungsvorschlag | +| `ki_vorschlag_at` | TIMESTAMP | Zeitpunkt der KI-Analyse | +| `beleg_text` | MEDIUMTEXT | Extrahierter OCR-Text | +| `locked` | TINYINT(1) | GoBD: mit Geschäftsjahr gesperrt | +| `fiscal_year_id` | INT UNSIGNED FK | → `fiscal_years.id` | +| `created_at` / `updated_at` | DATETIME | | + +--- + +### `bookings` – Buchungszeilen (EÜR-Journal) + +GoBD-gesichert: MariaDB-Trigger verhindern Änderungen und Löschungen gesperrter Buchungen. + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `buchungs_nr` | VARCHAR(30) UNIQUE | Automatisch vergeben | +| `beleg_id` | INT UNSIGNED FK | → `belege.id` | +| `asset_id` | INT UNSIGNED FK | → `assets.id` (nur bei Anlagevermögen-Buchungen) | +| `buchungs_datum` | DATE | | +| `buchungstyp` | ENUM | `Einnahme`, `Ausgabe` | +| `kategorie_konto_id` | INT UNSIGNED FK | → `accounts.id` (Was?) | +| `zahlungskonto_id` | INT UNSIGNED FK | → `accounts.id` (Wie bezahlt?) | +| `betrag_netto` | DECIMAL(12,2) | | +| `ust_satz` | DECIMAL(5,2) | Steuersatz in % (0, 7, 19) | +| `steuerart` | ENUM | `normal`, `ig_lieferung`, `export`, `ig_erwerb_19`, `ig_erwerb_7` | +| `betrag_ust` | DECIMAL(12,2) | | +| `betrag_brutto` | DECIMAL(12,2) | | +| `buchungstext` | VARCHAR(500) | | +| `is_reversal` | TINYINT(1) | `1` = Stornobuchung | +| `reversed_by` | INT UNSIGNED | ID der zugehörigen Stornobuchung | +| `locked` | TINYINT(1) | GoBD: unveränderlich | +| `fiscal_year_id` | INT UNSIGNED FK | → `fiscal_years.id` | +| `created_at` | DATETIME | | + +> **Hinweis:** Die Spalte `asset_id` existiert nur nach Migration 030 (Feature-Branch `Betriebsvermögen`). + +--- + +### `assets` – Anlagegüter (Wirtschaftsgüter) + +> Diese Tabelle existiert nur nach Migration 028 (Feature-Branch `Betriebsvermögen`). + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `lfd_nr` | INT UNSIGNED | Laufende Nummer | +| `anlage_nr` | VARCHAR(30) | Anlagen-Nummer (z.B. `ANL-2026-0001`) | +| `bezeichnung` | VARCHAR(255) | Bezeichnung des Wirtschaftsguts | +| `account_id` | INT UNSIGNED FK | → `accounts.id` (Anlageart) | +| `anschaffungsdatum` | DATE | | +| `anschaffungskosten` | DECIMAL(12,2) | Netto-Anschaffungskosten | +| `zugaenge` | DECIMAL(12,2) | Nachträgliche Anschaffungskosten | +| `abgaenge` | DECIMAL(12,2) | Wertminderungen | +| `abschreibung_jahr` | DECIMAL(12,2) | AfA laufendes Wirtschaftsjahr | +| `nutzungsdauer` | INT UNSIGNED | Nutzungsdauer in Jahren | +| `notizen` | TEXT | | +| `fiscal_year_id` | INT UNSIGNED FK | → `fiscal_years.id` | +| `created_at` / `updated_at` | DATETIME | | + +--- + +### `invoices` – Ausgangsrechnungen + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `rechnungs_nr` | VARCHAR(30) UNIQUE | `NULL` solange Entwurf | +| `customer_id` | INT UNSIGNED FK | → `customers.id` | +| `betreff` | VARCHAR(500) | | +| `leistungszeitraum_von` / `_bis` | DATE | | +| `rechnungs_datum` | DATE | | +| `faellig_datum` | DATE | | +| `status` | ENUM | `Entwurf`, `Gesendet`, `Bezahlt`, `Storniert` | +| `betrag_netto` / `_ust` / `_brutto` | DECIMAL(12,2) | Summenspalten | +| `beleg_id` | INT UNSIGNED FK | → `belege.id` | +| `notes` | TEXT | | +| `fiscal_year_id` | INT UNSIGNED FK | → `fiscal_years.id` | +| `created_at` / `updated_at` | DATETIME | | + +--- + +### `invoice_lines` – Rechnungspositionen + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `invoice_id` | INT UNSIGNED FK | → `invoices.id` (CASCADE DELETE) | +| `position` | SMALLINT UNSIGNED | Positionsnummer | +| `bezeichnung` | VARCHAR(500) | | +| `menge` | DECIMAL(10,3) | | +| `einheit` | VARCHAR(20) | z.B. `Stk.`, `h` | +| `einzelpreis` | DECIMAL(12,2) | Netto-Einzelpreis | +| `ust_satz` | DECIMAL(5,2) | | +| `betrag_netto` / `_ust` / `_brutto` | DECIMAL(12,2) | | + +--- + +### `users` – Benutzerkonten + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `username` | VARCHAR(64) UNIQUE | | +| `password_hash` | VARCHAR(255) | bcrypt | +| `active` | TINYINT(1) | `0` = deaktiviert | +| `created_at` | DATETIME | | + +--- + +### `ustva_submissions` – UStVA-Einreichungen + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | INT UNSIGNED PK | | +| `year` | SMALLINT UNSIGNED | Meldejahr | +| `month` | TINYINT UNSIGNED | Meldemonat (1–12) | +| `xml_content` | MEDIUMTEXT | Generiertes ELSTER-XML | +| `field_values` | JSON | Kennzahlen als JSON | +| `submitted_at` | DATETIME | `NULL` = noch nicht eingereicht | +| `status` | ENUM | `Erstellt`, `Eingereicht` | +| `created_at` | DATETIME | | + +UNIQUE KEY auf `(year, month)`. + +--- + +### `audit_log` – GoBD-Audit-Log + +| Spalte | Typ | Beschreibung | +|---|---|---| +| `id` | BIGINT UNSIGNED PK | | +| `timestamp` | DATETIME | | +| `table_name` | VARCHAR(50) | Betroffene Tabelle | +| `record_id` | INT UNSIGNED | Betroffener Datensatz | +| `action` | ENUM | `INSERT`, `UPDATE`, `DELETE`, `LOCK` | +| `old_values` | JSON | Werte vor der Änderung | +| `new_values` | JSON | Werte nach der Änderung | +| `ip_address` | VARCHAR(45) | | +| `user_agent` | VARCHAR(500) | | + +--- + +## GoBD-Mechanismen + +- **Trigger `bookings_before_update` / `bookings_before_delete`**: Jeder Versuch, eine gesperrte Buchung (`locked = 1`) zu ändern oder zu löschen, wird mit einem SQL-Fehler abgewiesen. +- **`file_hash` in `belege`**: SHA-256-Prüfsumme jedes Belegdokuments; erlaubt nachträgliche Integritätsprüfung. +- **`audit_log`**: Protokolliert alle Datenbankänderungen mit alten und neuen Werten. +- **Stornobuchungen** statt Löschungen: Korrekturen erzeugen eine Gegenbuchung, die ursprüngliche Buchung bleibt erhalten. + +--- + +## Migrationen + +Schemaänderungen kommen ausschließlich als nummerierte SQL-Dateien in `migrations/` und werden vom Script `scripts/migrate.php` beim Container-Start automatisch eingespielt. Die Tabelle `schema_migrations` protokolliert, welche Dateien bereits angewendet wurden.