import { type NextRequest } from "next/server"; import { prisma } from "@/lib/prisma"; import { ok, errorResponse, requireRole, parseJson, ApiError } from "@/lib/api"; import { UpdateFastenerSchema } from "@/lib/schemas"; import { audit } from "@/lib/audit"; import { clientIp } from "@/lib/request"; export async function GET(_req: NextRequest, ctx: { params: Promise<{ id: string }> }) { try { await requireRole("admin"); const { id } = await ctx.params; const f = await prisma.fastener.findUnique({ where: { id } }); if (!f) throw new ApiError(404, "not_found", "Fastener not found"); return ok({ fastener: f }); } catch (err) { return errorResponse(err); } } export async function PATCH(req: NextRequest, ctx: { params: Promise<{ id: string }> }) { try { const actor = await requireRole("admin"); const { id } = await ctx.params; const before = await prisma.fastener.findUnique({ where: { id } }); if (!before) throw new ApiError(404, "not_found", "Fastener not found"); const body = await parseJson(req, UpdateFastenerSchema); const after = await prisma.fastener.update({ where: { id }, data: body }); await audit({ actorId: actor.id, action: "update", entity: "Fastener", entityId: id, before, after, ipAddress: clientIp(req), }); return ok({ fastener: after }); } catch (err) { return errorResponse(err); } } /** * Delete only when no PO lines reference the fastener. If there are lines * (even on cancelled POs) we refuse, because removing the fastener would * orphan historical receipt records. Admin can zero the qty + flag the * description instead if they want to retire it. */ export async function DELETE(req: NextRequest, ctx: { params: Promise<{ id: string }> }) { try { const actor = await requireRole("admin"); const { id } = await ctx.params; const before = await prisma.fastener.findUnique({ where: { id }, include: { _count: { select: { poLines: true } } }, }); if (!before) throw new ApiError(404, "not_found", "Fastener not found"); if (before._count.poLines > 0) { throw new ApiError( 409, "fastener_in_use", "Fastener is referenced by purchase order lines; cancel them first", ); } await prisma.fastener.delete({ where: { id } }); await audit({ actorId: actor.id, action: "delete", entity: "Fastener", entityId: id, before, ipAddress: clientIp(req), }); return ok({ ok: true }); } catch (err) { return errorResponse(err); } }