Schema: bookings mit buchungstyp (Einnahme/Ausgabe), kategorie_konto_id, zahlungskonto_id statt soll_konto_id/haben_konto_id. Kontenarten umbenannt (Ertrag→Betriebseinnahme, Aufwand→Betriebsausgabe, Aktiv→Zahlungskonto, Passiv→Verbindlichkeit, Neutral→Privat). UI, Services, Controller, Tests vollständig auf EÜR-Terminologie umgestellt. Alle 78 Tests bestanden.
253 lines
13 KiB
SQL
253 lines
13 KiB
SQL
-- Steuerkram - Initial Schema
|
|
-- GoBD-konform: Buchungen sind unveränderlich, vollständiges Audit-Log
|
|
|
|
SET NAMES utf8mb4;
|
|
SET time_zone = '+00:00';
|
|
|
|
-- ============================================================
|
|
-- Geschäftsjahre
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS fiscal_years (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
year SMALLINT UNSIGNED NOT NULL UNIQUE,
|
|
start_date DATE NOT NULL,
|
|
end_date DATE NOT NULL,
|
|
locked TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'GoBD: Jahr gesperrt nach Abschluss',
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- Kontenrahmen (SKR03 und anpassbar)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS accounts (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
kontonummer VARCHAR(10) NOT NULL UNIQUE,
|
|
bezeichnung VARCHAR(255) NOT NULL,
|
|
kontenart ENUM('Betriebseinnahme','Betriebsausgabe','Zahlungskonto','Verbindlichkeit','Privat') NOT NULL,
|
|
beschreibung TEXT,
|
|
steuercode VARCHAR(20) NULL COMMENT 'z.B. USt19, VSt19, USt7, VSt7',
|
|
ustva_kz VARCHAR(10) NULL COMMENT 'ELSTER Kennzahl z.B. 81, 35',
|
|
is_system TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = SKR03-Standard',
|
|
aktiv TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
INDEX idx_kontonummer (kontonummer),
|
|
INDEX idx_kontenart (kontenart)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- Firmendaten / Einstellungen (Singleton, id=1)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS company_settings (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
firma VARCHAR(255) NOT NULL DEFAULT '',
|
|
inhaber VARCHAR(255) NOT NULL DEFAULT '',
|
|
strasse VARCHAR(255) NOT NULL DEFAULT '',
|
|
plz VARCHAR(10) NOT NULL DEFAULT '',
|
|
ort VARCHAR(255) NOT NULL DEFAULT '',
|
|
telefon VARCHAR(50),
|
|
email VARCHAR(255),
|
|
website VARCHAR(255),
|
|
ust_id VARCHAR(20) COMMENT 'Umsatzsteuer-ID DE...',
|
|
steuernummer VARCHAR(30) COMMENT 'Steuernummer beim Finanzamt',
|
|
finanzamt VARCHAR(255),
|
|
iban VARCHAR(34),
|
|
bic VARCHAR(11),
|
|
bank_name VARCHAR(255),
|
|
logo_path VARCHAR(500) COMMENT 'Pfad zum Logo-PNG',
|
|
ustva_steuernummer VARCHAR(20) COMMENT 'ELSTER-Format: FFFBBBBBBUUUUP',
|
|
ustva_bundesland VARCHAR(2) COMMENT 'z.B. BY, NW, BE',
|
|
invoice_nr_prefix VARCHAR(10) NOT NULL DEFAULT 'AR',
|
|
invoice_nr_next INT UNSIGNED NOT NULL DEFAULT 1,
|
|
invoice_nr_year SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
|
beleg_nr_prefix VARCHAR(10) NOT NULL DEFAULT 'BLG',
|
|
beleg_nr_next INT UNSIGNED NOT NULL DEFAULT 1,
|
|
payment_days_default INT UNSIGNED NOT NULL DEFAULT 14,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
INSERT IGNORE INTO company_settings (id, firma, inhaber, strasse, plz, ort) VALUES (1, '', '', '', '', '');
|
|
|
|
-- ============================================================
|
|
-- Kunden
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS customers (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
kundennummer VARCHAR(20) NOT NULL UNIQUE,
|
|
firma VARCHAR(255),
|
|
anrede VARCHAR(20),
|
|
vorname VARCHAR(100),
|
|
nachname VARCHAR(100) NOT NULL,
|
|
strasse VARCHAR(255) NOT NULL,
|
|
plz VARCHAR(10) NOT NULL,
|
|
ort VARCHAR(255) NOT NULL,
|
|
land VARCHAR(2) NOT NULL DEFAULT 'DE',
|
|
email VARCHAR(255),
|
|
telefon VARCHAR(50),
|
|
ust_id VARCHAR(20) COMMENT 'Umsatzsteuer-ID des Kunden (B2B)',
|
|
steuernummer VARCHAR(30),
|
|
payment_days INT UNSIGNED NOT NULL DEFAULT 14,
|
|
notes TEXT,
|
|
archived TINYINT(1) NOT NULL DEFAULT 0,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
INDEX idx_kundennummer (kundennummer),
|
|
INDEX idx_archived (archived)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- Belege (Dokumente / Quittungen)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS belege (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
beleg_nr VARCHAR(30) NOT NULL UNIQUE,
|
|
filename VARCHAR(500) NOT NULL COMMENT 'Originaldateiname',
|
|
storage_path VARCHAR(500) NOT NULL COMMENT 'Pfad unter public/static/uploads/',
|
|
mime_type VARCHAR(100) NOT NULL,
|
|
file_hash VARCHAR(64) NOT NULL COMMENT 'SHA-256 für GoBD-Integrität',
|
|
beleg_datum DATE NULL COMMENT 'Datum auf dem Dokument',
|
|
eingangs_datum DATE NOT NULL COMMENT 'Eingangsdatum',
|
|
rechnungs_nr VARCHAR(100) COMMENT 'Rechnungsnummer des Lieferanten',
|
|
betrag_brutto DECIMAL(12,2) COMMENT 'Bruttobetrag lt. Dokument',
|
|
belegtyp ENUM('Eingangsrechnung','Ausgangsrechnung','Quittung','Kontoauszug','Sonstiges') NOT NULL DEFAULT 'Eingangsrechnung',
|
|
notes TEXT,
|
|
booked TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = alle Buchungszeilen erfasst',
|
|
locked TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'GoBD: gesperrt mit Geschäftsjahr',
|
|
fiscal_year_id INT UNSIGNED NULL REFERENCES fiscal_years(id),
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
INDEX idx_beleg_datum (beleg_datum),
|
|
INDEX idx_fiscal_year (fiscal_year_id),
|
|
INDEX idx_booked (booked)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- Buchungen (EÜR-Journaleinträge: Einnahme oder Ausgabe)
|
|
-- GoBD: KEINE UPDATE/DELETE auf gesperrte Buchungen
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS bookings (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
buchungs_nr VARCHAR(30) NOT NULL UNIQUE,
|
|
beleg_id INT UNSIGNED NOT NULL,
|
|
buchungs_datum DATE NOT NULL,
|
|
buchungstyp ENUM('Einnahme','Ausgabe') NOT NULL,
|
|
kategorie_konto_id INT UNSIGNED NOT NULL COMMENT 'Was? z.B. Büromaterial, Honorar',
|
|
zahlungskonto_id INT UNSIGNED NOT NULL COMMENT 'Wie bezahlt? z.B. Bank, Kasse',
|
|
betrag_netto DECIMAL(12,2) NOT NULL,
|
|
ust_satz DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT 'Steuersatz in % (0, 7, 19)',
|
|
betrag_ust DECIMAL(12,2) NOT NULL DEFAULT 0.00,
|
|
betrag_brutto DECIMAL(12,2) NOT NULL,
|
|
buchungstext VARCHAR(500) NOT NULL,
|
|
is_reversal TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = Stornobuchung',
|
|
reversed_by INT UNSIGNED NULL COMMENT 'ID der Stornobuchung',
|
|
locked TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'GoBD: unveränderlich',
|
|
fiscal_year_id INT UNSIGNED NOT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_booking_beleg FOREIGN KEY (beleg_id) REFERENCES belege(id),
|
|
CONSTRAINT fk_booking_kategorie FOREIGN KEY (kategorie_konto_id) REFERENCES accounts(id),
|
|
CONSTRAINT fk_booking_zahlung FOREIGN KEY (zahlungskonto_id) REFERENCES accounts(id),
|
|
CONSTRAINT fk_booking_year FOREIGN KEY (fiscal_year_id) REFERENCES fiscal_years(id),
|
|
INDEX idx_buchungs_datum (buchungs_datum),
|
|
INDEX idx_fiscal_year (fiscal_year_id),
|
|
INDEX idx_buchungstyp (buchungstyp),
|
|
INDEX idx_kategorie_konto (kategorie_konto_id),
|
|
INDEX idx_zahlungskonto (zahlungskonto_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- GoBD-Trigger: Gesperrte Buchungen dürfen nicht geändert werden
|
|
DELIMITER $$
|
|
CREATE TRIGGER bookings_before_update
|
|
BEFORE UPDATE ON bookings
|
|
FOR EACH ROW
|
|
BEGIN
|
|
IF OLD.locked = 1 THEN
|
|
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'GoBD-Verletzung: Gesperrte Buchung kann nicht geändert werden.';
|
|
END IF;
|
|
END$$
|
|
|
|
CREATE TRIGGER bookings_before_delete
|
|
BEFORE DELETE ON bookings
|
|
FOR EACH ROW
|
|
BEGIN
|
|
IF OLD.locked = 1 THEN
|
|
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'GoBD-Verletzung: Gesperrte Buchung kann nicht gelöscht werden.';
|
|
END IF;
|
|
END$$
|
|
DELIMITER ;
|
|
|
|
-- ============================================================
|
|
-- Ausgangsrechnungen
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS invoices (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
rechnungs_nr VARCHAR(30) NULL UNIQUE COMMENT 'NULL solange Entwurf',
|
|
customer_id INT UNSIGNED NOT NULL,
|
|
betreff VARCHAR(500) NOT NULL,
|
|
leistungszeitraum_von DATE NULL,
|
|
leistungszeitraum_bis DATE NULL,
|
|
rechnungs_datum DATE NOT NULL,
|
|
faellig_datum DATE NOT NULL,
|
|
status ENUM('Entwurf','Gesendet','Bezahlt','Storniert') NOT NULL DEFAULT 'Entwurf',
|
|
betrag_netto DECIMAL(12,2) NOT NULL DEFAULT 0.00,
|
|
betrag_ust DECIMAL(12,2) NOT NULL DEFAULT 0.00,
|
|
betrag_brutto DECIMAL(12,2) NOT NULL DEFAULT 0.00,
|
|
beleg_id INT UNSIGNED NULL REFERENCES belege(id),
|
|
notes TEXT,
|
|
fiscal_year_id INT UNSIGNED NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_invoice_customer FOREIGN KEY (customer_id) REFERENCES customers(id),
|
|
INDEX idx_status (status),
|
|
INDEX idx_rechnungs_datum (rechnungs_datum)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- Rechnungspositionen
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS invoice_lines (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
invoice_id INT UNSIGNED NOT NULL,
|
|
position SMALLINT UNSIGNED NOT NULL DEFAULT 1,
|
|
bezeichnung VARCHAR(500) NOT NULL,
|
|
menge DECIMAL(10,3) NOT NULL DEFAULT 1.000,
|
|
einheit VARCHAR(20) NOT NULL DEFAULT 'Stk.',
|
|
einzelpreis DECIMAL(12,2) NOT NULL,
|
|
ust_satz DECIMAL(5,2) NOT NULL DEFAULT 19.00,
|
|
betrag_netto DECIMAL(12,2) NOT NULL,
|
|
betrag_ust DECIMAL(12,2) NOT NULL,
|
|
betrag_brutto DECIMAL(12,2) NOT NULL,
|
|
CONSTRAINT fk_line_invoice FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE CASCADE,
|
|
INDEX idx_invoice_id (invoice_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- UStVA Einreichungen
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS ustva_submissions (
|
|
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
year SMALLINT UNSIGNED NOT NULL,
|
|
month TINYINT UNSIGNED NOT NULL COMMENT '1-12',
|
|
xml_content MEDIUMTEXT NOT NULL COMMENT 'Generiertes ELSTER-XML',
|
|
field_values JSON COMMENT 'Kennzahlen als JSON',
|
|
submitted_at DATETIME NULL COMMENT 'NULL = noch nicht eingereicht',
|
|
status ENUM('Erstellt','Eingereicht') NOT NULL DEFAULT 'Erstellt',
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE KEY uq_year_month (year, month)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- ============================================================
|
|
-- Audit-Log (GoBD-Pflicht)
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
|
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
table_name VARCHAR(50) NOT NULL,
|
|
record_id INT UNSIGNED NOT NULL,
|
|
action ENUM('INSERT','UPDATE','DELETE','LOCK') NOT NULL,
|
|
old_values JSON,
|
|
new_values JSON,
|
|
ip_address VARCHAR(45),
|
|
user_agent VARCHAR(500),
|
|
INDEX idx_table_record (table_name, record_id),
|
|
INDEX idx_timestamp (timestamp)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|