shinkan-jinkendo/backend/migrations/037_training_framework_blueprint_units.sql
Lars 7e21b44604
Some checks failed
Deploy Development / deploy (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Failing after 39s
chore: update versioning and enhance training unit features
- Incremented APP_VERSION to 0.8.10 and DB_SCHEMA_VERSION to 20260505037.
- Added changelog entry for version 0.8.10 detailing database and API changes.
- Refactored training framework program handling to support cloning training units from framework slots.
- Improved permission checks for training units based on framework slot associations.
- Introduced new API endpoint for creating training units from framework slots.
2026-05-05 13:31:26 +02:00

131 lines
4.2 KiB
SQL

-- 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
)
);