Files
secu/db/migrations/001_initial.sql
2026-03-12 15:53:54 +00:00

164 lines
6.5 KiB
SQL

-- 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;