-- Migration 037: Rahmen-Slot-„Blueprint“ = eine training_units-Zeile (Ablauf wie echte Einheit) -- training_framework_slot_exercises migriert nach training_unit_sections / training_unit_section_items, -- dann entfernt. -- ── Neue Spalten ─────────────────────────────────────────────────────────────── ALTER TABLE training_units ADD COLUMN IF NOT EXISTS framework_slot_id INT REFERENCES training_framework_slots(id) ON DELETE CASCADE, ADD COLUMN IF NOT EXISTS origin_framework_slot_id INT REFERENCES training_framework_slots(id) ON DELETE SET NULL; -- Genau eine Blueprint-Einheit pro Slot (PostgreSQL UNIQUE erlaubt mehrere NULLs — hier Partial Index) DROP INDEX IF EXISTS uq_training_units_blueprint_slot; CREATE UNIQUE INDEX uq_training_units_blueprint_slot ON training_units(framework_slot_id) WHERE framework_slot_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_training_units_framework_blueprint_calendar ON training_units(planned_date, group_id) WHERE framework_slot_id IS NULL; CREATE INDEX IF NOT EXISTS idx_training_units_origin_slot ON training_units(origin_framework_slot_id) WHERE origin_framework_slot_id IS NOT NULL; -- ── Nullable für Blueprint-Zeilen ──────────────────────────────────────────── ALTER TABLE training_units ALTER COLUMN planned_date DROP NOT NULL; ALTER TABLE training_units ALTER COLUMN group_id DROP NOT NULL; -- ── Für jeden Slot eine Blueprint-Einheit; vorhandene Übungen in erste Sektion ─ DO $$ DECLARE rec RECORD; new_uid INTEGER; new_sec INTEGER; BEGIN FOR rec IN SELECT s.id AS sid, fp.created_by AS fp_created_by FROM training_framework_slots s JOIN training_framework_programs fp ON fp.id = s.framework_program_id LOOP IF EXISTS (SELECT 1 FROM training_units tu WHERE tu.framework_slot_id = rec.sid) THEN CONTINUE; END IF; INSERT INTO training_units ( group_id, planned_date, planned_time_start, planned_time_end, planned_focus, status, notes, trainer_notes, created_by, plan_template_id, framework_slot_id ) VALUES ( NULL, NULL, NULL, NULL, NULL, 'planned', NULL, NULL, rec.fp_created_by, NULL, rec.sid ) RETURNING id INTO new_uid; INSERT INTO training_unit_sections ( training_unit_id, order_index, title, guidance_notes ) VALUES (new_uid, 0, 'Ablauf', NULL) RETURNING id INTO new_sec; INSERT INTO training_unit_section_items ( section_id, order_index, item_type, exercise_id, exercise_variant_id, planned_duration_min, actual_duration_min, notes, modifications, note_body ) SELECT new_sec, sf.order_index, 'exercise'::character varying(20), sf.exercise_id, sf.exercise_variant_id, NULL::integer, NULL::integer, NULL::text, NULL::text, NULL::text FROM training_framework_slot_exercises sf WHERE sf.slot_id = rec.sid ORDER BY sf.order_index; END LOOP; END $$; DROP TABLE IF EXISTS training_framework_slot_exercises; ALTER TABLE training_units DROP CONSTRAINT IF EXISTS chk_training_units_blueprint_vs_scheduled; ALTER TABLE training_units ADD CONSTRAINT chk_training_units_blueprint_vs_scheduled CHECK ( ( framework_slot_id IS NOT NULL AND group_id IS NULL AND planned_date IS NULL AND origin_framework_slot_id IS NULL ) OR ( framework_slot_id IS NULL AND group_id IS NOT NULL AND planned_date IS NOT NULL ) );