Files
homaroma/apps/backend/src/routes/raw-materials.ts
T

173 lines
4.7 KiB
TypeScript

import { Elysia, t } from "elysia";
import prisma from "../lib/prisma";
export const rawMaterialsRoute = new Elysia({ prefix: "/raw-materials" })
.get("/", async () => {
const rawMaterials = await prisma.rawMaterial.findMany({
orderBy: { name: "asc" },
});
return rawMaterials;
})
.get("/:id", async ({ params, status }) => {
const id = parseInt(params.id);
if (isNaN(id)) return status(400, { message: "Invalid ID" });
const rawMaterial = await prisma.rawMaterial.findUnique({
where: { id },
include: {
bomItems: {
include: {
productVariant: {
include: {
product: {
select: {
id: true,
name: true,
translations: {
select: { langCode: true, name: true }
}
}
}
}
}
}
}
}
});
if (!rawMaterial) return status(404, { message: "Raw material not found" });
const result = {
...rawMaterial,
bomItems: rawMaterial.bomItems.map((item) => {
const prod = item.productVariant?.product;
if (!prod) return item;
const thName = prod.translations?.find((t) => t.langCode === "th")?.name;
const fallbackName = prod.translations?.[0]?.name;
const resolvedName = prod.name || thName || fallbackName || `Product #${prod.id}`;
return {
...item,
productVariant: {
...item.productVariant,
product: {
...prod,
name: resolvedName,
}
}
};
})
};
return result;
})
.post("/bulk", async ({ body, status }) => {
try {
const items = Array.isArray(body) ? body : [body];
if (items.length === 0) {
return status(400, { message: "Payload must be a non-empty array" });
}
const results = await prisma.$transaction(async (tx) => {
const records = [];
for (const item of items) {
const name = String(item.name || "").trim();
if (!name) continue;
const unit = String(item.unit || "piece").trim() || "piece";
const rawPrice = item.pricePerUnit;
const pricePerUnit = typeof rawPrice === "string" ? (parseFloat(rawPrice) || 0) : Number(rawPrice) || 0;
const record = await tx.rawMaterial.upsert({
where: { name },
create: {
name,
unit,
pricePerUnit,
},
update: {
unit,
pricePerUnit,
},
});
records.push(record);
}
return records;
});
return status(200, {
success: true,
count: results.length,
items: results,
});
} catch (err: any) {
console.error("Bulk raw material import failed:", err);
return status(500, { message: "Failed to bulk import raw materials", error: err.message });
}
}, {
body: t.Array(
t.Object({
name: t.String(),
unit: t.Optional(t.String()),
pricePerUnit: t.Union([t.Number(), t.String()]),
})
),
detail: { tags: ["raw-materials"] }
})
.post("/", async ({ body, status }) => {
try {
const rawMaterial = await prisma.rawMaterial.create({
data: body,
});
return status(201, rawMaterial);
} catch (err) {
console.error(err);
return status(500, { message: "Failed to create raw material" });
}
}, {
body: t.Object({
name: t.String(),
unit: t.String(),
pricePerUnit: t.Number(),
}),
detail: { tags: ["raw-materials"] }
})
.put("/:id", async ({ params, body, status }) => {
const id = parseInt(params.id);
if (isNaN(id)) return status(400, { message: "Invalid ID" });
try {
const rawMaterial = await prisma.rawMaterial.update({
where: { id },
data: body,
});
return rawMaterial;
} catch (err) {
console.error(err);
return status(500, { message: "Failed to update raw material" });
}
}, {
body: t.Object({
name: t.String(),
unit: t.String(),
pricePerUnit: t.Number(),
}),
detail: { tags: ["raw-materials"] }
})
.delete("/:id", async ({ params, status }) => {
const id = parseInt(params.id);
if (isNaN(id)) return status(400, { message: "Invalid ID" });
try {
await prisma.rawMaterial.delete({
where: { id },
});
return { success: true };
} catch (err) {
console.error(err);
return status(500, { message: "Failed to delete raw material" });
}
}, {
detail: { tags: ["raw-materials"] }
});