From 68d3dde1aa84c158429b655406fbcedfc8237d00 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 13 Feb 2026 16:19:56 +0100 Subject: [PATCH] Add migration for pipeline/stage system, new enums, and Round retirement The Round system redesign commit added Pipeline, Track, Stage models and converted roundId to legacy fields but was missing the SQL migration. This caused seed failure (AWARD_MASTER enum not found in DB). Co-Authored-By: Claude Opus 4.6 --- .../migration.sql | 445 ++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 prisma/migrations/20260213000000_add_pipeline_stage_system/migration.sql diff --git a/prisma/migrations/20260213000000_add_pipeline_stage_system/migration.sql b/prisma/migrations/20260213000000_add_pipeline_stage_system/migration.sql new file mode 100644 index 0000000..332c641 --- /dev/null +++ b/prisma/migrations/20260213000000_add_pipeline_stage_system/migration.sql @@ -0,0 +1,445 @@ +-- ============================================================================= +-- Migration: Pipeline & Stage System + Legacy roundId → stageId transition +-- ============================================================================= + +-- ===================================================== +-- 1. NEW ENUM TYPES +-- ===================================================== + +CREATE TYPE "StageType" AS ENUM ('INTAKE', 'FILTER', 'EVALUATION', 'SELECTION', 'LIVE_FINAL', 'RESULTS'); +CREATE TYPE "TrackKind" AS ENUM ('MAIN', 'AWARD', 'SHOWCASE'); +CREATE TYPE "RoutingMode" AS ENUM ('PARALLEL', 'EXCLUSIVE', 'POST_MAIN'); +CREATE TYPE "StageStatus" AS ENUM ('STAGE_DRAFT', 'STAGE_ACTIVE', 'STAGE_CLOSED', 'STAGE_ARCHIVED'); +CREATE TYPE "ProjectStageStateValue" AS ENUM ('PENDING', 'IN_PROGRESS', 'PASSED', 'REJECTED', 'ROUTED', 'COMPLETED', 'WITHDRAWN'); +CREATE TYPE "DecisionMode" AS ENUM ('JURY_VOTE', 'AWARD_MASTER_DECISION', 'ADMIN_DECISION'); +CREATE TYPE "OverrideReasonCode" AS ENUM ('DATA_CORRECTION', 'POLICY_EXCEPTION', 'JURY_CONFLICT', 'SPONSOR_DECISION', 'ADMIN_DISCRETION'); + +-- Add new values to existing UserRole enum +ALTER TYPE "UserRole" ADD VALUE 'AWARD_MASTER'; +ALTER TYPE "UserRole" ADD VALUE 'AUDIENCE'; + +-- ===================================================== +-- 2. NEW TABLES: Pipeline infrastructure +-- ===================================================== + +-- Pipeline +CREATE TABLE "Pipeline" ( + "id" TEXT NOT NULL, + "programId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'DRAFT', + "settingsJson" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Pipeline_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "Pipeline_slug_key" ON "Pipeline"("slug"); +CREATE INDEX "Pipeline_programId_idx" ON "Pipeline"("programId"); +CREATE INDEX "Pipeline_status_idx" ON "Pipeline"("status"); + +-- Track +CREATE TABLE "Track" ( + "id" TEXT NOT NULL, + "pipelineId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "kind" "TrackKind" NOT NULL DEFAULT 'MAIN', + "routingMode" "RoutingMode", + "decisionMode" "DecisionMode", + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "settingsJson" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Track_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "Track_pipelineId_slug_key" ON "Track"("pipelineId", "slug"); +CREATE UNIQUE INDEX "Track_pipelineId_sortOrder_key" ON "Track"("pipelineId", "sortOrder"); +CREATE INDEX "Track_pipelineId_idx" ON "Track"("pipelineId"); +CREATE INDEX "Track_kind_idx" ON "Track"("kind"); + +-- Stage +CREATE TABLE "Stage" ( + "id" TEXT NOT NULL, + "trackId" TEXT NOT NULL, + "stageType" "StageType" NOT NULL, + "name" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "status" "StageStatus" NOT NULL DEFAULT 'STAGE_DRAFT', + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "configJson" JSONB, + "windowOpenAt" TIMESTAMP(3), + "windowCloseAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Stage_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "Stage_trackId_slug_key" ON "Stage"("trackId", "slug"); +CREATE UNIQUE INDEX "Stage_trackId_sortOrder_key" ON "Stage"("trackId", "sortOrder"); +CREATE INDEX "Stage_trackId_idx" ON "Stage"("trackId"); +CREATE INDEX "Stage_stageType_idx" ON "Stage"("stageType"); +CREATE INDEX "Stage_status_idx" ON "Stage"("status"); + +-- StageTransition +CREATE TABLE "StageTransition" ( + "id" TEXT NOT NULL, + "fromStageId" TEXT NOT NULL, + "toStageId" TEXT NOT NULL, + "isDefault" BOOLEAN NOT NULL DEFAULT false, + "guardJson" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "StageTransition_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "StageTransition_fromStageId_toStageId_key" ON "StageTransition"("fromStageId", "toStageId"); +CREATE INDEX "StageTransition_fromStageId_idx" ON "StageTransition"("fromStageId"); +CREATE INDEX "StageTransition_toStageId_idx" ON "StageTransition"("toStageId"); + +-- ProjectStageState +CREATE TABLE "ProjectStageState" ( + "id" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "trackId" TEXT NOT NULL, + "stageId" TEXT NOT NULL, + "state" "ProjectStageStateValue" NOT NULL DEFAULT 'PENDING', + "enteredAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "exitedAt" TIMESTAMP(3), + "metadataJson" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ProjectStageState_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "ProjectStageState_projectId_trackId_stageId_key" ON "ProjectStageState"("projectId", "trackId", "stageId"); +CREATE INDEX "ProjectStageState_projectId_idx" ON "ProjectStageState"("projectId"); +CREATE INDEX "ProjectStageState_trackId_idx" ON "ProjectStageState"("trackId"); +CREATE INDEX "ProjectStageState_stageId_idx" ON "ProjectStageState"("stageId"); +CREATE INDEX "ProjectStageState_state_idx" ON "ProjectStageState"("state"); +CREATE INDEX "ProjectStageState_projectId_trackId_idx" ON "ProjectStageState"("projectId", "trackId"); + +-- RoutingRule +CREATE TABLE "RoutingRule" ( + "id" TEXT NOT NULL, + "pipelineId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "scope" TEXT NOT NULL DEFAULT 'global', + "sourceTrackId" TEXT, + "destinationTrackId" TEXT NOT NULL, + "destinationStageId" TEXT, + "predicateJson" JSONB NOT NULL, + "priority" INTEGER NOT NULL DEFAULT 0, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "RoutingRule_pkey" PRIMARY KEY ("id") +); + +CREATE INDEX "RoutingRule_pipelineId_idx" ON "RoutingRule"("pipelineId"); +CREATE INDEX "RoutingRule_priority_idx" ON "RoutingRule"("priority"); +CREATE INDEX "RoutingRule_isActive_idx" ON "RoutingRule"("isActive"); + +-- Cohort +CREATE TABLE "Cohort" ( + "id" TEXT NOT NULL, + "stageId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "votingMode" TEXT NOT NULL DEFAULT 'simple', + "isOpen" BOOLEAN NOT NULL DEFAULT false, + "windowOpenAt" TIMESTAMP(3), + "windowCloseAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Cohort_pkey" PRIMARY KEY ("id") +); + +CREATE INDEX "Cohort_stageId_idx" ON "Cohort"("stageId"); +CREATE INDEX "Cohort_isOpen_idx" ON "Cohort"("isOpen"); + +-- CohortProject +CREATE TABLE "CohortProject" ( + "id" TEXT NOT NULL, + "cohortId" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CohortProject_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "CohortProject_cohortId_projectId_key" ON "CohortProject"("cohortId", "projectId"); +CREATE INDEX "CohortProject_cohortId_idx" ON "CohortProject"("cohortId"); +CREATE INDEX "CohortProject_projectId_idx" ON "CohortProject"("projectId"); +CREATE INDEX "CohortProject_sortOrder_idx" ON "CohortProject"("sortOrder"); + +-- LiveProgressCursor +CREATE TABLE "LiveProgressCursor" ( + "id" TEXT NOT NULL, + "stageId" TEXT NOT NULL, + "sessionId" TEXT NOT NULL, + "activeProjectId" TEXT, + "activeOrderIndex" INTEGER NOT NULL DEFAULT 0, + "isPaused" BOOLEAN NOT NULL DEFAULT false, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "LiveProgressCursor_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "LiveProgressCursor_stageId_key" ON "LiveProgressCursor"("stageId"); +CREATE UNIQUE INDEX "LiveProgressCursor_sessionId_key" ON "LiveProgressCursor"("sessionId"); +CREATE INDEX "LiveProgressCursor_sessionId_idx" ON "LiveProgressCursor"("sessionId"); + +-- OverrideAction +CREATE TABLE "OverrideAction" ( + "id" TEXT NOT NULL, + "entityType" TEXT NOT NULL, + "entityId" TEXT NOT NULL, + "previousValue" JSONB, + "newValueJson" JSONB NOT NULL, + "reasonCode" "OverrideReasonCode" NOT NULL, + "reasonText" TEXT, + "actorId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "OverrideAction_pkey" PRIMARY KEY ("id") +); + +CREATE INDEX "OverrideAction_entityType_entityId_idx" ON "OverrideAction"("entityType", "entityId"); +CREATE INDEX "OverrideAction_actorId_idx" ON "OverrideAction"("actorId"); +CREATE INDEX "OverrideAction_reasonCode_idx" ON "OverrideAction"("reasonCode"); +CREATE INDEX "OverrideAction_createdAt_idx" ON "OverrideAction"("createdAt"); + +-- DecisionAuditLog +CREATE TABLE "DecisionAuditLog" ( + "id" TEXT NOT NULL, + "eventType" TEXT NOT NULL, + "entityType" TEXT NOT NULL, + "entityId" TEXT NOT NULL, + "actorId" TEXT, + "detailsJson" JSONB, + "snapshotJson" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "DecisionAuditLog_pkey" PRIMARY KEY ("id") +); + +CREATE INDEX "DecisionAuditLog_eventType_idx" ON "DecisionAuditLog"("eventType"); +CREATE INDEX "DecisionAuditLog_entityType_entityId_idx" ON "DecisionAuditLog"("entityType", "entityId"); +CREATE INDEX "DecisionAuditLog_actorId_idx" ON "DecisionAuditLog"("actorId"); +CREATE INDEX "DecisionAuditLog_createdAt_idx" ON "DecisionAuditLog"("createdAt"); + +-- NotificationPolicy +CREATE TABLE "NotificationPolicy" ( + "id" TEXT NOT NULL, + "eventType" TEXT NOT NULL, + "channel" TEXT NOT NULL DEFAULT 'EMAIL', + "templateId" TEXT, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "configJson" JSONB, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "NotificationPolicy_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "NotificationPolicy_eventType_key" ON "NotificationPolicy"("eventType"); +CREATE INDEX "NotificationPolicy_eventType_idx" ON "NotificationPolicy"("eventType"); +CREATE INDEX "NotificationPolicy_isActive_idx" ON "NotificationPolicy"("isActive"); + +-- ===================================================== +-- 3. DROP roundId FK constraints (roundId is now a raw legacy field) +-- ===================================================== + +ALTER TABLE "EvaluationForm" DROP CONSTRAINT IF EXISTS "EvaluationForm_roundId_fkey"; +ALTER TABLE "Assignment" DROP CONSTRAINT IF EXISTS "Assignment_roundId_fkey"; +ALTER TABLE "GracePeriod" DROP CONSTRAINT IF EXISTS "GracePeriod_roundId_fkey"; +ALTER TABLE "FileRequirement" DROP CONSTRAINT IF EXISTS "FileRequirement_roundId_fkey"; +ALTER TABLE "FilteringRule" DROP CONSTRAINT IF EXISTS "FilteringRule_roundId_fkey"; +ALTER TABLE "FilteringResult" DROP CONSTRAINT IF EXISTS "FilteringResult_roundId_fkey"; +ALTER TABLE "FilteringJob" DROP CONSTRAINT IF EXISTS "FilteringJob_roundId_fkey"; +ALTER TABLE "AssignmentJob" DROP CONSTRAINT IF EXISTS "AssignmentJob_roundId_fkey"; +ALTER TABLE "ReminderLog" DROP CONSTRAINT IF EXISTS "ReminderLog_roundId_fkey"; +ALTER TABLE "ConflictOfInterest" DROP CONSTRAINT IF EXISTS "ConflictOfInterest_roundId_fkey"; +ALTER TABLE "EvaluationSummary" DROP CONSTRAINT IF EXISTS "EvaluationSummary_roundId_fkey"; +ALTER TABLE "EvaluationDiscussion" DROP CONSTRAINT IF EXISTS "EvaluationDiscussion_roundId_fkey"; +ALTER TABLE "LiveVotingSession" DROP CONSTRAINT IF EXISTS "LiveVotingSession_roundId_fkey"; +ALTER TABLE "Project" DROP CONSTRAINT IF EXISTS "Project_roundId_fkey"; +ALTER TABLE "ProjectFile" DROP CONSTRAINT IF EXISTS "ProjectFile_roundId_fkey"; +ALTER TABLE "TaggingJob" DROP CONSTRAINT IF EXISTS "TaggingJob_roundId_fkey"; +ALTER TABLE "Message" DROP CONSTRAINT IF EXISTS "Message_roundId_fkey"; + +-- Make remaining roundId columns nullable (those that were NOT NULL) +ALTER TABLE "TaggingJob" ALTER COLUMN "roundId" DROP NOT NULL; + +-- Drop Round table and its enums (model retired in Phase 6) +DROP TABLE IF EXISTS "Round" CASCADE; +DROP TYPE IF EXISTS "RoundStatus"; +DROP TYPE IF EXISTS "RoundType"; + +-- ===================================================== +-- 4. ALTER EXISTING TABLES: Make roundId nullable, add stageId +-- ===================================================== + +-- EvaluationForm: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "EvaluationForm" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "EvaluationForm" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "EvaluationForm" ALTER COLUMN "stageId" DROP DEFAULT; +DROP INDEX "EvaluationForm_roundId_version_key"; +CREATE UNIQUE INDEX "EvaluationForm_stageId_version_key" ON "EvaluationForm"("stageId", "version"); +CREATE INDEX "EvaluationForm_stageId_isActive_idx" ON "EvaluationForm"("stageId", "isActive"); + +-- Assignment: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "Assignment" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "Assignment" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "Assignment" ALTER COLUMN "stageId" DROP DEFAULT; +DROP INDEX "Assignment_userId_projectId_roundId_key"; +CREATE UNIQUE INDEX "Assignment_userId_projectId_stageId_key" ON "Assignment"("userId", "projectId", "stageId"); +CREATE INDEX "Assignment_stageId_idx" ON "Assignment"("stageId"); + +-- GracePeriod: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "GracePeriod" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "GracePeriod" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "GracePeriod" ALTER COLUMN "stageId" DROP DEFAULT; +CREATE INDEX "GracePeriod_stageId_idx" ON "GracePeriod"("stageId"); +CREATE INDEX "GracePeriod_stageId_userId_extendedUntil_idx" ON "GracePeriod"("stageId", "userId", "extendedUntil"); + +-- FileRequirement: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "FileRequirement" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "FileRequirement" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "FileRequirement" ALTER COLUMN "stageId" DROP DEFAULT; +CREATE INDEX "FileRequirement_stageId_idx" ON "FileRequirement"("stageId"); + +-- FilteringRule: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "FilteringRule" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "FilteringRule" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "FilteringRule" ALTER COLUMN "stageId" DROP DEFAULT; +CREATE INDEX "FilteringRule_stageId_idx" ON "FilteringRule"("stageId"); + +-- FilteringResult: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "FilteringResult" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "FilteringResult" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "FilteringResult" ALTER COLUMN "stageId" DROP DEFAULT; +DROP INDEX "FilteringResult_roundId_projectId_key"; +CREATE UNIQUE INDEX "FilteringResult_stageId_projectId_key" ON "FilteringResult"("stageId", "projectId"); +CREATE INDEX "FilteringResult_stageId_idx" ON "FilteringResult"("stageId"); + +-- FilteringJob: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "FilteringJob" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "FilteringJob" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "FilteringJob" ALTER COLUMN "stageId" DROP DEFAULT; +CREATE INDEX "FilteringJob_stageId_idx" ON "FilteringJob"("stageId"); + +-- AssignmentJob: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "AssignmentJob" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "AssignmentJob" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "AssignmentJob" ALTER COLUMN "stageId" DROP DEFAULT; +CREATE INDEX "AssignmentJob_stageId_idx" ON "AssignmentJob"("stageId"); + +-- ReminderLog: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "ReminderLog" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "ReminderLog" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "ReminderLog" ALTER COLUMN "stageId" DROP DEFAULT; +DROP INDEX "ReminderLog_roundId_userId_type_key"; +CREATE UNIQUE INDEX "ReminderLog_stageId_userId_type_key" ON "ReminderLog"("stageId", "userId", "type"); +CREATE INDEX "ReminderLog_stageId_idx" ON "ReminderLog"("stageId"); + +-- ConflictOfInterest: roundId NOT NULL → nullable (no stageId on this table) +ALTER TABLE "ConflictOfInterest" ALTER COLUMN "roundId" DROP NOT NULL; + +-- EvaluationSummary: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "EvaluationSummary" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "EvaluationSummary" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "EvaluationSummary" ALTER COLUMN "stageId" DROP DEFAULT; +DROP INDEX "EvaluationSummary_projectId_roundId_key"; +CREATE UNIQUE INDEX "EvaluationSummary_projectId_stageId_key" ON "EvaluationSummary"("projectId", "stageId"); +CREATE INDEX "EvaluationSummary_stageId_idx" ON "EvaluationSummary"("stageId"); + +-- EvaluationDiscussion: roundId NOT NULL → nullable, add stageId NOT NULL +ALTER TABLE "EvaluationDiscussion" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "EvaluationDiscussion" ADD COLUMN "stageId" TEXT NOT NULL DEFAULT '__placeholder__'; +ALTER TABLE "EvaluationDiscussion" ALTER COLUMN "stageId" DROP DEFAULT; +DROP INDEX "EvaluationDiscussion_projectId_roundId_key"; +CREATE UNIQUE INDEX "EvaluationDiscussion_projectId_stageId_key" ON "EvaluationDiscussion"("projectId", "stageId"); +CREATE INDEX "EvaluationDiscussion_stageId_idx" ON "EvaluationDiscussion"("stageId"); + +-- Message: add stageId (nullable) +ALTER TABLE "Message" ADD COLUMN "stageId" TEXT; +CREATE INDEX "Message_stageId_idx" ON "Message"("stageId"); + +-- LiveVotingSession: roundId NOT NULL → nullable, add stageId (nullable, unique) +ALTER TABLE "LiveVotingSession" ALTER COLUMN "roundId" DROP NOT NULL; +ALTER TABLE "LiveVotingSession" ADD COLUMN "stageId" TEXT; +CREATE UNIQUE INDEX "LiveVotingSession_stageId_key" ON "LiveVotingSession"("stageId"); + +-- ===================================================== +-- 4. FOREIGN KEY CONSTRAINTS: New tables +-- ===================================================== + +-- Pipeline +ALTER TABLE "Pipeline" ADD CONSTRAINT "Pipeline_programId_fkey" FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- Track +ALTER TABLE "Track" ADD CONSTRAINT "Track_pipelineId_fkey" FOREIGN KEY ("pipelineId") REFERENCES "Pipeline"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- Stage +ALTER TABLE "Stage" ADD CONSTRAINT "Stage_trackId_fkey" FOREIGN KEY ("trackId") REFERENCES "Track"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- StageTransition +ALTER TABLE "StageTransition" ADD CONSTRAINT "StageTransition_fromStageId_fkey" FOREIGN KEY ("fromStageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "StageTransition" ADD CONSTRAINT "StageTransition_toStageId_fkey" FOREIGN KEY ("toStageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- ProjectStageState +ALTER TABLE "ProjectStageState" ADD CONSTRAINT "ProjectStageState_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "ProjectStageState" ADD CONSTRAINT "ProjectStageState_trackId_fkey" FOREIGN KEY ("trackId") REFERENCES "Track"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "ProjectStageState" ADD CONSTRAINT "ProjectStageState_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- RoutingRule +ALTER TABLE "RoutingRule" ADD CONSTRAINT "RoutingRule_pipelineId_fkey" FOREIGN KEY ("pipelineId") REFERENCES "Pipeline"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "RoutingRule" ADD CONSTRAINT "RoutingRule_sourceTrackId_fkey" FOREIGN KEY ("sourceTrackId") REFERENCES "Track"("id") ON DELETE SET NULL ON UPDATE CASCADE; +ALTER TABLE "RoutingRule" ADD CONSTRAINT "RoutingRule_destinationTrackId_fkey" FOREIGN KEY ("destinationTrackId") REFERENCES "Track"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- Cohort +ALTER TABLE "Cohort" ADD CONSTRAINT "Cohort_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- CohortProject +ALTER TABLE "CohortProject" ADD CONSTRAINT "CohortProject_cohortId_fkey" FOREIGN KEY ("cohortId") REFERENCES "Cohort"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "CohortProject" ADD CONSTRAINT "CohortProject_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- LiveProgressCursor +ALTER TABLE "LiveProgressCursor" ADD CONSTRAINT "LiveProgressCursor_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- ===================================================== +-- 5. FOREIGN KEY CONSTRAINTS: stageId on altered tables +-- ===================================================== + +ALTER TABLE "EvaluationForm" ADD CONSTRAINT "EvaluationForm_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "Assignment" ADD CONSTRAINT "Assignment_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "GracePeriod" ADD CONSTRAINT "GracePeriod_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "FileRequirement" ADD CONSTRAINT "FileRequirement_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "FilteringRule" ADD CONSTRAINT "FilteringRule_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "FilteringResult" ADD CONSTRAINT "FilteringResult_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "FilteringJob" ADD CONSTRAINT "FilteringJob_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "AssignmentJob" ADD CONSTRAINT "AssignmentJob_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "ReminderLog" ADD CONSTRAINT "ReminderLog_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "EvaluationSummary" ADD CONSTRAINT "EvaluationSummary_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "EvaluationDiscussion" ADD CONSTRAINT "EvaluationDiscussion_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "Message" ADD CONSTRAINT "Message_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE SET NULL ON UPDATE CASCADE; +ALTER TABLE "LiveVotingSession" ADD CONSTRAINT "LiveVotingSession_stageId_fkey" FOREIGN KEY ("stageId") REFERENCES "Stage"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- ===================================================== +-- 6. SpecialAward: add trackId (nullable, unique) +-- ===================================================== + +ALTER TABLE "SpecialAward" ADD COLUMN "trackId" TEXT; +CREATE UNIQUE INDEX "SpecialAward_trackId_key" ON "SpecialAward"("trackId"); +ALTER TABLE "SpecialAward" ADD CONSTRAINT "SpecialAward_trackId_fkey" FOREIGN KEY ("trackId") REFERENCES "Track"("id") ON DELETE SET NULL ON UPDATE CASCADE;