571 Zeilen
18 KiB
TypeScript
571 Zeilen
18 KiB
TypeScript
import Database from 'better-sqlite3'
|
|
import path from 'path'
|
|
import { Employee, User, SkillDefinition } from '@skillmate/shared'
|
|
import bcrypt from 'bcryptjs'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { db as secureDb, encryptedDb, initializeSecureDatabase } from './secureDatabase'
|
|
export { initializeSecureDatabase } from './secureDatabase'
|
|
|
|
// Export the secure database instance
|
|
export const db = secureDb
|
|
|
|
export function initializeDatabase() {
|
|
// Users table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
username TEXT UNIQUE NOT NULL,
|
|
email TEXT UNIQUE NOT NULL,
|
|
password TEXT NOT NULL,
|
|
role TEXT NOT NULL CHECK(role IN ('admin', 'superuser', 'user')),
|
|
employee_id TEXT,
|
|
last_login TEXT,
|
|
is_active INTEGER DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
)
|
|
`)
|
|
|
|
// Profiles table (erweitert für Yellow Pages)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS profiles (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
department TEXT,
|
|
location TEXT,
|
|
role TEXT,
|
|
email TEXT,
|
|
phone TEXT,
|
|
teams_link TEXT,
|
|
job_category TEXT CHECK(job_category IN ('Technik', 'IT & Digitalisierung', 'Verwaltung', 'F&E', 'Kommunikation & HR', 'Produktion', 'Sonstiges')),
|
|
job_title TEXT,
|
|
job_desc TEXT,
|
|
consent_public_profile INTEGER DEFAULT 0,
|
|
consent_searchable INTEGER DEFAULT 0,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
updated_by TEXT NOT NULL,
|
|
review_due_at TEXT,
|
|
search_vector TEXT
|
|
)
|
|
`)
|
|
|
|
// Volltext-Index für Suche
|
|
db.exec(`
|
|
CREATE INDEX IF NOT EXISTS idx_profiles_search
|
|
ON profiles(search_vector);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_profiles_department
|
|
ON profiles(department);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_profiles_location
|
|
ON profiles(location);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_profiles_job_category
|
|
ON profiles(job_category);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_profiles_review_due
|
|
ON profiles(review_due_at);
|
|
`)
|
|
|
|
// Employees table (für Kompatibilität beibehalten)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS employees (
|
|
id TEXT PRIMARY KEY,
|
|
first_name TEXT NOT NULL,
|
|
last_name TEXT NOT NULL,
|
|
employee_number TEXT UNIQUE NOT NULL,
|
|
photo TEXT,
|
|
position TEXT NOT NULL,
|
|
department TEXT NOT NULL,
|
|
email TEXT NOT NULL,
|
|
phone TEXT NOT NULL,
|
|
mobile TEXT,
|
|
office TEXT,
|
|
availability TEXT NOT NULL,
|
|
clearance_level TEXT,
|
|
clearance_valid_until TEXT,
|
|
clearance_issued_date TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
created_by TEXT NOT NULL,
|
|
updated_by TEXT
|
|
)
|
|
`)
|
|
|
|
// Skills table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS skills (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
category TEXT NOT NULL,
|
|
description TEXT,
|
|
requires_certification INTEGER DEFAULT 0,
|
|
expires_after INTEGER
|
|
)
|
|
`)
|
|
|
|
// Employee skills junction table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS employee_skills (
|
|
employee_id TEXT NOT NULL,
|
|
skill_id TEXT NOT NULL,
|
|
level TEXT,
|
|
verified INTEGER DEFAULT 0,
|
|
verified_by TEXT,
|
|
verified_date TEXT,
|
|
PRIMARY KEY (employee_id, skill_id),
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE
|
|
)
|
|
`)
|
|
|
|
// Profile Kompetenzen (Arrays als separate Tabellen)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS profile_domains (
|
|
profile_id TEXT NOT NULL,
|
|
domain TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, domain),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_tools (
|
|
profile_id TEXT NOT NULL,
|
|
tool TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, tool),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_methods (
|
|
profile_id TEXT NOT NULL,
|
|
method TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, method),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_industry_knowledge (
|
|
profile_id TEXT NOT NULL,
|
|
knowledge TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, knowledge),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_regulatory (
|
|
profile_id TEXT NOT NULL,
|
|
regulation TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, regulation),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_networks (
|
|
profile_id TEXT NOT NULL,
|
|
network TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, network),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_digital_skills (
|
|
profile_id TEXT NOT NULL,
|
|
skill TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, skill),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS profile_social_skills (
|
|
profile_id TEXT NOT NULL,
|
|
skill TEXT NOT NULL,
|
|
PRIMARY KEY (profile_id, skill),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
`)
|
|
|
|
// Profile Sprachen
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS profile_languages (
|
|
profile_id TEXT NOT NULL,
|
|
code TEXT NOT NULL,
|
|
level TEXT NOT NULL CHECK(level IN ('basic', 'fluent', 'native', 'business')),
|
|
PRIMARY KEY (profile_id, code),
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
`)
|
|
|
|
// Profile Projekte
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS profile_projects (
|
|
id TEXT PRIMARY KEY,
|
|
profile_id TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
role TEXT,
|
|
summary TEXT,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS project_links (
|
|
project_id TEXT NOT NULL,
|
|
link TEXT NOT NULL,
|
|
PRIMARY KEY (project_id, link),
|
|
FOREIGN KEY (project_id) REFERENCES profile_projects(id) ON DELETE CASCADE
|
|
);
|
|
`)
|
|
|
|
// Language skills table (für Kompatibilität)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS language_skills (
|
|
id TEXT PRIMARY KEY,
|
|
employee_id TEXT NOT NULL,
|
|
language TEXT NOT NULL,
|
|
proficiency TEXT NOT NULL,
|
|
certified INTEGER DEFAULT 0,
|
|
certificate_type TEXT,
|
|
is_native INTEGER DEFAULT 0,
|
|
can_interpret INTEGER DEFAULT 0,
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
|
|
)
|
|
`)
|
|
|
|
// Specializations table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS specializations (
|
|
id TEXT PRIMARY KEY,
|
|
employee_id TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
|
|
)
|
|
`)
|
|
|
|
// Sync log table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS sync_log (
|
|
id TEXT PRIMARY KEY,
|
|
sync_time TEXT NOT NULL,
|
|
success INTEGER NOT NULL,
|
|
items_synced INTEGER,
|
|
error_message TEXT,
|
|
duration INTEGER
|
|
)
|
|
`)
|
|
|
|
// Create default admin user if not exists
|
|
const adminExists = db.prepare('SELECT id FROM users WHERE username = ?').get('admin')
|
|
if (!adminExists) {
|
|
const hashedPassword = bcrypt.hashSync('admin123', 10)
|
|
const now = new Date().toISOString()
|
|
db.prepare(`
|
|
INSERT INTO users (id, username, email, password, role, is_active, created_at, updated_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
`).run(
|
|
uuidv4(),
|
|
'admin',
|
|
'admin@skillmate.local',
|
|
hashedPassword,
|
|
'admin',
|
|
1,
|
|
now,
|
|
now
|
|
)
|
|
}
|
|
|
|
// Workspace Management Tables
|
|
|
|
// Workspaces (Desks, Meeting Rooms, etc.)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS workspaces (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
type TEXT NOT NULL CHECK(type IN ('desk', 'meeting_room', 'phone_booth', 'parking', 'locker')),
|
|
floor TEXT NOT NULL,
|
|
building TEXT,
|
|
capacity INTEGER DEFAULT 1,
|
|
equipment TEXT, -- JSON array of equipment
|
|
position_x INTEGER, -- For floor plan visualization
|
|
position_y INTEGER,
|
|
is_active INTEGER DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
)
|
|
`)
|
|
|
|
// Bookings table
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS bookings (
|
|
id TEXT PRIMARY KEY,
|
|
workspace_id TEXT NOT NULL,
|
|
user_id TEXT NOT NULL,
|
|
employee_id TEXT NOT NULL,
|
|
start_time TEXT NOT NULL,
|
|
end_time TEXT NOT NULL,
|
|
status TEXT NOT NULL CHECK(status IN ('confirmed', 'cancelled', 'completed', 'no_show')),
|
|
check_in_time TEXT,
|
|
check_out_time TEXT,
|
|
notes TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id),
|
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id)
|
|
)
|
|
`)
|
|
|
|
// Recurring bookings
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS recurring_bookings (
|
|
id TEXT PRIMARY KEY,
|
|
workspace_id TEXT NOT NULL,
|
|
user_id TEXT NOT NULL,
|
|
employee_id TEXT NOT NULL,
|
|
start_date TEXT NOT NULL,
|
|
end_date TEXT NOT NULL,
|
|
time_start TEXT NOT NULL,
|
|
time_end TEXT NOT NULL,
|
|
days_of_week TEXT NOT NULL, -- JSON array of days [1,2,3,4,5] for Mon-Fri
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id),
|
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id)
|
|
)
|
|
`)
|
|
|
|
// Booking rules and restrictions
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS booking_rules (
|
|
id TEXT PRIMARY KEY,
|
|
workspace_type TEXT,
|
|
max_duration_hours INTEGER,
|
|
max_advance_days INTEGER,
|
|
min_advance_hours INTEGER,
|
|
max_bookings_per_user_per_day INTEGER,
|
|
max_bookings_per_user_per_week INTEGER,
|
|
auto_release_minutes INTEGER, -- Auto-release if not checked in
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
)
|
|
`)
|
|
|
|
// Analytics data
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS workspace_analytics (
|
|
id TEXT PRIMARY KEY,
|
|
workspace_id TEXT NOT NULL,
|
|
date TEXT NOT NULL,
|
|
total_bookings INTEGER DEFAULT 0,
|
|
total_hours_booked REAL DEFAULT 0,
|
|
utilization_rate REAL DEFAULT 0,
|
|
no_show_count INTEGER DEFAULT 0,
|
|
unique_users INTEGER DEFAULT 0,
|
|
peak_hour INTEGER,
|
|
FOREIGN KEY (workspace_id) REFERENCES workspaces(id),
|
|
UNIQUE(workspace_id, date)
|
|
)
|
|
`)
|
|
|
|
// Floor plans
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS floor_plans (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
floor TEXT NOT NULL,
|
|
building TEXT,
|
|
image_url TEXT,
|
|
svg_data TEXT, -- SVG data for interactive floor plan
|
|
width INTEGER,
|
|
height INTEGER,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
)
|
|
`)
|
|
|
|
// Organizational Structure Tables
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS organizational_units (
|
|
id TEXT PRIMARY KEY,
|
|
code TEXT NOT NULL UNIQUE,
|
|
name TEXT NOT NULL,
|
|
type TEXT NOT NULL CHECK(type IN ('direktion', 'abteilung', 'dezernat', 'sachgebiet', 'teildezernat', 'fuehrungsstelle', 'stabsstelle', 'sondereinheit')),
|
|
level INTEGER NOT NULL,
|
|
parent_id TEXT,
|
|
position_x INTEGER,
|
|
position_y INTEGER,
|
|
color TEXT,
|
|
order_index INTEGER DEFAULT 0,
|
|
description TEXT,
|
|
has_fuehrungsstelle INTEGER DEFAULT 0,
|
|
fuehrungsstelle_name TEXT,
|
|
is_active INTEGER DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (parent_id) REFERENCES organizational_units(id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_org_units_parent ON organizational_units(parent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_org_units_type ON organizational_units(type);
|
|
CREATE INDEX IF NOT EXISTS idx_org_units_level ON organizational_units(level);
|
|
`)
|
|
|
|
// Import rules for organization parsing (persist admin overrides)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS organization_import_rules (
|
|
id TEXT PRIMARY KEY,
|
|
target_code TEXT UNIQUE NOT NULL,
|
|
parent_code TEXT,
|
|
type_override TEXT,
|
|
name_override TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_org_rules_target ON organization_import_rules(target_code);
|
|
`)
|
|
|
|
// Employee Unit Assignments
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS employee_unit_assignments (
|
|
id TEXT PRIMARY KEY,
|
|
employee_id TEXT NOT NULL,
|
|
unit_id TEXT NOT NULL,
|
|
role TEXT NOT NULL CHECK(role IN ('leiter', 'stellvertreter', 'mitarbeiter', 'beauftragter')),
|
|
start_date TEXT NOT NULL,
|
|
end_date TEXT,
|
|
is_primary INTEGER DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unit_id) REFERENCES organizational_units(id) ON DELETE CASCADE,
|
|
UNIQUE(employee_id, unit_id, role)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_emp_units_employee ON employee_unit_assignments(employee_id);
|
|
CREATE INDEX IF NOT EXISTS idx_emp_units_unit ON employee_unit_assignments(unit_id);
|
|
`)
|
|
|
|
// Special Positions (Personalrat, Beauftragte, etc.)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS special_positions (
|
|
id TEXT PRIMARY KEY,
|
|
employee_id TEXT NOT NULL,
|
|
position_type TEXT NOT NULL CHECK(position_type IN ('personalrat', 'schwerbehindertenvertretung', 'datenschutzbeauftragter', 'gleichstellungsbeauftragter', 'inklusionsbeauftragter', 'informationssicherheitsbeauftragter', 'geheimschutzbeauftragter', 'extremismusbeauftragter')),
|
|
unit_id TEXT,
|
|
start_date TEXT NOT NULL,
|
|
end_date TEXT,
|
|
is_active INTEGER DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (unit_id) REFERENCES organizational_units(id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_special_pos_employee ON special_positions(employee_id);
|
|
CREATE INDEX IF NOT EXISTS idx_special_pos_type ON special_positions(position_type);
|
|
`)
|
|
|
|
// Deputy Assignments (Vertretungen)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS deputy_assignments (
|
|
id TEXT PRIMARY KEY,
|
|
principal_id TEXT NOT NULL,
|
|
deputy_id TEXT NOT NULL,
|
|
unit_id TEXT,
|
|
valid_from TEXT NOT NULL,
|
|
valid_until TEXT NOT NULL,
|
|
reason TEXT,
|
|
can_delegate INTEGER DEFAULT 1,
|
|
created_by TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
FOREIGN KEY (principal_id) REFERENCES employees(id),
|
|
FOREIGN KEY (deputy_id) REFERENCES employees(id),
|
|
FOREIGN KEY (unit_id) REFERENCES organizational_units(id),
|
|
FOREIGN KEY (created_by) REFERENCES users(id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_deputy_principal ON deputy_assignments(principal_id);
|
|
CREATE INDEX IF NOT EXISTS idx_deputy_deputy ON deputy_assignments(deputy_id);
|
|
CREATE INDEX IF NOT EXISTS idx_deputy_dates ON deputy_assignments(valid_from, valid_until);
|
|
`)
|
|
|
|
// Deputy Delegations (Vertretungs-Weitergaben)
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS deputy_delegations (
|
|
id TEXT PRIMARY KEY,
|
|
original_assignment_id TEXT NOT NULL,
|
|
from_deputy_id TEXT NOT NULL,
|
|
to_deputy_id TEXT NOT NULL,
|
|
reason TEXT,
|
|
delegated_at TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (original_assignment_id) REFERENCES deputy_assignments(id),
|
|
FOREIGN KEY (from_deputy_id) REFERENCES employees(id),
|
|
FOREIGN KEY (to_deputy_id) REFERENCES employees(id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_delegation_assignment ON deputy_delegations(original_assignment_id);
|
|
CREATE INDEX IF NOT EXISTS idx_delegation_from ON deputy_delegations(from_deputy_id);
|
|
CREATE INDEX IF NOT EXISTS idx_delegation_to ON deputy_delegations(to_deputy_id);
|
|
`)
|
|
|
|
// Audit Log für Änderungsverfolgung
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|
id TEXT PRIMARY KEY,
|
|
entity_type TEXT NOT NULL,
|
|
entity_id TEXT NOT NULL,
|
|
action TEXT NOT NULL CHECK(action IN ('create', 'update', 'delete')),
|
|
user_id TEXT NOT NULL,
|
|
changes TEXT, -- JSON mit Änderungen
|
|
timestamp TEXT NOT NULL,
|
|
ip_address TEXT,
|
|
user_agent TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_log(entity_type, entity_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
|
|
`)
|
|
|
|
// Reminder-System
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS reminders (
|
|
id TEXT PRIMARY KEY,
|
|
profile_id TEXT NOT NULL,
|
|
type TEXT NOT NULL CHECK(type IN ('annual_update', 'overdue', 'custom')),
|
|
message TEXT,
|
|
sent_at TEXT,
|
|
acknowledged_at TEXT,
|
|
created_at TEXT NOT NULL,
|
|
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_reminders_profile ON reminders(profile_id);
|
|
CREATE INDEX IF NOT EXISTS idx_reminders_sent ON reminders(sent_at);
|
|
`)
|
|
|
|
// Kontrollierte Vokabulare/Tags
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS controlled_vocabulary (
|
|
id TEXT PRIMARY KEY,
|
|
category TEXT NOT NULL,
|
|
value TEXT NOT NULL,
|
|
description TEXT,
|
|
is_active INTEGER DEFAULT 1,
|
|
created_at TEXT NOT NULL,
|
|
UNIQUE(category, value)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_vocab_category ON controlled_vocabulary(category);
|
|
`)
|
|
|
|
// Create indexes for better performance
|
|
db.exec(`
|
|
CREATE INDEX IF NOT EXISTS idx_employees_availability ON employees(availability);
|
|
CREATE INDEX IF NOT EXISTS idx_employees_department ON employees(department);
|
|
CREATE INDEX IF NOT EXISTS idx_employee_skills_employee ON employee_skills(employee_id);
|
|
CREATE INDEX IF NOT EXISTS idx_employee_skills_skill ON employee_skills(skill_id);
|
|
CREATE INDEX IF NOT EXISTS idx_language_skills_employee ON language_skills(employee_id);
|
|
CREATE INDEX IF NOT EXISTS idx_specializations_employee ON specializations(employee_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bookings_workspace ON bookings(workspace_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bookings_user ON bookings(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bookings_start_time ON bookings(start_time);
|
|
CREATE INDEX IF NOT EXISTS idx_bookings_status ON bookings(status);
|
|
CREATE INDEX IF NOT EXISTS idx_workspace_analytics_date ON workspace_analytics(date);
|
|
`)
|
|
}
|