teecup/003_scoring_and_blinddraw.sql
2026-07-16 07:26:04 +02:00

98 lines
4.6 KiB
SQL

-- =====================================================================
-- TeeCup — migrasjon 003
-- Scoring-modus (ADR-012), blind draw (ADR-013), to-lags-format (ADR-011)
-- =====================================================================
-- Kjøres etter 001 (skjema) og 002 (roller). Idempotent der det er lett å få til.
-- =====================================================================
\set ON_ERROR_STOP on
-- ---------------------------------------------------------------------
-- 1. Scoring-modus per økt (ADR-012)
-- ---------------------------------------------------------------------
-- 'stroke' : spillere taster slag per hull -> hole_score. Motoren utleder
-- netto og hull-resultat.
-- 'hole_result' : registrer bare hvem som vant hullet -> match_hole_result.
ALTER TABLE session
ADD COLUMN scoring_mode text NOT NULL DEFAULT 'stroke'
CHECK (scoring_mode IN ('stroke', 'hole_result'));
-- ---------------------------------------------------------------------
-- 2. Forenklet scoreføring: matchresultat per hull (ADR-012)
-- ---------------------------------------------------------------------
-- Brukes når session.scoring_mode = 'hole_result'. winning_side NULL = delt hull.
-- compute_match_state i motoren mates fra enten hole_score (stroke) eller denne.
CREATE TABLE match_hole_result (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id uuid NOT NULL,
match_id uuid NOT NULL,
hole_number smallint NOT NULL CHECK (hole_number BETWEEN 1 AND 18),
winning_side team_side, -- NULL = delt (halved)
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
FOREIGN KEY (organization_id, match_id)
REFERENCES match(organization_id, id) ON DELETE CASCADE,
UNIQUE (match_id, hole_number)
);
CREATE INDEX ON match_hole_result (organization_id, match_id);
CREATE TRIGGER trg_match_hole_result_updated
BEFORE UPDATE ON match_hole_result
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
-- ---------------------------------------------------------------------
-- 3. Blind draw: lås per lag per økt (ADR-013)
-- ---------------------------------------------------------------------
-- En økt er "avslørt" når det finnes en lineup_lock for BEGGE lag. Appen skjuler
-- motstanderens match_participant til da, og hindrer endring av egen oppstilling
-- etter lås. (Finkornet synlighet i app-laget, ikke RLS — begge lag samme org.)
CREATE TABLE lineup_lock (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id uuid NOT NULL,
session_id uuid NOT NULL,
team_id uuid NOT NULL,
locked_at timestamptz NOT NULL DEFAULT now(),
locked_by uuid REFERENCES app_user(id) ON DELETE SET NULL,
FOREIGN KEY (organization_id, session_id)
REFERENCES session(organization_id, id) ON DELETE CASCADE,
FOREIGN KEY (organization_id, team_id)
REFERENCES team(organization_id, id) ON DELETE CASCADE,
UNIQUE (session_id, team_id) -- ett lås per lag per økt
);
CREATE INDEX ON lineup_lock (organization_id, session_id);
-- ---------------------------------------------------------------------
-- 4. RLS for de nye tabellene (samme org-isolasjon som resten, ADR-003)
-- ---------------------------------------------------------------------
DO $$
DECLARE t text;
BEGIN
FOREACH t IN ARRAY ARRAY['match_hole_result', 'lineup_lock']
LOOP
EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY;', t);
EXECUTE format('ALTER TABLE %I FORCE ROW LEVEL SECURITY;', t);
EXECUTE format($p$
CREATE POLICY org_isolation ON %I
USING (organization_id = current_setting('app.current_org', true)::uuid)
WITH CHECK (organization_id = current_setting('app.current_org', true)::uuid);
$p$, t);
END LOOP;
END $$;
-- Grants til runtime-rollen (den eier ikke tabellene, så den trenger eksplisitt DML).
GRANT SELECT, INSERT, UPDATE, DELETE ON match_hole_result TO teecup_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON lineup_lock TO teecup_app;
-- ---------------------------------------------------------------------
-- 5. To-lags-format (ADR-011)
-- ---------------------------------------------------------------------
-- MERK: "nøyaktig to lag per turnering" håndheves i app-laget ved
-- turneringsoppsett, ikke som DB-trigger (holder match-modellen generell slik at
-- flere lag / knockout kan komme senere uten dataomskriving). Ingen skjemaendring
-- her — dette er kun en påminnelse om regelen.