-- SeCu Database Schema -- Version: 1.0.0 -- Enable UUID extension CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Organizations CREATE TABLE IF NOT EXISTS organizations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(255) NOT NULL, slug VARCHAR(100) NOT NULL UNIQUE, settings JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Users CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, email VARCHAR(255) NOT NULL, password_hash VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL CHECK (role IN ('chef', 'disponent', 'mitarbeiter')), first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, phone VARCHAR(50), avatar_url TEXT, created_by UUID REFERENCES users(id), managed_by UUID REFERENCES users(id), active BOOLEAN DEFAULT true, last_login TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(org_id, email) ); -- Refresh Tokens CREATE TABLE IF NOT EXISTS refresh_tokens ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, token_hash VARCHAR(255) NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Orders (Aufträge) CREATE TABLE IF NOT EXISTS orders ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, number SERIAL, title VARCHAR(255) NOT NULL, description TEXT, location VARCHAR(255), address TEXT, client_name VARCHAR(255), client_contact VARCHAR(255), status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'in_progress', 'completed', 'cancelled')), start_time TIMESTAMP WITH TIME ZONE, end_time TIMESTAMP WITH TIME ZONE, required_staff INTEGER DEFAULT 1, special_instructions TEXT, created_by UUID REFERENCES users(id), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Order Assignments (Mitarbeiter-Zuweisung) CREATE TABLE IF NOT EXISTS order_assignments ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'declined', 'completed')), note TEXT, confirmed_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(order_id, user_id) ); -- Availability (Verfügbarkeit) CREATE TABLE IF NOT EXISTS availability ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, date DATE NOT NULL, available BOOLEAN NOT NULL DEFAULT true, time_from TIME, time_to TIME, note TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(user_id, date) ); -- Timesheets (Stundenzettel) CREATE TABLE IF NOT EXISTS timesheets ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, order_id UUID REFERENCES orders(id) ON DELETE SET NULL, work_date DATE NOT NULL, start_time TIME, end_time TIME, hours_worked DECIMAL(5,2), photo_url TEXT, status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected')), approved_by UUID REFERENCES users(id), rejection_reason TEXT, approved_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Modules (für modulares System) CREATE TABLE IF NOT EXISTS modules ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL UNIQUE, display_name VARCHAR(255) NOT NULL, description TEXT, is_core BOOLEAN DEFAULT false, default_config JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Organization Modules (welche Module aktiviert) CREATE TABLE IF NOT EXISTS organization_modules ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, module_id UUID NOT NULL REFERENCES modules(id) ON DELETE CASCADE, enabled BOOLEAN DEFAULT true, config JSONB DEFAULT '{}', enabled_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(org_id, module_id) ); -- Indexes for performance CREATE INDEX IF NOT EXISTS idx_users_org_id ON users(org_id); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); CREATE INDEX IF NOT EXISTS idx_users_role ON users(role); CREATE INDEX IF NOT EXISTS idx_users_managed_by ON users(managed_by); CREATE INDEX IF NOT EXISTS idx_orders_org_id ON orders(org_id); CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status); CREATE INDEX IF NOT EXISTS idx_orders_start_time ON orders(start_time); CREATE INDEX IF NOT EXISTS idx_order_assignments_order_id ON order_assignments(order_id); CREATE INDEX IF NOT EXISTS idx_order_assignments_user_id ON order_assignments(user_id); CREATE INDEX IF NOT EXISTS idx_availability_user_id ON availability(user_id); CREATE INDEX IF NOT EXISTS idx_availability_date ON availability(date); CREATE INDEX IF NOT EXISTS idx_timesheets_user_id ON timesheets(user_id); CREATE INDEX IF NOT EXISTS idx_timesheets_work_date ON timesheets(work_date); CREATE INDEX IF NOT EXISTS idx_timesheets_status ON timesheets(status); CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id ON refresh_tokens(user_id); -- Seed data: Core modules INSERT INTO modules (name, display_name, description, is_core) VALUES ('core', 'Kernfunktionen', 'Authentifizierung und Benutzerverwaltung', true), ('orders', 'Auftragsverwaltung', 'Erstellen und Verwalten von Aufträgen', true), ('availability', 'Verfügbarkeit', 'Mitarbeiter-Verfügbarkeitsplanung', false), ('timesheets', 'Stundenzettel', 'Zeiterfassung und Stundenzettel-Upload', false), ('developer', 'Entwickler-Panel', 'Fernverwaltung und Modul-Konfiguration', false) ON CONFLICT (name) DO NOTHING; -- Create a default demo organization INSERT INTO organizations (name, slug) VALUES ('Demo Organisation', 'demo') ON CONFLICT (slug) DO NOTHING;