Tables fixed: - rooms → added updated_at - housekeeping_tasks → added updated_at - channels → added created_at + updated_at Trigger function set_updated_at() attached to all 6 tables (hotels, users, rooms, bookings, housekeeping_tasks, channels) — updated_at now auto-sets on every UPDATE via BEFORE UPDATE trigger. Indexes added for archiving / range queries: - bookings(hotel_id, created_at), bookings(hotel_id, updated_at) - housekeeping_tasks(hotel_id, created_at) - rooms(hotel_id, updated_at) - channels(hotel_id, updated_at) Also patched 001_schema.sql so fresh installs start correct. Migration runner is idempotent — skips already applied files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
6.3 KiB
SQL
114 lines
6.3 KiB
SQL
-- HotelSync Database Schema
|
|
-- Migration 001 — Initial Schema
|
|
|
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
|
|
-- ── Hotels ─────────────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS hotels (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(100) NOT NULL UNIQUE,
|
|
plan VARCHAR(20) NOT NULL DEFAULT 'starter'
|
|
CHECK (plan IN ('starter', 'pro', 'enterprise')),
|
|
timezone VARCHAR(50) NOT NULL DEFAULT 'Europe/Moscow',
|
|
currency VARCHAR(3) NOT NULL DEFAULT 'RUB',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ── Users ──────────────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
hotel_id UUID REFERENCES hotels(id) ON DELETE SET NULL,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
role VARCHAR(20) NOT NULL DEFAULT 'manager'
|
|
CHECK (role IN ('manager', 'housekeeper', 'super_admin')),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ── Rooms ──────────────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS rooms (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
hotel_id UUID NOT NULL REFERENCES hotels(id) ON DELETE CASCADE,
|
|
number VARCHAR(20) NOT NULL,
|
|
type VARCHAR(50) NOT NULL,
|
|
floor INTEGER NOT NULL DEFAULT 1,
|
|
capacity INTEGER NOT NULL DEFAULT 2,
|
|
price_per_night DECIMAL(10,2) NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'clean'
|
|
CHECK (status IN ('clean', 'dirty', 'maintenance', 'out_of_order')),
|
|
amenities TEXT[] NOT NULL DEFAULT '{}',
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(hotel_id, number)
|
|
);
|
|
|
|
-- ── Bookings ───────────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS bookings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
hotel_id UUID NOT NULL REFERENCES hotels(id) ON DELETE CASCADE,
|
|
room_id UUID NOT NULL REFERENCES rooms(id),
|
|
guest_name VARCHAR(255) NOT NULL,
|
|
guest_email VARCHAR(255),
|
|
guest_phone VARCHAR(50),
|
|
check_in DATE NOT NULL,
|
|
check_out DATE NOT NULL,
|
|
adults INTEGER NOT NULL DEFAULT 1,
|
|
children INTEGER NOT NULL DEFAULT 0,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'confirmed'
|
|
CHECK (status IN ('confirmed', 'checked_in', 'checked_out', 'cancelled', 'no_show')),
|
|
source VARCHAR(20) NOT NULL DEFAULT 'direct'
|
|
CHECK (source IN ('direct', 'booking_com', 'airbnb', 'expedia', 'vrbo')),
|
|
total_amount DECIMAL(10,2),
|
|
notes TEXT,
|
|
external_id VARCHAR(255),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ── Housekeeping Tasks ─────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS housekeeping_tasks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
hotel_id UUID NOT NULL REFERENCES hotels(id) ON DELETE CASCADE,
|
|
room_id UUID REFERENCES rooms(id) ON DELETE SET NULL,
|
|
type VARCHAR(30) NOT NULL DEFAULT 'cleaning'
|
|
CHECK (type IN ('cleaning', 'turnover', 'inspection', 'maintenance', 'amenities')),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending'
|
|
CHECK (status IN ('pending', 'in_progress', 'done', 'skipped')),
|
|
priority VARCHAR(10) NOT NULL DEFAULT 'medium'
|
|
CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
|
|
assignee_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
notes TEXT,
|
|
due_date DATE,
|
|
completed_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ── Channels ───────────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS channels (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
hotel_id UUID NOT NULL REFERENCES hotels(id) ON DELETE CASCADE,
|
|
name VARCHAR(50) NOT NULL
|
|
CHECK (name IN ('booking_com', 'airbnb', 'expedia', 'vrbo')),
|
|
enabled BOOLEAN NOT NULL DEFAULT false,
|
|
api_key_encrypted TEXT,
|
|
hotel_external_id VARCHAR(255),
|
|
last_synced_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(hotel_id, name)
|
|
);
|
|
|
|
-- ── Indexes ────────────────────────────────────────────────────────────────
|
|
CREATE INDEX IF NOT EXISTS idx_bookings_hotel_dates ON bookings(hotel_id, check_in, check_out);
|
|
CREATE INDEX IF NOT EXISTS idx_bookings_room ON bookings(room_id);
|
|
CREATE INDEX IF NOT EXISTS idx_rooms_hotel ON rooms(hotel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_tasks_hotel ON housekeeping_tasks(hotel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
|
CREATE INDEX IF NOT EXISTS idx_users_hotel ON users(hotel_id);
|