115 lines
4.2 KiB
SQL
115 lines
4.2 KiB
SQL
-- Migration 039: Vereins-Mitgliedschaft, Rollen pro Verein, aktiver Vereinskontext (Multi-Tenancy Phase 1)
|
|
-- Erstellt: 2026-05-05
|
|
|
|
-- Mitgliedschaft Profil ↔ Verein
|
|
CREATE TABLE club_members (
|
|
id SERIAL PRIMARY KEY,
|
|
profile_id INT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
|
club_id INT NOT NULL REFERENCES clubs(id) ON DELETE CASCADE,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(profile_id, club_id)
|
|
);
|
|
|
|
CREATE INDEX idx_club_members_profile ON club_members(profile_id);
|
|
CREATE INDEX idx_club_members_club ON club_members(club_id);
|
|
CREATE INDEX idx_club_members_status ON club_members(status);
|
|
|
|
-- Rollen pro Mitgliedschaft (role_code: club_admin, trainer, division_lead, content_editor)
|
|
CREATE TABLE club_member_roles (
|
|
id SERIAL PRIMARY KEY,
|
|
club_member_id INT NOT NULL REFERENCES club_members(id) ON DELETE CASCADE,
|
|
role_code VARCHAR(50) NOT NULL,
|
|
division_id INT REFERENCES divisions(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE (club_member_id, role_code)
|
|
);
|
|
|
|
CREATE INDEX idx_club_member_roles_member ON club_member_roles(club_member_id);
|
|
CREATE INDEX idx_club_member_roles_code ON club_member_roles(role_code);
|
|
|
|
-- Hauptverwalter:in (fachliche Referenz; Rechte über club_member_roles.club_admin)
|
|
ALTER TABLE clubs ADD COLUMN IF NOT EXISTS primary_admin_profile_id INT REFERENCES profiles(id) ON DELETE SET NULL;
|
|
|
|
CREATE INDEX idx_clubs_primary_admin ON clubs(primary_admin_profile_id);
|
|
|
|
-- Persistenz gewählter aktiver Verein (UI); Request-Header kann überschreiben
|
|
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS active_club_id INT REFERENCES clubs(id) ON DELETE SET NULL;
|
|
|
|
CREATE INDEX idx_profiles_active_club ON profiles(active_club_id);
|
|
|
|
-- ── Backfill: Trainer/Co-Trainer aus Trainingsgruppen → Mitgliedschaft + Rolle trainer
|
|
INSERT INTO club_members (profile_id, club_id, status)
|
|
SELECT DISTINCT t.trainer_id, t.club_id, 'active'
|
|
FROM training_groups t
|
|
WHERE t.trainer_id IS NOT NULL
|
|
ON CONFLICT (profile_id, club_id) DO NOTHING;
|
|
|
|
INSERT INTO club_members (profile_id, club_id, status)
|
|
SELECT DISTINCT elem::int, t.club_id, 'active'
|
|
FROM training_groups t,
|
|
LATERAL jsonb_array_elements_text(COALESCE(t.co_trainer_ids, '[]'::jsonb)) AS elem
|
|
WHERE jsonb_array_length(COALESCE(t.co_trainer_ids, '[]'::jsonb)) > 0
|
|
ON CONFLICT (profile_id, club_id) DO NOTHING;
|
|
|
|
INSERT INTO club_member_roles (club_member_id, role_code)
|
|
SELECT cm.id, 'trainer'
|
|
FROM club_members cm
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM club_member_roles r
|
|
WHERE r.club_member_id = cm.id AND r.role_code = 'trainer'
|
|
);
|
|
|
|
-- Pro Verein: kleinste trainer_id einer Gruppe als primärer Admin (Pilot / CURR-008-Analog)
|
|
UPDATE clubs c
|
|
SET primary_admin_profile_id = sub.pid
|
|
FROM (
|
|
SELECT DISTINCT ON (club_id)
|
|
club_id,
|
|
trainer_id AS pid
|
|
FROM training_groups
|
|
WHERE trainer_id IS NOT NULL
|
|
ORDER BY club_id, id ASC
|
|
) sub
|
|
WHERE c.id = sub.club_id
|
|
AND c.primary_admin_profile_id IS NULL;
|
|
|
|
INSERT INTO club_member_roles (club_member_id, role_code)
|
|
SELECT cm.id, 'club_admin'
|
|
FROM club_members cm
|
|
INNER JOIN clubs cl ON cl.id = cm.club_id AND cl.primary_admin_profile_id = cm.profile_id
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM club_member_roles r
|
|
WHERE r.club_member_id = cm.id AND r.role_code = 'club_admin'
|
|
);
|
|
|
|
-- Globale Admins: in jedem bestehenden Verein als club_admin spiegeln (volle Plattform-Sicht)
|
|
INSERT INTO club_members (profile_id, club_id, status)
|
|
SELECT p.id, c.id, 'active'
|
|
FROM profiles p
|
|
CROSS JOIN clubs c
|
|
WHERE p.role IN ('admin', 'superadmin')
|
|
ON CONFLICT (profile_id, club_id) DO NOTHING;
|
|
|
|
INSERT INTO club_member_roles (club_member_id, role_code)
|
|
SELECT cm.id, 'club_admin'
|
|
FROM club_members cm
|
|
INNER JOIN profiles p ON p.id = cm.profile_id AND p.role IN ('admin', 'superadmin')
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM club_member_roles r
|
|
WHERE r.club_member_id = cm.id AND r.role_code = 'club_admin'
|
|
);
|
|
|
|
-- Default aktiver Verein: einziger Verein des Nutzers
|
|
UPDATE profiles p
|
|
SET active_club_id = sub.only_club
|
|
FROM (
|
|
SELECT profile_id, MIN(club_id) AS only_club
|
|
FROM club_members
|
|
WHERE status = 'active'
|
|
GROUP BY profile_id
|
|
HAVING COUNT(DISTINCT club_id) = 1
|
|
) sub
|
|
WHERE p.id = sub.profile_id AND (p.active_club_id IS NULL);
|