-- CodeREADr Integration Tables -- Table to store CodeREADr configuration for each event CREATE TABLE codereadr_configs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE, organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE, -- CodeREADr API entities database_id TEXT NOT NULL, service_id TEXT NOT NULL, user_id TEXT NOT NULL, -- Readable names for reference database_name TEXT NOT NULL, service_name TEXT NOT NULL, user_name TEXT NOT NULL, -- Status and metadata status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'error')), last_sync_at TIMESTAMP WITH TIME ZONE, total_scans INTEGER DEFAULT 0, -- Timestamps created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(), -- Unique constraint to prevent multiple configs per event UNIQUE(event_id, organization_id) ); -- Table to store synchronized scans from CodeREADr CREATE TABLE codereadr_scans ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), codereadr_scan_id TEXT NOT NULL UNIQUE, -- CodeREADr's scan ID -- Event and service references event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE, service_id TEXT NOT NULL, -- Scan data ticket_uuid TEXT NOT NULL, scan_timestamp TIMESTAMP WITH TIME ZONE NOT NULL, response TEXT, -- CodeREADr response data device_id TEXT, location JSONB, -- {latitude, longitude} if available -- Sync metadata synced_at TIMESTAMP WITH TIME ZONE DEFAULT now(), webhook_processed BOOLEAN DEFAULT false, ticket_updated BOOLEAN DEFAULT false, error_message TEXT, -- Index for quick lookups INDEX idx_codereadr_scans_event_id (event_id), INDEX idx_codereadr_scans_ticket_uuid (ticket_uuid), INDEX idx_codereadr_scans_timestamp (scan_timestamp), INDEX idx_codereadr_scans_service_id (service_id) ); -- Add scan_method column to existing tickets table to track how ticket was scanned ALTER TABLE tickets ADD COLUMN IF NOT EXISTS scan_method TEXT DEFAULT 'manual' CHECK (scan_method IN ('manual', 'qr', 'codereadr', 'api')); -- Add scan_method column to existing scan_attempts table ALTER TABLE scan_attempts ADD COLUMN IF NOT EXISTS scan_method TEXT DEFAULT 'manual' CHECK (scan_method IN ('manual', 'qr', 'codereadr', 'api')); -- Update existing scan attempts to have a scan_method UPDATE scan_attempts SET scan_method = 'qr' WHERE scan_method IS NULL; UPDATE tickets SET scan_method = 'qr' WHERE scan_method IS NULL AND checked_in = true; -- Add RLS policies for codereadr_configs table ALTER TABLE codereadr_configs ENABLE ROW LEVEL SECURITY; -- Users can only access configs for their organization CREATE POLICY "Users can view codereadr_configs for their organization" ON codereadr_configs FOR SELECT USING ( organization_id IN ( SELECT organization_id FROM users WHERE id = auth.uid() ) ); CREATE POLICY "Users can insert codereadr_configs for their organization" ON codereadr_configs FOR INSERT WITH CHECK ( organization_id IN ( SELECT organization_id FROM users WHERE id = auth.uid() ) ); CREATE POLICY "Users can update codereadr_configs for their organization" ON codereadr_configs FOR UPDATE USING ( organization_id IN ( SELECT organization_id FROM users WHERE id = auth.uid() ) ); CREATE POLICY "Users can delete codereadr_configs for their organization" ON codereadr_configs FOR DELETE USING ( organization_id IN ( SELECT organization_id FROM users WHERE id = auth.uid() ) ); -- Admin bypass for codereadr_configs CREATE POLICY "Admins can manage all codereadr_configs" ON codereadr_configs FOR ALL USING ( EXISTS ( SELECT 1 FROM users WHERE id = auth.uid() AND role = 'admin' ) ); -- Add RLS policies for codereadr_scans table ALTER TABLE codereadr_scans ENABLE ROW LEVEL SECURITY; -- Users can only access scans for events in their organization CREATE POLICY "Users can view codereadr_scans for their organization" ON codereadr_scans FOR SELECT USING ( event_id IN ( SELECT e.id FROM events e JOIN users u ON e.organization_id = u.organization_id WHERE u.id = auth.uid() ) ); CREATE POLICY "Users can insert codereadr_scans for their organization" ON codereadr_scans FOR INSERT WITH CHECK ( event_id IN ( SELECT e.id FROM events e JOIN users u ON e.organization_id = u.organization_id WHERE u.id = auth.uid() ) ); -- Admin bypass for codereadr_scans CREATE POLICY "Admins can manage all codereadr_scans" ON codereadr_scans FOR ALL USING ( EXISTS ( SELECT 1 FROM users WHERE id = auth.uid() AND role = 'admin' ) ); -- Add indexes for better performance CREATE INDEX IF NOT EXISTS idx_codereadr_configs_event_id ON codereadr_configs(event_id); CREATE INDEX IF NOT EXISTS idx_codereadr_configs_organization_id ON codereadr_configs(organization_id); CREATE INDEX IF NOT EXISTS idx_codereadr_configs_status ON codereadr_configs(status); -- Add function to automatically update updated_at timestamp CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ language 'plpgsql'; -- Add trigger for codereadr_configs updated_at CREATE TRIGGER update_codereadr_configs_updated_at BEFORE UPDATE ON codereadr_configs FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Add useful views for reporting CREATE VIEW codereadr_scan_summary AS SELECT c.event_id, c.organization_id, c.database_name, c.service_name, c.status, c.last_sync_at, COUNT(s.id) as total_scans, COUNT(CASE WHEN s.ticket_updated = true THEN 1 END) as successful_checkins, COUNT(CASE WHEN s.webhook_processed = true THEN 1 END) as webhook_scans, MAX(s.scan_timestamp) as latest_scan_time FROM codereadr_configs c LEFT JOIN codereadr_scans s ON c.event_id = s.event_id GROUP BY c.event_id, c.organization_id, c.database_name, c.service_name, c.status, c.last_sync_at; -- Add RLS to the view ALTER VIEW codereadr_scan_summary SET (security_invoker = true); COMMENT ON TABLE codereadr_configs IS 'Configuration for CodeREADr integration per event'; COMMENT ON TABLE codereadr_scans IS 'Synchronized scan records from CodeREADr'; COMMENT ON VIEW codereadr_scan_summary IS 'Summary view of CodeREADr scan statistics per event';