diff --git a/db/migrations/001_initial.sql b/db/migrations/001_initial.sql new file mode 100644 index 0000000..a077d40 --- /dev/null +++ b/db/migrations/001_initial.sql @@ -0,0 +1,163 @@ +-- 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;