/** * Backfill Organization IDs Script * * Populates the new organizationId columns on Deliverable and DeliverableStage * by copying from their parent Project's organizationId. * * Run with: npx tsx scripts/backfill-org-ids.ts */ import "dotenv/config"; import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaClient } from "../src/generated/prisma/client"; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! }); const prisma = new PrismaClient({ adapter }); async function main() { console.log("Backfilling organizationId on deliverables..."); // Get all deliverables missing organizationId const deliverables = await prisma.deliverable.findMany({ where: { organizationId: null }, select: { id: true, projectId: true }, }); if (deliverables.length === 0) { console.log("No deliverables need backfill."); } else { // Group by projectId for efficiency const projectIds = [...new Set(deliverables.map((d) => d.projectId))]; const projects = await prisma.project.findMany({ where: { id: { in: projectIds } }, select: { id: true, organizationId: true }, }); const projectOrgMap = new Map(projects.map((p) => [p.id, p.organizationId])); let updated = 0; for (const deliverable of deliverables) { const orgId = projectOrgMap.get(deliverable.projectId); if (!orgId) { console.warn(` Skipping deliverable ${deliverable.id} — project ${deliverable.projectId} has no org`); continue; } await prisma.deliverable.update({ where: { id: deliverable.id }, data: { organizationId: orgId }, }); updated++; } console.log(` Updated ${updated}/${deliverables.length} deliverables.`); } console.log("Backfilling organizationId on deliverable stages..."); const stages = await prisma.deliverableStage.findMany({ where: { organizationId: null }, select: { id: true, deliverable: { select: { project: { select: { organizationId: true } } }, }, }, }); if (stages.length === 0) { console.log("No stages need backfill."); } else { let updated = 0; // Batch update for efficiency const batchSize = 100; for (let i = 0; i < stages.length; i += batchSize) { const batch = stages.slice(i, i + batchSize); await prisma.$transaction( batch.map((stage) => prisma.deliverableStage.update({ where: { id: stage.id }, data: { organizationId: stage.deliverable.project.organizationId }, }) ) ); updated += batch.length; if (updated % 500 === 0) { console.log(` Progress: ${updated}/${stages.length}`); } } console.log(` Updated ${updated}/${stages.length} stages.`); } console.log("Backfill complete!"); } main() .catch((e) => { console.error(e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });