Implement process materials auto-fill functionality for outsource purchase orders
- Added a new endpoint to retrieve process materials based on routing details and work order ID. - Introduced the `getProcessMaterials` function in the `outsourcePurchaseController` to handle the logic for fetching materials. - Updated the `outsourcePurchaseRoutes` to include the new route for process materials. - Enhanced the `RegistrationModal` component to toggle material needs and automatically fill materials when required. (TASK:ERP-019)
This commit is contained in:
@@ -182,6 +182,32 @@ export async function autoProcesses(
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// 공정 자재투입(material_input) 자동 채움
|
||||
// — 사급자재 체크 시 해당 공정의 자재 목록을 외주발주 자재 형식으로 반환
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
export async function getProcessMaterials(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response,
|
||||
) {
|
||||
try {
|
||||
const companyCode = req.user!.companyCode;
|
||||
const workOrderId = req.query.work_order_id as string | undefined;
|
||||
const routingDetailId = req.query.routing_detail_id as string | undefined;
|
||||
if (!routingDetailId) {
|
||||
return fail(res, 400, "routing_detail_id는 필수입니다");
|
||||
}
|
||||
const data = await svc.getProcessMaterialInputs(
|
||||
companyCode,
|
||||
workOrderId,
|
||||
routingDetailId,
|
||||
);
|
||||
return ok(res, data);
|
||||
} catch (e: any) {
|
||||
return fail(res, 500, e?.message || "공정 자재투입 조회 실패", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// 외주발주 가능 작업지시 목록
|
||||
// (TASK:ERP-019 재구현 — 좌측 리스트 필터)
|
||||
|
||||
@@ -466,6 +466,7 @@ export async function getWorkItemDetails(req: AuthenticatedRequest, res: Respons
|
||||
selected_bom_items, process_inspection_apply, equip_inspection_apply,
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data,
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit,
|
||||
created_date
|
||||
FROM process_work_item_detail
|
||||
WHERE work_item_id = $1 AND company_code = $2
|
||||
@@ -499,6 +500,8 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
// 설비조건(equip_condition) 전용 5개 필드 — TASK:ERP-015
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data,
|
||||
// 자재투입(material_input) 전용 4필드 — 외주발주 사급자재 자동 채움 연동
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit,
|
||||
} = req.body;
|
||||
|
||||
if (!work_item_id || !content) {
|
||||
@@ -524,9 +527,10 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
duration_minutes, input_type, lookup_target, display_fields, selected_bom_items,
|
||||
process_inspection_apply, equip_inspection_apply,
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data)
|
||||
condition_auto_collect, condition_plc_data,
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20,
|
||||
$21, $22, $23, $24, $25)
|
||||
$21, $22, $23, $24, $25, $26, $27, $28, $29)
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
@@ -559,6 +563,10 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
condition_tolerance || null,
|
||||
condition_auto_collect || null,
|
||||
condition_plc_data || null,
|
||||
bom_item_id || null,
|
||||
bom_item_name || null,
|
||||
bom_qty != null && bom_qty !== "" ? String(bom_qty) : null,
|
||||
bom_unit || null,
|
||||
]);
|
||||
|
||||
logger.info("작업 항목 상세 생성", { companyCode, id: result.rows[0].id });
|
||||
@@ -588,6 +596,8 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
// 설비조건(equip_condition) 전용 5개 필드 — TASK:ERP-015
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data,
|
||||
// 자재투입(material_input) 전용 4필드 — 외주발주 사급자재 자동 채움 연동
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit,
|
||||
} = req.body;
|
||||
|
||||
const bomItemsJson = Array.isArray(selected_bom_items) ? JSON.stringify(selected_bom_items) : selected_bom_items ?? null;
|
||||
@@ -616,6 +626,10 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
condition_tolerance = $22,
|
||||
condition_auto_collect = $23,
|
||||
condition_plc_data = $24,
|
||||
bom_item_id = $25,
|
||||
bom_item_name = $26,
|
||||
bom_qty = $27,
|
||||
bom_unit = $28,
|
||||
updated_date = NOW()
|
||||
WHERE id = $6 AND company_code = $7
|
||||
RETURNING *
|
||||
@@ -646,6 +660,10 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
condition_tolerance ?? null,
|
||||
condition_auto_collect ?? null,
|
||||
condition_plc_data ?? null,
|
||||
bom_item_id ?? null,
|
||||
bom_item_name ?? null,
|
||||
bom_qty != null && bom_qty !== "" ? String(bom_qty) : null,
|
||||
bom_unit ?? null,
|
||||
]);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
@@ -762,9 +780,10 @@ export async function saveAll(req: AuthenticatedRequest, res: Response) {
|
||||
inspection_code, inspection_method, unit, lower_limit, upper_limit,
|
||||
duration_minutes, input_type, lookup_target, display_fields,
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data)
|
||||
condition_auto_collect, condition_plc_data,
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
|
||||
$18, $19, $20, $21, $22)`,
|
||||
$18, $19, $20, $21, $22, $23, $24, $25, $26)`,
|
||||
[
|
||||
companyCode,
|
||||
workItemId,
|
||||
@@ -789,6 +808,11 @@ export async function saveAll(req: AuthenticatedRequest, res: Response) {
|
||||
detail.condition_tolerance || null,
|
||||
detail.condition_auto_collect || null,
|
||||
detail.condition_plc_data || null,
|
||||
// 자재투입(material_input) 전용 4필드 — 외주발주 사급자재 자동 채움 연동
|
||||
detail.bom_item_id || null,
|
||||
detail.bom_item_name || null,
|
||||
detail.bom_qty != null && detail.bom_qty !== "" ? String(detail.bom_qty) : null,
|
||||
detail.bom_unit || null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -668,7 +668,8 @@ export async function getWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
duration_minutes, input_type, lookup_target, display_fields,
|
||||
process_inspection_apply, equip_inspection_apply,
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data
|
||||
condition_auto_collect, condition_plc_data,
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit
|
||||
FROM wi_process_work_item_detail
|
||||
WHERE wi_work_item_id = $1 AND company_code = $2
|
||||
ORDER BY sort_order`,
|
||||
@@ -695,7 +696,8 @@ export async function getWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
duration_minutes, input_type, lookup_target, display_fields,
|
||||
process_inspection_apply, equip_inspection_apply,
|
||||
condition_unit, condition_base_value, condition_tolerance,
|
||||
condition_auto_collect, condition_plc_data
|
||||
condition_auto_collect, condition_plc_data,
|
||||
bom_item_id, bom_item_name, bom_qty, bom_unit
|
||||
FROM process_work_item_detail
|
||||
WHERE work_item_id = $1 AND company_code = $2
|
||||
ORDER BY sort_order`,
|
||||
@@ -751,7 +753,7 @@ async function syncMasterChecklistFromWi(
|
||||
// 3. 접수 건수 확인
|
||||
const acceptCount = await client.query(
|
||||
`SELECT COUNT(*)::int AS cnt FROM work_order_process_result wopr
|
||||
JOIN work_order_process wop ON wop.id = wopr.work_order_process_id
|
||||
JOIN work_order_process wop ON wop.id = wopr.wop_id
|
||||
WHERE wop.wo_id = $1 AND wop.company_code = $2 AND wopr.company_code = $2`,
|
||||
[wiId, companyCode],
|
||||
);
|
||||
@@ -850,9 +852,9 @@ export async function copyWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
|
||||
for (const origDetail of origDetails.rows) {
|
||||
await client.query(
|
||||
`INSERT INTO wi_process_work_item_detail (id, company_code, wi_work_item_id, detail_type, content, is_required, sort_order, remark, inspection_code, inspection_method, unit, lower_limit, upper_limit, duration_minutes, input_type, lookup_target, display_fields, process_inspection_apply, equip_inspection_apply, condition_unit, condition_base_value, condition_tolerance, condition_auto_collect, condition_plc_data, writer)
|
||||
VALUES (gen_random_uuid()::text, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)`,
|
||||
[companyCode, newItemId, origDetail.detail_type, origDetail.content, origDetail.is_required, origDetail.sort_order, origDetail.remark, origDetail.inspection_code, origDetail.inspection_method, origDetail.unit, origDetail.lower_limit, origDetail.upper_limit, origDetail.duration_minutes, origDetail.input_type, origDetail.lookup_target, origDetail.display_fields, origDetail.process_inspection_apply || null, origDetail.equip_inspection_apply || null, origDetail.condition_unit || null, origDetail.condition_base_value || null, origDetail.condition_tolerance || null, origDetail.condition_auto_collect || null, origDetail.condition_plc_data || null, userId]
|
||||
`INSERT INTO wi_process_work_item_detail (id, company_code, wi_work_item_id, detail_type, content, is_required, sort_order, remark, inspection_code, inspection_method, unit, lower_limit, upper_limit, duration_minutes, input_type, lookup_target, display_fields, process_inspection_apply, equip_inspection_apply, condition_unit, condition_base_value, condition_tolerance, condition_auto_collect, condition_plc_data, bom_item_id, bom_item_name, bom_qty, bom_unit, writer)
|
||||
VALUES (gen_random_uuid()::text, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28)`,
|
||||
[companyCode, newItemId, origDetail.detail_type, origDetail.content, origDetail.is_required, origDetail.sort_order, origDetail.remark, origDetail.inspection_code, origDetail.inspection_method, origDetail.unit, origDetail.lower_limit, origDetail.upper_limit, origDetail.duration_minutes, origDetail.input_type, origDetail.lookup_target, origDetail.display_fields, origDetail.process_inspection_apply || null, origDetail.equip_inspection_apply || null, origDetail.condition_unit || null, origDetail.condition_base_value || null, origDetail.condition_tolerance || null, origDetail.condition_auto_collect || null, origDetail.condition_plc_data || null, origDetail.bom_item_id || null, origDetail.bom_item_name || null, origDetail.bom_qty || null, origDetail.bom_unit || null, userId]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -919,9 +921,9 @@ export async function saveWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
if (wi.details && Array.isArray(wi.details)) {
|
||||
for (const d of wi.details) {
|
||||
await client.query(
|
||||
`INSERT INTO wi_process_work_item_detail (id, company_code, wi_work_item_id, detail_type, content, is_required, sort_order, remark, inspection_code, inspection_method, unit, lower_limit, upper_limit, duration_minutes, input_type, lookup_target, display_fields, process_inspection_apply, equip_inspection_apply, condition_unit, condition_base_value, condition_tolerance, condition_auto_collect, condition_plc_data, writer)
|
||||
VALUES (gen_random_uuid()::text, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)`,
|
||||
[companyCode, newId, d.detail_type, d.content, d.is_required, d.sort_order, d.remark || null, d.inspection_code || null, d.inspection_method || null, d.unit || null, d.lower_limit || null, d.upper_limit || null, d.duration_minutes || null, d.input_type || null, d.lookup_target || null, d.display_fields || null, d.process_inspection_apply || null, d.equip_inspection_apply || null, d.condition_unit || null, d.condition_base_value || null, d.condition_tolerance || null, d.condition_auto_collect || null, d.condition_plc_data || null, userId]
|
||||
`INSERT INTO wi_process_work_item_detail (id, company_code, wi_work_item_id, detail_type, content, is_required, sort_order, remark, inspection_code, inspection_method, unit, lower_limit, upper_limit, duration_minutes, input_type, lookup_target, display_fields, process_inspection_apply, equip_inspection_apply, condition_unit, condition_base_value, condition_tolerance, condition_auto_collect, condition_plc_data, bom_item_id, bom_item_name, bom_qty, bom_unit, writer)
|
||||
VALUES (gen_random_uuid()::text, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28)`,
|
||||
[companyCode, newId, d.detail_type, d.content, d.is_required, d.sort_order, d.remark || null, d.inspection_code || null, d.inspection_method || null, d.unit || null, d.lower_limit || null, d.upper_limit || null, d.duration_minutes || null, d.input_type || null, d.lookup_target || null, d.display_fields || null, d.process_inspection_apply || null, d.equip_inspection_apply || null, d.condition_unit || null, d.condition_base_value || null, d.condition_tolerance || null, d.condition_auto_collect || null, d.condition_plc_data || null, d.bom_item_id || null, d.bom_item_name || null, d.bom_qty != null && d.bom_qty !== "" ? String(d.bom_qty) : null, d.bom_unit || null, userId]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -939,7 +941,13 @@ export async function saveWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
client.release();
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error("작업지시 공정작업기준 저장 실패", { error: error.message });
|
||||
logger.error("작업지시 공정작업기준 저장 실패", {
|
||||
message: error?.message,
|
||||
code: error?.code,
|
||||
detail: error?.detail,
|
||||
where: error?.where,
|
||||
stack: error?.stack,
|
||||
});
|
||||
return res.status(500).json({ success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user