Files
blackcanyontickets/supabase/migrations/20250109_territory_manager_schema.sql
dzinesco 26a87d0d00 feat: Complete platform enhancement with multi-tenant architecture
Major additions:
- Territory manager system with application workflow
- Custom pricing and page builder with Craft.js
- Enhanced Stripe Connect onboarding
- CodeReadr QR scanning integration
- Kiosk mode for venue sales
- Super admin dashboard and analytics
- MCP integration for AI-powered operations

Infrastructure improvements:
- Centralized API client and routing system
- Enhanced authentication with organization context
- Comprehensive theme management system
- Advanced event management with custom tabs
- Performance monitoring and accessibility features

Database schema updates:
- Territory management tables
- Custom pages and pricing structures
- Kiosk PIN system
- Enhanced organization profiles
- CodeReadr integration tables

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-12 18:21:40 -06:00

297 lines
14 KiB
PL/PgSQL

-- Territory Manager System Schema
-- This migration creates the core tables for the Territory Manager system
-- Create territories table
CREATE TABLE IF NOT EXISTS territories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
boundary JSONB, -- GeoJSON polygon for territory bounds
population INTEGER,
market_size VARCHAR(50),
assigned_to UUID REFERENCES users(id),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create territory_managers table
CREATE TABLE IF NOT EXISTS territory_managers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) UNIQUE NOT NULL,
territory_id UUID REFERENCES territories(id),
referral_code VARCHAR(50) UNIQUE NOT NULL,
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'suspended', 'inactive')),
application_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
approved_date TIMESTAMP WITH TIME ZONE,
approved_by UUID REFERENCES users(id),
profile JSONB, -- Personal info, preferences, etc.
documents JSONB, -- Uploaded documents like ID, W-9, etc.
earnings_data JSONB, -- Earnings statistics and history
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create territory_applications table
CREATE TABLE IF NOT EXISTS territory_applications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL,
full_name VARCHAR(255) NOT NULL,
phone VARCHAR(20),
address JSONB, -- Address information
desired_territory VARCHAR(255),
has_transportation BOOLEAN DEFAULT false,
has_event_experience BOOLEAN DEFAULT false,
motivation TEXT,
documents JSONB, -- File uploads
consent JSONB, -- Background check and data processing consent
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected')),
reviewed_by UUID REFERENCES users(id),
reviewed_at TIMESTAMP WITH TIME ZONE,
review_notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create leads table
CREATE TABLE IF NOT EXISTS leads (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
territory_manager_id UUID REFERENCES territory_managers(id) NOT NULL,
event_name VARCHAR(255) NOT NULL,
organizer_contact JSONB, -- Contact information
event_details JSONB, -- Event details like date, type, etc.
status VARCHAR(20) DEFAULT 'cold' CHECK (status IN ('cold', 'contacted', 'confirmed', 'converted', 'dead')),
notes TEXT[],
follow_up_date TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create commissions table
CREATE TABLE IF NOT EXISTS commissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
territory_manager_id UUID REFERENCES territory_managers(id) NOT NULL,
event_id UUID REFERENCES events(id) NOT NULL,
tickets_sold INTEGER DEFAULT 0,
commission_per_ticket DECIMAL(10,2) DEFAULT 0.10,
total_commission DECIMAL(10,2) DEFAULT 0,
status VARCHAR(20) DEFAULT 'unpaid' CHECK (status IN ('unpaid', 'paid', 'pending')),
payout_date TIMESTAMP WITH TIME ZONE,
stripe_transfer_id VARCHAR(255),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create training_progress table
CREATE TABLE IF NOT EXISTS training_progress (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
territory_manager_id UUID REFERENCES territory_managers(id) NOT NULL,
module_id VARCHAR(255) NOT NULL,
completed_at TIMESTAMP WITH TIME ZONE,
score INTEGER,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(territory_manager_id, module_id)
);
-- Create achievements table
CREATE TABLE IF NOT EXISTS achievements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT,
requirements JSONB, -- Achievement requirements
reward_amount DECIMAL(10,2) DEFAULT 0,
badge_icon VARCHAR(255),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create territory_manager_achievements table
CREATE TABLE IF NOT EXISTS territory_manager_achievements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
territory_manager_id UUID REFERENCES territory_managers(id) NOT NULL,
achievement_id UUID REFERENCES achievements(id) NOT NULL,
earned_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(territory_manager_id, achievement_id)
);
-- Create expense_reports table
CREATE TABLE IF NOT EXISTS expense_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
territory_manager_id UUID REFERENCES territory_managers(id) NOT NULL,
event_id UUID REFERENCES events(id),
mileage DECIMAL(10,2) DEFAULT 0,
receipts JSONB, -- File uploads
total_amount DECIMAL(10,2) DEFAULT 0,
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'paid', 'rejected')),
submitted_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
approved_date TIMESTAMP WITH TIME ZONE,
approved_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create notifications table
CREATE TABLE IF NOT EXISTS tm_notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
territory_manager_id UUID REFERENCES territory_managers(id) NOT NULL,
type VARCHAR(50) NOT NULL,
title VARCHAR(255) NOT NULL,
message TEXT,
data JSONB, -- Additional notification data
read BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Create indexes for better performance
CREATE INDEX idx_territories_assigned_to ON territories(assigned_to);
CREATE INDEX idx_territory_managers_user_id ON territory_managers(user_id);
CREATE INDEX idx_territory_managers_territory_id ON territory_managers(territory_id);
CREATE INDEX idx_territory_managers_status ON territory_managers(status);
CREATE INDEX idx_territory_applications_status ON territory_applications(status);
CREATE INDEX idx_territory_applications_email ON territory_applications(email);
CREATE INDEX idx_leads_territory_manager_id ON leads(territory_manager_id);
CREATE INDEX idx_leads_status ON leads(status);
CREATE INDEX idx_commissions_territory_manager_id ON commissions(territory_manager_id);
CREATE INDEX idx_commissions_event_id ON commissions(event_id);
CREATE INDEX idx_commissions_status ON commissions(status);
CREATE INDEX idx_training_progress_territory_manager_id ON training_progress(territory_manager_id);
CREATE INDEX idx_tm_notifications_territory_manager_id ON tm_notifications(territory_manager_id);
CREATE INDEX idx_tm_notifications_read ON tm_notifications(read);
-- Add RLS policies
ALTER TABLE territories ENABLE ROW LEVEL SECURITY;
ALTER TABLE territory_managers ENABLE ROW LEVEL SECURITY;
ALTER TABLE territory_applications ENABLE ROW LEVEL SECURITY;
ALTER TABLE leads ENABLE ROW LEVEL SECURITY;
ALTER TABLE commissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE training_progress ENABLE ROW LEVEL SECURITY;
ALTER TABLE achievements ENABLE ROW LEVEL SECURITY;
ALTER TABLE territory_manager_achievements ENABLE ROW LEVEL SECURITY;
ALTER TABLE expense_reports ENABLE ROW LEVEL SECURITY;
ALTER TABLE tm_notifications ENABLE ROW LEVEL SECURITY;
-- Basic RLS policies (admin can see all, territory managers can only see their own data)
CREATE POLICY "Admin can view all territories" ON territories FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can view their territory" ON territories FOR SELECT USING (assigned_to = auth.uid());
CREATE POLICY "Admin can view all territory managers" ON territory_managers FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can view their own data" ON territory_managers FOR SELECT USING (user_id = auth.uid());
CREATE POLICY "Territory managers can update their own data" ON territory_managers FOR UPDATE USING (user_id = auth.uid());
CREATE POLICY "Admin can view all applications" ON territory_applications FOR ALL USING (is_admin());
CREATE POLICY "Public can create applications" ON territory_applications FOR INSERT WITH CHECK (true);
CREATE POLICY "Admin can view all leads" ON leads FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can manage their leads" ON leads FOR ALL USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = leads.territory_manager_id AND territory_managers.user_id = auth.uid())
);
CREATE POLICY "Admin can view all commissions" ON commissions FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can view their commissions" ON commissions FOR SELECT USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = commissions.territory_manager_id AND territory_managers.user_id = auth.uid())
);
CREATE POLICY "Admin can view all training progress" ON training_progress FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can manage their training progress" ON training_progress FOR ALL USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = training_progress.territory_manager_id AND territory_managers.user_id = auth.uid())
);
CREATE POLICY "Everyone can view achievements" ON achievements FOR SELECT USING (true);
CREATE POLICY "Admin can manage achievements" ON achievements FOR ALL USING (is_admin());
CREATE POLICY "Admin can view all territory manager achievements" ON territory_manager_achievements FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can view their achievements" ON territory_manager_achievements FOR SELECT USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = territory_manager_achievements.territory_manager_id AND territory_managers.user_id = auth.uid())
);
CREATE POLICY "Admin can view all expense reports" ON expense_reports FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can manage their expense reports" ON expense_reports FOR ALL USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = expense_reports.territory_manager_id AND territory_managers.user_id = auth.uid())
);
CREATE POLICY "Admin can view all notifications" ON tm_notifications FOR ALL USING (is_admin());
CREATE POLICY "Territory managers can view their notifications" ON tm_notifications FOR SELECT USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = tm_notifications.territory_manager_id AND territory_managers.user_id = auth.uid())
);
CREATE POLICY "Territory managers can update their notifications" ON tm_notifications FOR UPDATE USING (
EXISTS (SELECT 1 FROM territory_managers WHERE territory_managers.id = tm_notifications.territory_manager_id AND territory_managers.user_id = auth.uid())
);
-- Create trigger for updating updated_at timestamps
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_territories_updated_at BEFORE UPDATE ON territories
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_territory_managers_updated_at BEFORE UPDATE ON territory_managers
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_territory_applications_updated_at BEFORE UPDATE ON territory_applications
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_leads_updated_at BEFORE UPDATE ON leads
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_commissions_updated_at BEFORE UPDATE ON commissions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_expense_reports_updated_at BEFORE UPDATE ON expense_reports
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Function to generate unique referral codes
CREATE OR REPLACE FUNCTION generate_referral_code() RETURNS TEXT AS $$
DECLARE
code TEXT;
exists_check INTEGER;
BEGIN
LOOP
code := UPPER(SUBSTRING(MD5(random()::text) FROM 1 FOR 8));
SELECT COUNT(*) INTO exists_check FROM territory_managers WHERE referral_code = code;
IF exists_check = 0 THEN
RETURN code;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- Function to auto-assign referral code when creating territory manager
CREATE OR REPLACE FUNCTION auto_assign_referral_code() RETURNS TRIGGER AS $$
BEGIN
IF NEW.referral_code IS NULL OR NEW.referral_code = '' THEN
NEW.referral_code := generate_referral_code();
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER auto_assign_referral_code_trigger
BEFORE INSERT ON territory_managers
FOR EACH ROW
EXECUTE FUNCTION auto_assign_referral_code();
-- Function to calculate commission totals
CREATE OR REPLACE FUNCTION calculate_commission_total() RETURNS TRIGGER AS $$
BEGIN
NEW.total_commission := NEW.tickets_sold * NEW.commission_per_ticket;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER calculate_commission_total_trigger
BEFORE INSERT OR UPDATE ON commissions
FOR EACH ROW
EXECUTE FUNCTION calculate_commission_total();
-- Insert sample achievements
INSERT INTO achievements (name, description, requirements, reward_amount, badge_icon) VALUES
('First Referral', 'Successfully refer your first event to BCT', '{"referrals": 1}', 50.00, 'first-referral.svg'),
('Sales Champion', 'Refer 10 events in a single month', '{"monthly_referrals": 10}', 500.00, 'sales-champion.svg'),
('Territory Master', 'Maintain 95% event success rate for 3 months', '{"success_rate": 0.95, "months": 3}', 1000.00, 'territory-master.svg'),
('Training Graduate', 'Complete all training modules', '{"training_modules": "all"}', 100.00, 'training-graduate.svg'),
('Community Builder', 'Refer 5 recurring events', '{"recurring_events": 5}', 300.00, 'community-builder.svg'),
('Revenue Generator', 'Generate over $10,000 in commission in a quarter', '{"quarterly_commission": 10000}', 2000.00, 'revenue-generator.svg');