feat(stock): add POST /api/v1/stock/items/set-pricing
This commit is contained in:
parent
ab639eea6d
commit
6f8c6da0fe
1 changed files with 78 additions and 0 deletions
78
src/app/api/v1/stock/items/set-pricing/route.ts
Normal file
78
src/app/api/v1/stock/items/set-pricing/route.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { updateStockItemsPricing } from "@/lib/server/stock-pricing-service";
|
||||||
|
import { getAdminPocketBase } from "@/lib/server/pocketbase-admin";
|
||||||
|
|
||||||
|
const MAX_UPDATES = 100;
|
||||||
|
|
||||||
|
const PB_ID_RE = /^[a-z0-9]{15}$/;
|
||||||
|
|
||||||
|
type Body = {
|
||||||
|
updates?: Array<{ id?: unknown; costPrice?: unknown; sellingPrice?: unknown; markup?: unknown }>;
|
||||||
|
actor?: { id?: unknown; name?: unknown };
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function POST(req: Request) {
|
||||||
|
let body: Body;
|
||||||
|
try {
|
||||||
|
body = (await req.json()) as Body;
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ code: "invalid_json", message: "Body is not valid JSON" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(body.updates) || body.updates.length === 0) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ code: "validation_error", message: "updates must be a non-empty array" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (body.updates.length > MAX_UPDATES) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
code: "validation_error",
|
||||||
|
message: `At most ${MAX_UPDATES} updates per call`,
|
||||||
|
},
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!body.actor || typeof body.actor.id !== "string" || !body.actor.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ code: "validation_error", message: "actor.id is required" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updates = body.updates.map((u, i) => {
|
||||||
|
if (typeof u.id !== "string" || !PB_ID_RE.test(u.id)) {
|
||||||
|
throw new Error(`updates[${i}].id must be a 15-char PB id`);
|
||||||
|
}
|
||||||
|
const out: { id: string; costPrice?: number; sellingPrice?: number; markup?: number } = {
|
||||||
|
id: u.id,
|
||||||
|
};
|
||||||
|
if (u.costPrice !== undefined) out.costPrice = Number(u.costPrice);
|
||||||
|
if (u.sellingPrice !== undefined) out.sellingPrice = Number(u.sellingPrice);
|
||||||
|
if (u.markup !== undefined) out.markup = Number(u.markup);
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
|
||||||
|
let pb;
|
||||||
|
try {
|
||||||
|
pb = await getAdminPocketBase();
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ code: "pb_error", message },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await updateStockItemsPricing({
|
||||||
|
pb,
|
||||||
|
updates,
|
||||||
|
actor: { id: body.actor.id, name: String(body.actor.name ?? body.actor.id) },
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json(result, { status: 200 });
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue