Implement BOM base quantity retrieval functionality

- Added a new endpoint `/work-instruction/bom-base-qty` to retrieve base quantities for items based on their codes.
- Introduced the `getBomBaseQtyMap` function in the `workInstructionController` to handle the logic for fetching base quantities.
- Updated the frontend to call the new API and integrate base quantity mapping into the work instruction registration process.
- Enhanced the work instruction page to calculate batch counts and split quantities based on the retrieved base quantities.

(TASK:ERP-020)
This commit is contained in:
kjs
2026-05-07 12:01:03 +09:00
parent 20c76d3478
commit 629bc25cd5
4 changed files with 180 additions and 57 deletions

View File

@@ -314,7 +314,7 @@ export async function save(req: AuthenticatedRequest, res: Response) {
if (!firstRouting && itemRouting) firstRouting = itemRouting;
totalQty += Number(item.qty || 0);
await client.query(
`INSERT INTO work_instruction_detail (id,company_code,work_instruction_no,work_instruction_id,item_number,qty,remark,source_table,source_id,part_code,routing_version_id,start_date,end_date,equipment_ids,work_teams,workers,created_date,writer) VALUES (gen_random_uuid()::text,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,NOW(),$16)`,
`INSERT INTO work_instruction_detail (id,company_code,work_instruction_no,work_instruction_id,item_number,qty,remark,source_table,source_id,part_code,routing_version_id,start_date,end_date,equipment_ids,work_teams,workers,created_date,writer) VALUES (gen_random_uuid()::text,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,clock_timestamp(),$16)`,
[
companyCode,
wiNo,
@@ -993,3 +993,40 @@ export async function resetWorkStandard(req: AuthenticatedRequest, res: Response
return res.status(500).json({ success: false, message: error.message });
}
}
// ─── BOM 기준수(0레벨 base_qty) 일괄 조회 ───
// itemCodes(item_info.item_number 기준) 배열을 받아 { [itemCode]: base_qty | null } 맵 반환
export async function getBomBaseQtyMap(req: AuthenticatedRequest, res: Response) {
try {
const companyCode = req.user!.companyCode;
const itemCodes: string[] = Array.isArray(req.body?.itemCodes) ? req.body.itemCodes.filter(Boolean) : [];
if (itemCodes.length === 0) return res.json({ success: true, data: {} });
const pool = getPool();
// bom.item_code 우선 매칭, 없으면 item_info.id 경유 매칭
const result = await pool.query(
`SELECT i.item_number AS item_code, b.base_qty
FROM bom b
LEFT JOIN item_info i ON i.id = b.item_id AND i.company_code = b.company_code
WHERE b.company_code = $1
AND (b.item_code = ANY($2::text[]) OR i.item_number = ANY($2::text[]))`,
[companyCode, itemCodes]
);
const map: Record<string, number | null> = {};
for (const code of itemCodes) map[code] = null;
for (const row of result.rows) {
const code = row.item_code;
const base = parseFloat(row.base_qty || "");
if (!code) continue;
if (Number.isFinite(base) && base > 0) {
// 동일 품목 다건 BOM 시 첫 유효값 유지
if (map[code] == null) map[code] = base;
}
}
return res.json({ success: true, data: map });
} catch (error: any) {
logger.error("BOM 기준수 일괄 조회 실패", { error: error.message });
return res.status(500).json({ success: false, message: error.message });
}
}

View File

@@ -18,6 +18,9 @@ router.get("/employees", ctrl.getEmployeeList);
// 벌크 라우팅 조회 (품목별 공정 일괄 조회)
router.post("/routing-versions-bulk", ctrl.getRoutingVersionsBulk);
// BOM 기준수 일괄 조회 (작업지시 등록 모달 기준수/배치수 산출용)
router.post("/bom-base-qty", ctrl.getBomBaseQtyMap);
// 라우팅 & 공정작업기준
router.get("/:wiNo/routing-versions/:itemCode", ctrl.getRoutingVersions);
router.put("/:wiNo/routing", ctrl.updateRouting);