Add BOM Copy Functionality to BOM Management
- Introduced a new API endpoint to copy a BOM tree to multiple target items, allowing for efficient duplication of BOM structures. - Implemented payload validation to ensure correct data format and integrity during the copy process. - Added a modal in the frontend for managing the BOM copy operation, including options for conflict resolution and progress tracking. - Enhanced the BOM service with necessary logic for handling BOM copies, including versioning and error handling. (TASK: ERP-028)
This commit is contained in:
@@ -143,6 +143,73 @@ export async function initializeBomVersion(req: Request, res: Response) {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── BOM 복사 (TASK:ERP-028) ─────────────────────────
|
||||
|
||||
/**
|
||||
* POST /bom/:bomId/copy-to-items
|
||||
* 기준 BOM의 트리(편집본)를 대상 품목 N개에 복제
|
||||
* - conflictStrategy = "skip": 대상 품목에 BOM 있으면 skipped[]
|
||||
* - conflictStrategy = "new_version": 대상 품목 BOM에 새 draft 버전 추가 (없으면 새 BOM 생성)
|
||||
*/
|
||||
export async function copyBomToItems(req: Request, res: Response) {
|
||||
try {
|
||||
const { bomId } = req.params;
|
||||
const companyCode = (req as any).user?.companyCode || "*";
|
||||
const userId = (req as any).user?.userName || (req as any).user?.userId || "";
|
||||
|
||||
const { targetItemIds, conflictStrategy, editedTree } = req.body || {};
|
||||
|
||||
// ─── 페이로드 검증 ─────────────────────
|
||||
if (!Array.isArray(targetItemIds) || targetItemIds.length === 0) {
|
||||
res.status(400).json({ success: false, message: "targetItemIds는 1개 이상의 배열이어야 합니다" });
|
||||
return;
|
||||
}
|
||||
if (conflictStrategy !== "skip" && conflictStrategy !== "new_version") {
|
||||
res.status(400).json({ success: false, message: "conflictStrategy는 'skip' 또는 'new_version'이어야 합니다" });
|
||||
return;
|
||||
}
|
||||
if (!Array.isArray(editedTree) || editedTree.length === 0) {
|
||||
res.status(400).json({ success: false, message: "editedTree는 1개 이상의 노드 배열이어야 합니다" });
|
||||
return;
|
||||
}
|
||||
|
||||
// 트리 노드 필수 필드 검증
|
||||
for (const n of editedTree) {
|
||||
if (!n || typeof n !== "object") {
|
||||
res.status(400).json({ success: false, message: "editedTree 노드는 객체여야 합니다" });
|
||||
return;
|
||||
}
|
||||
if (!n.tempId) {
|
||||
res.status(400).json({ success: false, message: "editedTree 각 노드에는 tempId가 필요합니다" });
|
||||
return;
|
||||
}
|
||||
if (!n.childItemId) {
|
||||
res.status(400).json({ success: false, message: `노드 ${n.tempId}: childItemId가 필요합니다` });
|
||||
return;
|
||||
}
|
||||
const qty = Number(n.quantity);
|
||||
if (!Number.isFinite(qty) || qty <= 0) {
|
||||
res.status(400).json({ success: false, message: `노드 ${n.tempId}: quantity는 0보다 커야 합니다` });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const data = await bomService.copyBomToItems({
|
||||
sourceBomId: bomId,
|
||||
companyCode,
|
||||
userId,
|
||||
targetItemIds,
|
||||
conflictStrategy,
|
||||
editedTree,
|
||||
});
|
||||
|
||||
res.json({ success: true, data });
|
||||
} catch (error: any) {
|
||||
logger.error("BOM 복사 실패", { error: error.message });
|
||||
res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
// ─── BOM 엑셀 업로드/다운로드 ─────────────────────────
|
||||
|
||||
export async function createBomFromExcel(req: Request, res: Response) {
|
||||
|
||||
Reference in New Issue
Block a user