Files
drivetimeplaner/www/fahrschultermin.de/database/schema_pgsql.sql
Hermes Agent 2d6c0ad3aa Split API and frontend, migrate SQLite to PostgreSQL
- api.fahrschultermin.de: full API entry point with CORS + session auth
- fahrschultermin.de: frontend only, no more /api/v1 routing
- Database.php: PostgreSQL support (pgsql driver + lastval())
- All repositories: use Database::lastInsertId() for PG compatibility
- config/app.php: PostgreSQL connection settings + CORS config
- bootstrap.php: pass full $config to Database::configure()
- public/index.php: fetch-patch to redirect /api/v1 to api.fahrschultermin.de
- database/schema_pgsql.sql: full PostgreSQL schema (BIGSERIAL)
- database/import_pgsql.sql: 518 rows migrated from SQLite
2026-05-18 23:50:00 +02:00

166 lines
7.6 KiB
PL/PgSQL

-- PostgreSQL Schema for fahrschultermin.de
-- Run this BEFORE importing data
BEGIN;
-- ── Tenants ───────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS tenants (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
timezone TEXT NOT NULL DEFAULT 'Europe/Berlin',
is_active INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
location_label TEXT DEFAULT '',
latitude DOUBLE PRECISION DEFAULT NULL,
longitude DOUBLE PRECISION DEFAULT NULL,
federal_state TEXT DEFAULT 'BE'
);
-- ── Users ─────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT REFERENCES tenants(id) ON DELETE CASCADE,
role TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
is_active INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
-- ── Instructors ────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS instructors (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
color TEXT NOT NULL,
notes TEXT DEFAULT '',
is_active INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
user_id BIGINT REFERENCES users(id) ON DELETE SET NULL,
pre_start_buffer_minutes INTEGER NOT NULL DEFAULT 0
);
-- ── License Class Templates ───────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS license_class_templates (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
code TEXT NOT NULL,
name TEXT NOT NULL,
is_combination INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE (tenant_id, code)
);
-- ── License Template Requirements ─────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS license_template_requirements (
id BIGSERIAL PRIMARY KEY,
template_id BIGINT NOT NULL REFERENCES license_class_templates(id) ON DELETE CASCADE,
requirement_key TEXT NOT NULL,
label TEXT NOT NULL,
required_units INTEGER NOT NULL,
sort_order INTEGER NOT NULL DEFAULT 0,
phase_label TEXT NOT NULL DEFAULT ''
);
-- ── Lesson Types ──────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS lesson_types (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
name TEXT NOT NULL,
color TEXT NOT NULL,
default_duration INTEGER NOT NULL,
category TEXT NOT NULL,
requirement_key TEXT DEFAULT NULL,
is_billable INTEGER NOT NULL DEFAULT 0,
is_counted INTEGER NOT NULL DEFAULT 0,
is_rest_relevant INTEGER NOT NULL DEFAULT 0,
fixed_duration INTEGER NOT NULL DEFAULT 0,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
system_key TEXT DEFAULT NULL
);
-- ── Lesson Type ↔ Template Visibility ──────────────────────────────────────────
CREATE TABLE IF NOT EXISTS lesson_type_template_visibility (
lesson_type_id BIGINT NOT NULL REFERENCES lesson_types(id) ON DELETE CASCADE,
template_id BIGINT NOT NULL REFERENCES license_class_templates(id) ON DELETE CASCADE,
PRIMARY KEY (lesson_type_id, template_id)
);
-- ── Students ──────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS students (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
template_id BIGINT NOT NULL REFERENCES license_class_templates(id),
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
phone TEXT DEFAULT '',
notes TEXT DEFAULT '',
status TEXT NOT NULL DEFAULT 'active',
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
email TEXT NOT NULL DEFAULT '',
needs_glasses INTEGER NOT NULL DEFAULT 0
);
-- ── Student Requirement Snapshots ──────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS student_requirement_snapshots (
id BIGSERIAL PRIMARY KEY,
student_id BIGINT NOT NULL REFERENCES students(id) ON DELETE CASCADE,
requirement_key TEXT NOT NULL,
label TEXT NOT NULL,
required_units INTEGER NOT NULL,
sort_order INTEGER NOT NULL DEFAULT 0,
prior_completed_units INTEGER NOT NULL DEFAULT 0,
phase_label TEXT NOT NULL DEFAULT ''
);
-- ── Appointments ──────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS appointments (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
student_id BIGINT REFERENCES students(id) ON DELETE SET NULL,
instructor_id BIGINT NOT NULL REFERENCES instructors(id),
lesson_type_id BIGINT NOT NULL REFERENCES lesson_types(id),
title TEXT NOT NULL,
category TEXT NOT NULL,
start_at TIMESTAMPTZ NOT NULL,
end_at TIMESTAMPTZ NOT NULL,
units INTEGER NOT NULL DEFAULT 1,
status TEXT NOT NULL DEFAULT 'planned',
notes TEXT DEFAULT '',
warning_acknowledged INTEGER NOT NULL DEFAULT 0,
created_by BIGINT NOT NULL REFERENCES users(id),
updated_by BIGINT NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
-- ── Appointment Units ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS appointment_units (
id BIGSERIAL PRIMARY KEY,
appointment_id BIGINT NOT NULL REFERENCES appointments(id) ON DELETE CASCADE,
requirement_key TEXT DEFAULT NULL,
label TEXT DEFAULT NULL,
units_counted INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL
);
-- ── Settings ───────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS settings (
id BIGSERIAL PRIMARY KEY,
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
key_name TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE (tenant_id, key_name)
);
COMMIT;