Files
mrp-qrcode/app/api/v1/operations/[id]/qc-reset/route.ts
T
jason e0dfac2d48
Build and Push Docker Image / build (push) Successful in 1m4s
step 9 and cleanup
2026-04-22 09:27:01 -05:00

62 lines
2.0 KiB
TypeScript

import { type NextRequest } from "next/server";
import { prisma } from "@/lib/prisma";
import { ok, errorResponse, requireRole, ApiError } from "@/lib/api";
import { audit } from "@/lib/audit";
import { clientIp } from "@/lib/request";
/**
* Admin-only: clear a QC failure so the step can be reworked. We roll the
* op back to either `pending` (nothing produced yet — the failed unit was
* the first attempt) or `partial` (some units had already been logged as
* good before the fail) based on the cumulative `unitsCompleted` counter.
*
* The failed QCRecord is intentionally LEFT IN PLACE so reporting can still
* count rework events — we just unblock the op.
*/
export async function POST(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
try {
const actor = await requireRole("admin");
const { id } = await ctx.params;
const existing = await prisma.operation.findUnique({
where: { id },
select: { id: true, status: true, unitsCompleted: true },
});
if (!existing) throw new ApiError(404, "not_found", "Operation not found");
if (existing.status !== "qc_failed") {
throw new ApiError(
409,
"op_not_qc_failed",
"Only steps in qc_failed state can be reset",
);
}
const nextStatus = existing.unitsCompleted > 0 ? "partial" : "pending";
const updated = await prisma.operation.update({
where: { id },
data: {
status: nextStatus,
// Claim was already cleared on the failing close; belt-and-braces
// defensively zero it here in case something set it back somehow.
claimedByUserId: null,
claimedAt: null,
completedAt: null,
},
});
await audit({
actorId: actor.id,
action: "qc_reset_op",
entity: "Operation",
entityId: id,
before: { status: "qc_failed" },
after: { status: nextStatus, unitsCompleted: existing.unitsCompleted },
ipAddress: clientIp(req),
});
return ok({ operation: updated });
} catch (err) {
return errorResponse(err);
}
}