From 33a36d616b2295fa53ea5984be7d81c17980453d Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Thu, 7 May 2026 15:12:52 +0200 Subject: [PATCH] feat(ST-cmovhvegf): add existingPbi pre-check in materializeIdeaPlanAction - Adds options.allowAlongside parameter to control behaviour when a PBI with executed tasks already exists. - Returns 409 PBI_HAS_ACTIVE_TASKS: when tasks are DONE/IN_PROGRESS and allowAlongside is not set. - Auto-deletes the old PBI inside the transaction when no tasks have been executed (atomic replace). - Alongside mode (allowAlongside=true) skips deletion and creates a new PBI. --- actions/ideas.ts | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/actions/ideas.ts b/actions/ideas.ts index 1cdab96..859457b 100644 --- a/actions/ideas.ts +++ b/actions/ideas.ts @@ -540,6 +540,7 @@ function nextNumber(existing: (string | null)[], re: RegExp): number { export async function materializeIdeaPlanAction( id: string, + options?: { allowAlongside?: boolean }, ): Promise> { const session = await getSession() if (!session.userId) return { error: 'Niet ingelogd', code: 401 } @@ -550,7 +551,7 @@ export async function materializeIdeaPlanAction( const idea = await prisma.idea.findFirst({ where: { id, user_id: session.userId }, - select: { id: true, status: true, product_id: true, plan_md: true }, + select: { id: true, status: true, product_id: true, plan_md: true, pbi_id: true }, }) if (!idea) return { error: 'Idee niet gevonden', code: 404 } if (idea.status !== 'PLAN_READY') { @@ -574,8 +575,36 @@ export async function materializeIdeaPlanAction( const productId = idea.product_id const plan = parsed.plan + let oldPbiId: string | null = null + if (idea.pbi_id) { + const executedCount = await prisma.task.count({ + where: { + story: { pbi_id: idea.pbi_id }, + status: { in: ['DONE', 'IN_PROGRESS'] }, + }, + }) + if (executedCount > 0 && !options?.allowAlongside) { + const existingPbi = await prisma.pbi.findUnique({ + where: { id: idea.pbi_id }, + select: { code: true }, + }) + return { + error: `PBI_HAS_ACTIVE_TASKS:${existingPbi?.code ?? idea.pbi_id}`, + code: 409, + } + } + if (executedCount === 0) { + oldPbiId = idea.pbi_id + } + // executedCount > 0 && allowAlongside: doorgaan zonder delete + } + try { const result = await prisma.$transaction(async (tx) => { + if (oldPbiId) { + await tx.pbi.delete({ where: { id: oldPbiId } }) + } + // Codes: één keer SELECT max per type binnen de transactie. Bij P2002 // (race met andere materialize) abort de transactie en gooien we 409. const [existingPbis, existingStories, existingTasks] = await Promise.all([