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;