-- +goose Up CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE EXTENSION IF NOT EXISTS pg_stat_statements; CREATE TABLE IF NOT EXISTS tenants ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), slug text NOT NULL UNIQUE, name text NOT NULL, preset text NOT NULL, locale text NOT NULL DEFAULT 'cs', timezone text NOT NULL DEFAULT 'Europe/Prague', plan_code text NOT NULL DEFAULT 'starter', subscription_status text NOT NULL DEFAULT 'trialing', stripe_customer_id text, stripe_subscription_id text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS users ( id uuid PRIMARY KEY, email text NOT NULL, display_name text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS tenant_users ( tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, role text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (tenant_id, user_id) ); CREATE TABLE IF NOT EXISTS locations ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, name text NOT NULL, timezone text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS staff ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, location_id uuid REFERENCES locations(id) ON DELETE SET NULL, display_name text NOT NULL, active boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS services ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, name text NOT NULL, duration_minutes integer NOT NULL, buffer_before_minutes integer NOT NULL DEFAULT 0, buffer_after_minutes integer NOT NULL DEFAULT 0, price_cents integer NOT NULL DEFAULT 0, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS class_templates ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, title text NOT NULL, duration_minutes integer NOT NULL, capacity integer NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS class_sessions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, template_id uuid NOT NULL REFERENCES class_templates(id) ON DELETE CASCADE, location_id uuid REFERENCES locations(id) ON DELETE SET NULL, starts_at timestamptz NOT NULL, ends_at timestamptz NOT NULL, capacity integer NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS availability_rules ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, staff_id uuid REFERENCES staff(id) ON DELETE CASCADE, day_of_week integer NOT NULL, starts_local time NOT NULL, ends_local time NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS availability_exceptions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, staff_id uuid REFERENCES staff(id) ON DELETE CASCADE, starts_at timestamptz NOT NULL, ends_at timestamptz NOT NULL, kind text NOT NULL, reason text, created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS bookings ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, service_id uuid REFERENCES services(id) ON DELETE SET NULL, class_session_id uuid REFERENCES class_sessions(id) ON DELETE SET NULL, staff_id uuid REFERENCES staff(id) ON DELETE SET NULL, location_id uuid REFERENCES locations(id) ON DELETE SET NULL, booking_mode text NOT NULL, customer_name text NOT NULL, customer_email text NOT NULL, starts_at timestamptz NOT NULL, ends_at timestamptz NOT NULL, status text NOT NULL, reference text NOT NULL UNIQUE, notes text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS waitlist_entries ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, class_session_id uuid NOT NULL REFERENCES class_sessions(id) ON DELETE CASCADE, customer_name text NOT NULL, customer_email text NOT NULL, position integer NOT NULL, status text NOT NULL DEFAULT 'pending', created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS reminder_jobs ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, booking_id uuid REFERENCES bookings(id) ON DELETE CASCADE, channel text NOT NULL, scheduled_for timestamptz NOT NULL, dispatched_at timestamptz, status text NOT NULL DEFAULT 'pending', created_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS subscription_events ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id uuid NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, stripe_event_id text NOT NULL UNIQUE, event_type text NOT NULL, payload jsonb NOT NULL, processed_at timestamptz, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_bookings_tenant_time ON bookings (tenant_id, starts_at DESC); CREATE INDEX IF NOT EXISTS idx_sessions_tenant_time ON class_sessions (tenant_id, starts_at DESC); CREATE INDEX IF NOT EXISTS idx_reminder_jobs_pending ON reminder_jobs (scheduled_for) WHERE status = 'pending'; -- +goose Down DROP TABLE IF EXISTS subscription_events; DROP TABLE IF EXISTS reminder_jobs; DROP TABLE IF EXISTS waitlist_entries; DROP TABLE IF EXISTS bookings; DROP TABLE IF EXISTS availability_exceptions; DROP TABLE IF EXISTS availability_rules; DROP TABLE IF EXISTS class_sessions; DROP TABLE IF EXISTS class_templates; DROP TABLE IF EXISTS services; DROP TABLE IF EXISTS staff; DROP TABLE IF EXISTS locations; DROP TABLE IF EXISTS tenant_users; DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS tenants;