Yaltopia-FIFA/supabase/migrations/20250524000004_functions.sql
Kirubel-Kibru-Yaltopia 89440985f1
Some checks failed
Deploy to Cloudflare Workers / deploy (push) Has been cancelled
x
2026-05-24 21:46:10 +03:00

303 lines
9.1 KiB
PL/PgSQL

-- Core business logic functions
CREATE OR REPLACE FUNCTION get_default_rules()
RETURNS JSONB AS $$
SELECT jsonb_build_object(
'points_win', 3,
'points_draw', 1,
'round_robin_format', 'double',
'transfer_policy', 'anytime',
'auto_qualify_count', 1,
'strict_availability', false,
'result_approval_required', true,
'cup_format', jsonb_build_object(
'legs', 'single',
'third_place_match', false,
'seeding', 'random'
)
);
$$ LANGUAGE sql IMMUTABLE;
-- Snapshot rules when activating competition
CREATE OR REPLACE FUNCTION activate_competition(p_competition_id UUID)
RETURNS VOID AS $$
DECLARE
v_league_id UUID;
v_latest_rules JSONB;
v_version INT;
BEGIN
SELECT league_id INTO v_league_id FROM competitions WHERE id = p_competition_id;
SELECT rules, version INTO v_latest_rules, v_version
FROM league_rules
WHERE league_id = v_league_id
ORDER BY version DESC
LIMIT 1;
IF v_latest_rules IS NULL THEN
v_latest_rules := get_default_rules();
v_version := 1;
END IF;
UPDATE competitions
SET status = 'active',
rules_snapshot = v_latest_rules,
rule_set_version = v_version
WHERE id = p_competition_id AND status = 'draft';
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Generate double round-robin fixtures
CREATE OR REPLACE FUNCTION generate_league_fixtures(p_competition_id UUID)
RETURNS INT AS $$
DECLARE
team_ids UUID[];
n INT;
rounds INT;
r INT;
i INT;
j INT;
fixed UUID;
rotating UUID[];
match_count INT := 0;
leg INT;
BEGIN
SELECT ARRAY_AGG(id ORDER BY name) INTO team_ids
FROM teams WHERE competition_id = p_competition_id;
n := COALESCE(array_length(team_ids, 1), 0);
IF n < 2 THEN
RAISE EXCEPTION 'Need at least 2 teams';
END IF;
DELETE FROM matches WHERE competition_id = p_competition_id AND status = 'scheduled';
IF n % 2 = 1 THEN
team_ids := team_ids || NULL::UUID;
n := n + 1;
END IF;
rounds := n - 1;
rotating := team_ids[2:n];
FOR leg IN 1..2 LOOP
FOR r IN 1..rounds LOOP
IF team_ids[1] IS NOT NULL AND rotating[1] IS NOT NULL THEN
IF leg = 1 THEN
INSERT INTO matches (competition_id, home_team_id, away_team_id, round, matchday, leg, status)
VALUES (p_competition_id, team_ids[1], rotating[1], r, r + (leg-1)*rounds, leg, 'scheduled');
ELSE
INSERT INTO matches (competition_id, home_team_id, away_team_id, round, matchday, leg, status)
VALUES (p_competition_id, rotating[1], team_ids[1], r, r + (leg-1)*rounds, leg, 'scheduled');
END IF;
match_count := match_count + 1;
END IF;
FOR i IN 1..(array_length(rotating, 1) / 2) LOOP
j := array_length(rotating, 1) - i + 1;
IF rotating[i] IS NOT NULL AND rotating[j] IS NOT NULL AND i <> j THEN
IF leg = 1 THEN
INSERT INTO matches (competition_id, home_team_id, away_team_id, round, matchday, leg, status)
VALUES (p_competition_id, rotating[i], rotating[j], r, r + (leg-1)*rounds, leg, 'scheduled');
ELSE
INSERT INTO matches (competition_id, home_team_id, away_team_id, round, matchday, leg, status)
VALUES (p_competition_id, rotating[j], rotating[i], r, r + (leg-1)*rounds, leg, 'scheduled');
END IF;
match_count := match_count + 1;
END IF;
END LOOP;
fixed := rotating[array_length(rotating, 1)];
rotating := ARRAY[fixed] || rotating[1:array_length(rotating,1)-1];
END LOOP;
END LOOP;
RETURN match_count;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Generate cup bracket (single elimination, first round)
CREATE OR REPLACE FUNCTION generate_cup_bracket(p_competition_id UUID)
RETURNS INT AS $$
DECLARE
team_ids UUID[];
n INT;
i INT;
match_count INT := 0;
BEGIN
SELECT ARRAY_AGG(id ORDER BY COALESCE(cup_seed, 999), name) INTO team_ids
FROM teams WHERE competition_id = p_competition_id;
n := COALESCE(array_length(team_ids, 1), 0);
IF n < 2 THEN RAISE EXCEPTION 'Need at least 2 teams'; END IF;
DELETE FROM matches WHERE competition_id = p_competition_id AND status = 'scheduled';
i := 1;
WHILE i < n LOOP
IF team_ids[i] IS NOT NULL AND team_ids[i+1] IS NOT NULL THEN
INSERT INTO matches (competition_id, home_team_id, away_team_id, bracket_round, bracket_slot, status)
VALUES (p_competition_id, team_ids[i], team_ids[i+1], 1, (i+1)/2, 'scheduled');
match_count := match_count + 1;
END IF;
i := i + 2;
END LOOP;
RETURN match_count;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Submit match result from team
CREATE OR REPLACE FUNCTION submit_match_result(
p_match_id UUID,
p_team_id UUID,
p_home_score INT,
p_away_score INT
)
RETURNS VOID AS $$
DECLARE
v_competition_id UUID;
v_sub_count INT;
v_home_sub INT;
v_away_sub INT;
v_sub1_home INT;
v_sub1_away INT;
v_sub2_home INT;
v_sub2_away INT;
BEGIN
SELECT competition_id INTO v_competition_id FROM matches WHERE id = p_match_id;
INSERT INTO match_result_submissions (match_id, team_id, home_score, away_score, submitted_by)
VALUES (p_match_id, p_team_id, p_home_score, p_away_score, auth.uid())
ON CONFLICT (match_id, team_id) DO UPDATE
SET home_score = EXCLUDED.home_score, away_score = EXCLUDED.away_score,
submitted_at = now(), submitted_by = auth.uid();
SELECT COUNT(*) INTO v_sub_count FROM match_result_submissions WHERE match_id = p_match_id;
IF v_sub_count < 2 THEN
UPDATE matches SET result_status = 'result_submitted', status = 'awaiting_results'
WHERE id = p_match_id;
RETURN;
END IF;
SELECT home_score, away_score INTO v_sub1_home, v_sub1_away
FROM match_result_submissions WHERE match_id = p_match_id
ORDER BY submitted_at LIMIT 1;
SELECT home_score, away_score INTO v_sub2_home, v_sub2_away
FROM match_result_submissions WHERE match_id = p_match_id
ORDER BY submitted_at DESC LIMIT 1;
IF v_sub1_home = v_sub2_home AND v_sub1_away = v_sub2_away THEN
UPDATE matches SET result_status = 'pending_approval', status = 'pending_approval'
WHERE id = p_match_id;
ELSE
UPDATE matches SET result_status = 'disputed', status = 'disputed'
WHERE id = p_match_id;
END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- League manager approves result
CREATE OR REPLACE FUNCTION approve_match_result(p_match_id UUID)
RETURNS VOID AS $$
DECLARE
v_sub RECORD;
BEGIN
IF NOT EXISTS (
SELECT 1 FROM matches m
WHERE m.id = p_match_id
AND is_competition_league_manager(m.competition_id)
) AND NOT EXISTS (
SELECT 1 FROM matches m JOIN competitions c ON c.id = m.competition_id
JOIN leagues l ON l.id = c.league_id
WHERE m.id = p_match_id AND l.created_by = auth.uid()
) THEN
RAISE EXCEPTION 'Not authorized';
END IF;
SELECT home_score, away_score INTO v_sub
FROM match_result_submissions WHERE match_id = p_match_id LIMIT 1;
UPDATE matches SET
home_score = v_sub.home_score,
away_score = v_sub.away_score,
status = 'completed',
result_status = 'completed',
approved_by = auth.uid(),
approved_at = now()
WHERE id = p_match_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- League manager sets result directly
CREATE OR REPLACE FUNCTION set_match_result_by_manager(
p_match_id UUID,
p_home_score INT,
p_away_score INT,
p_note TEXT DEFAULT NULL
)
RETURNS VOID AS $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM matches m WHERE m.id = p_match_id
AND is_competition_league_manager(m.competition_id)
) AND NOT EXISTS (
SELECT 1 FROM matches m JOIN competitions c ON c.id = m.competition_id
JOIN leagues l ON l.id = c.league_id
WHERE m.id = p_match_id AND l.created_by = auth.uid()
) THEN
RAISE EXCEPTION 'Not authorized';
END IF;
UPDATE matches SET
home_score = p_home_score,
away_score = p_away_score,
status = 'completed',
result_status = 'completed',
approved_by = auth.uid(),
approved_at = now(),
resolution_note = p_note
WHERE id = p_match_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Sign schedule
CREATE OR REPLACE FUNCTION sign_match_schedule(p_match_id UUID, p_team_id UUID)
RETURNS VOID AS $$
DECLARE
v_sig_count INT;
v_proposed TIMESTAMPTZ;
BEGIN
SELECT proposed_scheduled_at INTO v_proposed FROM matches WHERE id = p_match_id;
INSERT INTO match_signatures (match_id, team_id, user_id, signature_type)
VALUES (p_match_id, p_team_id, auth.uid(), 'schedule')
ON CONFLICT (match_id, team_id, signature_type) DO NOTHING;
SELECT COUNT(*) INTO v_sig_count FROM match_signatures
WHERE match_id = p_match_id AND signature_type = 'schedule';
IF v_sig_count >= 2 AND v_proposed IS NOT NULL THEN
UPDATE matches SET
status = 'schedule_confirmed',
scheduled_at = v_proposed,
venue = (SELECT home_stadium_name FROM teams t
JOIN matches m ON m.home_team_id = t.id WHERE m.id = p_match_id)
WHERE id = p_match_id;
END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Propose schedule time
CREATE OR REPLACE FUNCTION propose_match_schedule(p_match_id UUID, p_scheduled_at TIMESTAMPTZ)
RETURNS VOID AS $$
BEGIN
UPDATE matches SET
proposed_scheduled_at = p_scheduled_at,
status = 'schedule_pending'
WHERE id = p_match_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;