mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
191 lines
7.0 KiB
SQL
191 lines
7.0 KiB
SQL
-- Ticket system migration for match ticketing
|
|
-- Migration: 20250109000001_add_ticket_system.up.sql
|
|
|
|
-- Enable UUID extension if not exists
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
|
|
-- Ticket types for different pricing tiers
|
|
CREATE TABLE ticket_types (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
deleted_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
price_cents BIGINT NOT NULL,
|
|
currency VARCHAR(10) DEFAULT 'CZK',
|
|
color VARCHAR(20) DEFAULT 'primary', -- primary, secondary, success, warning, danger
|
|
display_order INTEGER DEFAULT 0,
|
|
active BOOLEAN DEFAULT true,
|
|
|
|
-- Constraints and limits
|
|
max_tickets_per_order INTEGER DEFAULT 10,
|
|
sale_start_time TIMESTAMP WITH TIME ZONE,
|
|
sale_end_time TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- Access control (optional)
|
|
requires_membership BOOLEAN DEFAULT false,
|
|
min_age INTEGER,
|
|
|
|
INDEX idx_ticket_types_active (active),
|
|
INDEX idx_ticket_types_order (display_order)
|
|
);
|
|
|
|
-- Ticket campaigns for specific matches or events
|
|
CREATE TABLE ticket_campaigns (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
deleted_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Link to match (can be null for non-match events)
|
|
external_match_id VARCHAR(255), -- FACR match ID
|
|
competition_code VARCHAR(50),
|
|
match_date_time TIMESTAMP WITH TIME ZONE,
|
|
home_team VARCHAR(255),
|
|
away_team VARCHAR(255),
|
|
venue VARCHAR(255),
|
|
|
|
-- Campaign settings
|
|
active BOOLEAN DEFAULT true,
|
|
sale_start_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
sale_end_time TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
max_total_tickets INTEGER, -- Overall capacity limit
|
|
|
|
INDEX idx_ticket_campaigns_active (active),
|
|
INDEX idx_ticket_campaigns_match (external_match_id),
|
|
INDEX idx_ticket_campaigns_dates (sale_start_time, sale_end_time)
|
|
);
|
|
|
|
-- Link ticket types to campaigns (many-to-many with campaign-specific overrides)
|
|
CREATE TABLE campaign_ticket_types (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
campaign_id UUID NOT NULL REFERENCES ticket_campaigns(id) ON DELETE CASCADE,
|
|
ticket_type_id UUID NOT NULL REFERENCES ticket_types(id) ON DELETE CASCADE,
|
|
|
|
-- Campaign-specific overrides
|
|
price_cents BIGINT, -- Override default price if set
|
|
max_quantity INTEGER, -- Campaign-specific quantity limit
|
|
|
|
UNIQUE(campaign_id, ticket_type_id),
|
|
INDEX idx_campaign_ticket_types_campaign (campaign_id),
|
|
INDEX idx_campaign_ticket_types_type (ticket_type_id)
|
|
);
|
|
|
|
-- Ticket instances (actual sold tickets)
|
|
CREATE TABLE tickets (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
order_id UUID, -- Link to e-shop order when purchased
|
|
campaign_id UUID NOT NULL REFERENCES ticket_campaigns(id),
|
|
ticket_type_id UUID NOT NULL REFERENCES ticket_types(id),
|
|
|
|
-- Ticket holder information
|
|
holder_name VARCHAR(255) NOT NULL,
|
|
holder_email VARCHAR(255) NOT NULL,
|
|
holder_phone VARCHAR(50),
|
|
|
|
-- Ticket details
|
|
quantity INTEGER NOT NULL DEFAULT 1,
|
|
unit_price_cents BIGINT NOT NULL,
|
|
total_price_cents BIGINT NOT NULL,
|
|
currency VARCHAR(10) DEFAULT 'CZK',
|
|
|
|
-- Status tracking
|
|
status VARCHAR(20) DEFAULT 'reserved', -- reserved, paid, cancelled, used
|
|
barcode VARCHAR(255) UNIQUE, -- Unique barcode/QR code
|
|
qr_code_data TEXT, -- JSON with QR code data
|
|
|
|
-- Usage tracking
|
|
used_at TIMESTAMP WITH TIME ZONE,
|
|
used_by VARCHAR(255), -- Who validated the ticket
|
|
|
|
INDEX idx_tickets_order (order_id),
|
|
INDEX idx_tickets_campaign (campaign_id),
|
|
INDEX idx_tickets_status (status),
|
|
INDEX idx_tickets_barcode (barcode),
|
|
INDEX idx_tickets_holder_email (holder_email)
|
|
);
|
|
|
|
-- Ticket availability tracking per campaign/type
|
|
CREATE TABLE ticket_availability (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
campaign_id UUID NOT NULL REFERENCES ticket_campaigns(id) ON DELETE CASCADE,
|
|
ticket_type_id UUID NOT NULL REFERENCES ticket_types(id) ON DELETE CASCADE,
|
|
|
|
total_capacity INTEGER NOT NULL DEFAULT 0,
|
|
sold_quantity INTEGER NOT NULL DEFAULT 0,
|
|
reserved_quantity INTEGER NOT NULL DEFAULT 0,
|
|
available_quantity INTEGER GENERATED ALWAYS AS (total_capacity - sold_quantity - reserved_quantity) STORED,
|
|
|
|
UNIQUE(campaign_id, ticket_type_id),
|
|
INDEX idx_ticket_availability_campaign (campaign_id),
|
|
INDEX idx_ticket_availability_type (ticket_type_id)
|
|
);
|
|
|
|
-- Add ticket-related fields to existing e-shop orders
|
|
ALTER TABLE eshop_orders
|
|
ADD COLUMN ticket_order BOOLEAN DEFAULT false,
|
|
ADD COLUMN ticket_campaign_id UUID REFERENCES ticket_campaigns(id);
|
|
|
|
-- Create indexes for new order fields
|
|
CREATE INDEX idx_eshop_orders_ticket_order ON eshop_orders(ticket_order);
|
|
CREATE INDEX idx_eshop_orders_ticket_campaign ON eshop_orders(ticket_campaign_id);
|
|
|
|
-- Insert default ticket types
|
|
INSERT INTO ticket_types (name, description, price_cents, color, display_order) VALUES
|
|
('Dospělý', 'Vstupenka pro dospělého', 15000, 'primary', 1),
|
|
('Dítě do 15 let', 'Vstupenka pro děti do 15 let', 5000, 'success', 2),
|
|
('Student', 'Vstupenka pro studenty (platí ISIC)', 8000, 'warning', 3),
|
|
('Senior', 'Vstupenka pro seniory nad 65 let', 8000, 'secondary', 4),
|
|
('VIP', 'VIP vstupenka s občerstvením', 50000, 'danger', 5);
|
|
|
|
-- Create view for available tickets with campaign info
|
|
CREATE VIEW available_tickets_view AS
|
|
SELECT
|
|
c.id as campaign_id,
|
|
c.title as campaign_title,
|
|
c.description as campaign_description,
|
|
c.external_match_id,
|
|
c.competition_code,
|
|
c.match_date_time,
|
|
c.home_team,
|
|
c.away_team,
|
|
c.venue,
|
|
c.sale_start_time,
|
|
c.sale_end_time,
|
|
tt.id as ticket_type_id,
|
|
tt.name as ticket_type_name,
|
|
tt.description as ticket_type_description,
|
|
COALESCE(ctt.price_cents, tt.price_cents) as price_cents,
|
|
COALESCE(ctt.max_quantity, tt.max_tickets_per_order) as max_per_order,
|
|
tt.color,
|
|
ta.available_quantity,
|
|
ta.total_capacity,
|
|
CASE
|
|
WHEN NOW() < c.sale_start_time THEN 'upcoming'
|
|
WHEN NOW() > c.sale_end_time THEN 'ended'
|
|
WHEN ta.available_quantity <= 0 THEN 'sold_out'
|
|
ELSE 'available'
|
|
END as sale_status
|
|
FROM ticket_campaigns c
|
|
JOIN campaign_ticket_types ctt ON c.id = ctt.campaign_id
|
|
JOIN ticket_types tt ON ctt.ticket_type_id = tt.id
|
|
LEFT JOIN ticket_availability ta ON c.id = ta.campaign_id AND tt.id = ta.ticket_type_id
|
|
WHERE c.active = true
|
|
AND tt.active = true
|
|
AND c.deleted_at IS NULL
|
|
AND tt.deleted_at IS NULL;
|