From 811b2dca1ca795a61ccc7f221c1c3e175a3f5011 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Fri, 31 Jul 2026 13:40:06 +0200 Subject: [PATCH] feat(stock-quick-draft): PATCH /api/v1/stock-quick-draft/line/[lineId] Adds a PATCH handler next to the existing DELETE handler. Validates JSON body, maps service result reasons to HTTP status codes, and uses admin PB to bypass user-side collection rules. Co-authored-by: Orca --- .../stock-quick-draft/line/[lineId]/route.ts | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/app/api/v1/stock-quick-draft/line/[lineId]/route.ts b/src/app/api/v1/stock-quick-draft/line/[lineId]/route.ts index c0c8417..5b3cee2 100644 --- a/src/app/api/v1/stock-quick-draft/line/[lineId]/route.ts +++ b/src/app/api/v1/stock-quick-draft/line/[lineId]/route.ts @@ -1,6 +1,10 @@ import { NextRequest, NextResponse } from "next/server"; import { getAdminPocketBase } from "@/lib/server/pocketbase-admin"; -import { removeQuickDraftLine } from "@/lib/server/stock-quick-draft-service"; +import { + removeQuickDraftLine, + updateQuickDraftLine, + type UpdateQuickDraftLineResult, +} from "@/lib/server/stock-quick-draft-service"; /** * DELETE /api/v1/stock-quick-draft/line/[lineId] @@ -44,3 +48,74 @@ export async function DELETE( ); } } + +type FailureReason = Extract["reason"]; +const STATUS_BY_REASON: Record = { + validation: 400, + line_not_found: 404, + update_failed: 500, +}; + +/** + * PATCH /api/v1/stock-quick-draft/line/[lineId] + * + * Updates `quantity` and/or `cost_price` on a stock_purchase_items + * row. Backed by `updateQuickDraftLine` in the service module. + * + * Request body: { quantity?: number (>0), costPrice?: number (>=0) } + * At least one of the two fields is required. + * + * Response (success): { ok: true } + * Error mapping (reason → status): + * "validation" → 400 + * "line_not_found" → 404 + * "update_failed" → 500 + */ +export async function PATCH( + req: NextRequest, + ctx: { params: Promise<{ lineId: string }> }, +) { + const { lineId } = await ctx.params; + if (!lineId) { + return NextResponse.json( + { ok: false, reason: "validation", message: "lineId is required" }, + { status: 400 }, + ); + } + + let body: Record; + try { + body = (await req.json()) as Record; + } catch { + return NextResponse.json( + { ok: false, reason: "validation", message: "invalid JSON body" }, + { status: 400 }, + ); + } + + const quantity = typeof body.quantity === "number" ? body.quantity : undefined; + const costPrice = typeof body.costPrice === "number" ? body.costPrice : undefined; + + try { + const pb = await getAdminPocketBase(); + const result = await updateQuickDraftLine(pb as never, { + lineId, + quantity, + costPrice, + }); + if (!result.ok) { + const status = STATUS_BY_REASON[result.reason] ?? 500; + return NextResponse.json(result, { status }); + } + return NextResponse.json(result); + } catch (err) { + return NextResponse.json( + { + ok: false, + reason: "update_failed", + message: err instanceof Error ? err.message : "unknown error", + }, + { status: 500 }, + ); + } +}