-- Add printed tickets support -- This migration adds support for printed tickets with barcodes -- Create printed_tickets table CREATE TABLE printed_tickets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), barcode_number TEXT NOT NULL UNIQUE, event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE, ticket_type_id UUID NOT NULL REFERENCES ticket_types(id) ON DELETE CASCADE, organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, status TEXT NOT NULL DEFAULT 'valid' CHECK (status IN ('valid', 'used', 'invalid')), batch_number TEXT, notes TEXT, issued_by UUID REFERENCES users(id), checked_in_at TIMESTAMP WITH TIME ZONE, scanned_by UUID REFERENCES users(id), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Create scan_attempts table for audit logging CREATE TABLE scan_attempts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), barcode_number TEXT NOT NULL, event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE, scanned_by UUID REFERENCES users(id), result TEXT NOT NULL CHECK (result IN ('SUCCESS', 'INVALID_BARCODE', 'WRONG_EVENT', 'ALREADY_USED', 'NOT_VALID')), error_message TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Add indexes for performance CREATE INDEX idx_printed_tickets_barcode ON printed_tickets(barcode_number); CREATE INDEX idx_printed_tickets_event_id ON printed_tickets(event_id); CREATE INDEX idx_printed_tickets_org_id ON printed_tickets(organization_id); CREATE INDEX idx_scan_attempts_barcode ON scan_attempts(barcode_number); CREATE INDEX idx_scan_attempts_event_id ON scan_attempts(event_id); CREATE INDEX idx_scan_attempts_created_at ON scan_attempts(created_at); -- Add RLS policies for multi-tenant security ALTER TABLE printed_tickets ENABLE ROW LEVEL SECURITY; ALTER TABLE scan_attempts ENABLE ROW LEVEL SECURITY; -- Policies for printed_tickets CREATE POLICY "Users can view printed tickets in their organization" ON printed_tickets FOR SELECT USING ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id = printed_tickets.organization_id ) ); CREATE POLICY "Users can insert printed tickets in their organization" ON printed_tickets FOR INSERT WITH CHECK ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id = printed_tickets.organization_id ) ); CREATE POLICY "Users can update printed tickets in their organization" ON printed_tickets FOR UPDATE USING ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id = printed_tickets.organization_id ) ); -- Policies for scan_attempts CREATE POLICY "Users can view scan attempts in their organization" ON scan_attempts FOR SELECT USING ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id = ( SELECT organization_id FROM events WHERE events.id = scan_attempts.event_id ) ) ); CREATE POLICY "Users can insert scan attempts in their organization" ON scan_attempts FOR INSERT WITH CHECK ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id = ( SELECT organization_id FROM events WHERE events.id = scan_attempts.event_id ) ) ); -- Admin override policies (for users with admin privileges) CREATE POLICY "Admin can view all printed tickets" ON printed_tickets FOR SELECT USING ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id IS NULL ) ); CREATE POLICY "Admin can manage all printed tickets" ON printed_tickets FOR ALL USING ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id IS NULL ) ); CREATE POLICY "Admin can view all scan attempts" ON scan_attempts FOR SELECT USING ( EXISTS ( SELECT 1 FROM users WHERE users.id = auth.uid() AND users.organization_id IS NULL ) ); -- Create function to automatically set organization_id when inserting printed tickets CREATE OR REPLACE FUNCTION set_printed_ticket_organization_id() RETURNS TRIGGER AS $$ BEGIN -- Set organization_id from the event NEW.organization_id = ( SELECT organization_id FROM events WHERE id = NEW.event_id ); RETURN NEW; END; $$ LANGUAGE plpgsql; -- Create trigger to automatically set organization_id CREATE TRIGGER set_printed_ticket_organization_id_trigger BEFORE INSERT ON printed_tickets FOR EACH ROW EXECUTE FUNCTION set_printed_ticket_organization_id(); -- Create function to update updated_at timestamp CREATE OR REPLACE FUNCTION update_printed_ticket_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; -- Create trigger to update updated_at CREATE TRIGGER update_printed_ticket_updated_at_trigger BEFORE UPDATE ON printed_tickets FOR EACH ROW EXECUTE FUNCTION update_printed_ticket_updated_at();