createDeliverable: auto-mirror dynamic stages into legacy seed table
DeliverableStage.templateId is a NOT-NULL FK to pipeline_stage_templates (HP-era seed). On a fresh DB with no seed run, that table is empty — so even with a dynamic pipeline attached, the FK has nothing to point at and stage creation silently fails (deliverable lands with 0 stages). When createDeliverable detects this state, mirror each dynamic stage definition into pipeline_stage_templates (idempotent upsert on slug) so the FK is satisfied. No schema change; no operator action required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4aa711c7ae
commit
45dfdcad23
1 changed files with 40 additions and 1 deletions
|
|
@ -81,10 +81,49 @@ export async function createDeliverable(
|
|||
const dynamicStages = resolvedTemplate?.stages ?? [];
|
||||
|
||||
// Always fetch global templates (needed for templateId FK even with dynamic pipelines)
|
||||
const globalTemplates = await prisma.pipelineStageTemplate.findMany({
|
||||
let globalTemplates = await prisma.pipelineStageTemplate.findMany({
|
||||
include: { dependsOn: true },
|
||||
orderBy: { order: "asc" },
|
||||
});
|
||||
|
||||
// Bootstrap fallback: when the org has no legacy PipelineStageTemplate
|
||||
// seed rows (fresh DB, no seed run), the FK from DeliverableStage.templateId
|
||||
// has nothing to point at. Auto-create a 1:1 mirror of the dynamic
|
||||
// pipeline's stages so the FK is satisfied without forcing the user to
|
||||
// run a seed. Idempotent on slug + globally unique constraint.
|
||||
if (useDynamic && globalTemplates.length === 0) {
|
||||
for (const def of dynamicStages) {
|
||||
await prisma.pipelineStageTemplate
|
||||
.upsert({
|
||||
where: { slug: def.slug },
|
||||
create: {
|
||||
name: def.name,
|
||||
slug: def.slug,
|
||||
order: def.order,
|
||||
isCriticalGate: def.isCriticalGate,
|
||||
isOptional: def.isOptional,
|
||||
description: def.description,
|
||||
estimatedDays: def.estimatedDays,
|
||||
approvalType: def.approvalType,
|
||||
},
|
||||
update: {},
|
||||
})
|
||||
.catch((err) => {
|
||||
// Don't block deliverable creation on a seed-mirror miss —
|
||||
// log and move on; the fallback below uses the first existing
|
||||
// row as a last-resort templateId.
|
||||
console.error(
|
||||
"[deliverable-service] PipelineStageTemplate mirror upsert failed for slug",
|
||||
def.slug,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
globalTemplates = await prisma.pipelineStageTemplate.findMany({
|
||||
include: { dependsOn: true },
|
||||
orderBy: { order: "asc" },
|
||||
});
|
||||
}
|
||||
const globalBySlug = new Map(globalTemplates.map((t) => [t.slug, t]));
|
||||
|
||||
return prisma.$transaction(async (tx) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue