Files
MyClub/database/migrations/20260109000001_create_facility_management_tables.up.sql
T
Tomas Dvorak dfc079288f hot fix #1
2026-01-26 08:13:18 +01:00

293 lines
12 KiB
SQL

-- Create facility management tables
-- Migration: 20260109000001_create_facility_management_tables.up.sql
-- Facilities table
CREATE TABLE IF NOT EXISTS facilities (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
name VARCHAR(255) NOT NULL,
description TEXT,
type VARCHAR(20) NOT NULL CHECK (type IN ('field', 'gym', 'locker', 'classroom', 'storage', 'other')),
status VARCHAR(20) NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'maintenance', 'closed')),
capacity INTEGER,
area DECIMAL(10,2),
location VARCHAR(255),
is_indoor BOOLEAN DEFAULT true,
is_outdoor BOOLEAN DEFAULT false,
image_url VARCHAR(500),
-- Booking settings
requires_approval BOOLEAN DEFAULT false,
min_booking_duration INTEGER DEFAULT 30,
max_booking_duration INTEGER DEFAULT 240,
booking_advance_days INTEGER DEFAULT 30,
-- Pricing
price_per_hour DECIMAL(10,2) DEFAULT 0.00,
CONSTRAINT facilities_name_unique UNIQUE (name, deleted_at)
);
-- Facility availability rules
CREATE TABLE IF NOT EXISTS facility_availability_rules (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
day_of_week INTEGER NOT NULL CHECK (day_of_week BETWEEN 0 AND 6),
start_time VARCHAR(5) NOT NULL CHECK (start_time ~ '^[0-2][0-9]:[0-5][0-9]$'),
end_time VARCHAR(5) NOT NULL CHECK (end_time ~ '^[0-2][0-9]:[0-5][0-9]$'),
is_available BOOLEAN DEFAULT true,
start_date DATE,
end_date DATE
);
-- Facility bookings
CREATE TABLE IF NOT EXISTS facility_bookings (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id),
title VARCHAR(255) NOT NULL,
description TEXT,
start_time TIMESTAMP WITH TIME ZONE NOT NULL,
end_time TIMESTAMP WITH TIME ZONE NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'cancelled', 'completed', 'noshow')),
-- Pricing
total_price DECIMAL(10,2) DEFAULT 0.00,
payment_status VARCHAR(20) DEFAULT 'pending',
-- Attendance tracking
actual_start_time TIMESTAMP WITH TIME ZONE,
actual_end_time TIMESTAMP WITH TIME ZONE,
attendees_count INTEGER DEFAULT 0,
-- Notes
internal_notes TEXT,
public_notes TEXT,
-- Cancellation
cancelled_at TIMESTAMP WITH TIME ZONE,
cancelled_by INTEGER REFERENCES users(id),
cancel_reason TEXT,
CONSTRAINT bookings_no_overlap EXCLUDE (facility_id WITH =, tsrange(start_time, end_time) WITH &&) WHERE (deleted_at IS NULL AND status NOT IN ('cancelled', 'noshow'))
);
-- Facility equipment
CREATE TABLE IF NOT EXISTS facility_equipment (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
category VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'available' CHECK (status IN ('available', 'in_use', 'maintenance', 'damaged', 'lost', 'retired')),
quantity INTEGER NOT NULL DEFAULT 1,
available INTEGER NOT NULL DEFAULT 1,
-- Purchase info
purchase_date DATE,
purchase_price DECIMAL(10,2),
supplier VARCHAR(255),
serial_number VARCHAR(255),
warranty_expiry DATE,
-- Maintenance
last_maintenance_date DATE,
next_maintenance_date DATE,
-- Location tracking
current_location VARCHAR(255),
image_url VARCHAR(500),
-- Usage tracking
usage_count INTEGER DEFAULT 0,
last_used_at TIMESTAMP WITH TIME ZONE,
CONSTRAINT equipment_name_facility_unique UNIQUE (name, facility_id, deleted_at)
);
-- Facility maintenance
CREATE TABLE IF NOT EXISTS facility_maintenance (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
type VARCHAR(20) NOT NULL CHECK (type IN ('routine', 'repair', 'inspection', 'upgrade')),
title VARCHAR(255) NOT NULL,
description TEXT,
-- Scheduling
scheduled_date TIMESTAMP WITH TIME ZONE,
estimated_duration INTEGER, -- minutes
actual_duration INTEGER, -- minutes
-- Status
status VARCHAR(20) DEFAULT 'scheduled',
started_at TIMESTAMP WITH TIME ZONE,
completed_at TIMESTAMP WITH TIME ZONE,
-- Cost
estimated_cost DECIMAL(10,2),
actual_cost DECIMAL(10,2),
-- Personnel
assigned_to VARCHAR(255),
performed_by VARCHAR(255),
-- Impact
is_facility_unavailable BOOLEAN DEFAULT true,
-- Notes
internal_notes TEXT,
public_notes TEXT,
-- Related equipment (JSON array)
equipment_affected TEXT
);
-- Weather conditions for outdoor facilities
CREATE TABLE IF NOT EXISTS weather_conditions (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
date_time TIMESTAMP WITH TIME ZONE NOT NULL,
temperature DECIMAL(5,2), -- Celsius
humidity DECIMAL(5,2), -- Percentage
precipitation DECIMAL(8,2), -- mm
wind_speed DECIMAL(5,2), -- km/h
wind_direction INTEGER, -- Degrees
weather_code VARCHAR(10), -- OpenWeatherMap condition code
description VARCHAR(255),
is_suitable BOOLEAN DEFAULT false,
recommendations TEXT,
CONSTRAINT weather_facility_datetime_unique UNIQUE (facility_id, date_time)
);
-- Facility booking templates
CREATE TABLE IF NOT EXISTS facility_booking_templates (
id SERIAL PRIMARY KEY,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP WITH TIME ZONE,
facility_id INTEGER NOT NULL REFERENCES facilities(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
-- Default booking settings
duration INTEGER, -- minutes
price_per_hour DECIMAL(10,2),
requires_approval BOOLEAN DEFAULT false,
-- Recurrence pattern
is_recurring BOOLEAN DEFAULT false,
recurrence_pattern TEXT, -- JSON
-- Default settings
default_title VARCHAR(255),
default_description TEXT,
default_attendees INTEGER DEFAULT 1,
is_active BOOLEAN DEFAULT true,
CONSTRAINT template_name_facility_unique UNIQUE (name, facility_id, deleted_at)
);
-- Indexes for performance
CREATE INDEX IF NOT EXISTS idx_facilities_type ON facilities(type);
CREATE INDEX IF NOT EXISTS idx_facilities_status ON facilities(status);
CREATE INDEX IF NOT EXISTS idx_facilities_is_outdoor ON facilities(is_outdoor);
CREATE INDEX IF NOT EXISTS idx_facility_bookings_facility_id ON facility_bookings(facility_id);
CREATE INDEX IF NOT EXISTS idx_facility_bookings_user_id ON facility_bookings(user_id);
CREATE INDEX IF NOT EXISTS idx_facility_bookings_start_time ON facility_bookings(start_time);
CREATE INDEX IF NOT EXISTS idx_facility_bookings_end_time ON facility_bookings(end_time);
CREATE INDEX IF NOT EXISTS idx_facility_bookings_status ON facility_bookings(status);
CREATE INDEX IF NOT EXISTS idx_facility_bookings_time_range ON facility_bookings USING gist (tsrange(start_time, end_time));
CREATE INDEX IF NOT EXISTS idx_facility_equipment_facility_id ON facility_equipment(facility_id);
CREATE INDEX IF NOT EXISTS idx_facility_equipment_status ON facility_equipment(status);
CREATE INDEX IF NOT EXISTS idx_facility_equipment_category ON facility_equipment(category);
CREATE INDEX IF NOT EXISTS idx_facility_maintenance_facility_id ON facility_maintenance(facility_id);
CREATE INDEX IF NOT EXISTS idx_facility_maintenance_scheduled_date ON facility_maintenance(scheduled_date);
CREATE INDEX IF NOT EXISTS idx_facility_maintenance_status ON facility_maintenance(status);
CREATE INDEX IF NOT EXISTS idx_weather_conditions_facility_id ON weather_conditions(facility_id);
CREATE INDEX IF NOT EXISTS idx_weather_conditions_date_time ON weather_conditions(date_time);
CREATE INDEX IF NOT EXISTS idx_weather_conditions_is_suitable ON weather_conditions(is_suitable);
CREATE INDEX IF NOT EXISTS idx_facility_booking_templates_facility_id ON facility_booking_templates(facility_id);
CREATE INDEX IF NOT EXISTS idx_facility_booking_templates_is_active ON facility_booking_templates(is_active);
-- Insert sample data
INSERT INTO facilities (name, description, type, status, capacity, area, location, is_indoor, is_outdoor, price_per_hour, min_booking_duration, max_booking_duration, booking_advance_days) VALUES
('Hlavní hřiště', 'Plnoformátové fotbalové hřiště s přírodním trávníkem', 'field', 'active', 22, 7200.00, 'Hlavní sportovní areál', false, true, 500.00, 60, 180, 14),
('Tréninkové hřiště č. 1', 'Menší tréninkové hřiště s umělým trávníkem', 'field', 'active', 16, 4800.00, 'Tréninkový areál - sever', false, true, 300.00, 30, 120, 7),
('Posilovna', 'Plně vybavená posilovna pro hráče', 'gym', 'active', 20, 150.00, 'Hlavní budova - 1. patro', true, false, 100.00, 30, 90, 3),
('Šatna A', 'Šatna pro domácí tým', 'locker', 'active', 25, 80.00, 'Hlavní budova - přízemí', true, false, 0.00, 15, 60, 1),
('Zasedací místnost', 'Místnost pro týmové porady a prezentace', 'classroom', 'active', 30, 60.00, 'Hlavní budova - 2. patro', true, false, 50.00, 30, 120, 7),
('Sklad vybavení', 'Sklad pro tréninkové vybavení', 'storage', 'active', 5, 40.00, 'Hlavní budova - suterén', true, false, 0.00, 15, 30, 1);
-- Insert availability rules for main field (available Mon-Fri 16:00-22:00, Sat-Sun 08:00-22:00)
INSERT INTO facility_availability_rules (facility_id, day_of_week, start_time, end_time, is_available)
SELECT f.id, d.day_of_week, d.start_time, d.end_time, true
FROM facilities f,
(VALUES
(1, 1, '16:00', '22:00'), -- Monday
(1, 2, '16:00', '22:00'), -- Tuesday
(1, 3, '16:00', '22:00'), -- Wednesday
(1, 4, '16:00', '22:00'), -- Thursday
(1, 5, '16:00', '22:00'), -- Friday
(1, 6, '08:00', '22:00'), -- Saturday
(1, 0, '08:00', '22:00') -- Sunday
) AS d(day_of_week, start_time, end_time)
WHERE f.name = 'Hlavní hřiště';
-- Insert sample equipment
INSERT INTO facility_equipment (facility_id, name, description, category, quantity, available, purchase_date, purchase_price)
SELECT f.id, e.name, e.description, e.category, e.quantity, e.quantity, e.purchase_date, e.purchase_price
FROM facilities f,
(VALUES
(1, 'Míče velikosti 5', 'Plnoformátové fotbalové míče', 'balls', 20, 20, '2024-01-15', 800.00),
(1, 'Tréninkové kužely', 'Plastové kužele pro cvičení', 'cones', 50, 50, '2024-01-15', 500.00),
(1, 'Branky', 'Přenosné branky pro trénink', 'goals', 4, 4, '2024-02-01', 2000.00),
(3, 'Činky', 'Sada činek různých vah', 'weights', 10, 10, '2024-01-20', 5000.00),
(3, 'Posilovací lavice', 'Profesionální lavice na bench press', 'benches', 3, 3, '2024-01-20', 3000.00)
) AS e(name, description, category, quantity, available, purchase_date, purchase_price)
WHERE f.name = 'Hlavní hřiště' AND e.category IN ('balls', 'cones', 'goals')
UNION ALL
SELECT f.id, e.name, e.description, e.category, e.quantity, e.quantity, e.purchase_date, e.purchase_price
FROM facilities f,
(VALUES
('Činky', 'Sada činek různých vah', 'weights', 10, 10, '2024-01-20', 5000.00),
('Posilovací lavice', 'Profesionální lavice na bench press', 'benches', 3, 3, '2024-01-20', 3000.00)
) AS e(name, description, category, quantity, available, purchase_date, purchase_price)
WHERE f.name = 'Posilovna' AND e.category IN ('weights', 'benches');