feat: Implement cutting plan management and work instruction modal

- Introduced a new cutting plan management page for COMPANY_9, allowing users to manage cutting plans effectively.
- Added a Work Instruction Apply Modal to facilitate the application of work instructions linked to cutting plans.
- Enhanced data handling by incorporating additional fields such as condition_unit, condition_base_value, condition_tolerance, condition_auto_collect, and condition_plc_data in relevant controllers and database interactions.
- Updated UI components to support new features, including displaying batch numbers and item sizes in the work instruction page.

These changes aim to improve the efficiency and usability of cutting plan and work instruction management processes.
This commit is contained in:
kjs
2026-04-24 11:12:32 +09:00
parent c01166263b
commit 37ca354af9
8 changed files with 3240 additions and 25 deletions

View File

@@ -163,24 +163,29 @@ export async function getMaterialStatus(
bomParams.push(companyCode);
}
// inventory_unit은 카테고리 코드(CAT_xxx)로 저장됨 → category_values 조인으로 라벨 해상
const bomQuery = `
SELECT
b.item_code AS parent_item_code,
b.base_qty AS bom_base_qty,
bd.child_item_id,
bd.quantity AS bom_qty,
bd.unit AS bom_unit,
bd.loss_rate,
ii.item_name AS material_name,
ii.item_number AS material_code,
ii.unit AS material_unit,
ii.inventory_unit AS material_inventory_unit,
COALESCE(cv_inv.value_label, ii.inventory_unit) AS material_inventory_unit,
COALESCE(ii.width::text, '') AS material_width,
COALESCE(ii.height::text, '') AS material_height,
COALESCE(ii.thickness::text, '') AS material_thickness
FROM bom b
JOIN bom_detail bd ON b.id = bd.bom_id AND b.company_code = bd.company_code
LEFT JOIN item_info ii ON bd.child_item_id = ii.id AND b.company_code = ii.company_code
LEFT JOIN category_values cv_inv
ON cv_inv.table_name = 'item_info'
AND cv_inv.column_name = 'inventory_unit'
AND cv_inv.value_code = ii.inventory_unit
AND cv_inv.company_code = ii.company_code
AND cv_inv.is_active = true
WHERE b.item_code IN (${itemPlaceholders})
${bomCompanyCondition}
ORDER BY b.item_code, bd.seq_no
@@ -221,11 +226,7 @@ export async function getMaterialStatus(
materialCode:
bomRow.material_code || bomRow.child_item_id,
materialName: bomRow.material_name || "알 수 없음",
unit:
bomRow.material_inventory_unit ||
bomRow.bom_unit ||
bomRow.material_unit ||
"EA",
unit: bomRow.material_inventory_unit || "",
requiredQty,
width: bomRow.material_width || "",
height: bomRow.material_height || "",

View File

@@ -463,7 +463,10 @@ export async function getWorkItemDetails(req: AuthenticatedRequest, res: Respons
SELECT id, 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,
selected_bom_items, process_inspection_apply, equip_inspection_apply, created_date
selected_bom_items, process_inspection_apply, equip_inspection_apply,
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data,
created_date
FROM process_work_item_detail
WHERE work_item_id = $1 AND company_code = $2
ORDER BY sort_order, created_date
@@ -493,6 +496,9 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
inspection_code, inspection_method, unit, lower_limit, upper_limit,
duration_minutes, input_type, lookup_target, display_fields,
selected_bom_items, process_inspection_apply, equip_inspection_apply,
// 설비조건(equip_condition) 전용 5개 필드 — TASK:ERP-015
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data,
} = req.body;
if (!work_item_id || !content) {
@@ -516,8 +522,11 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
(company_code, work_item_id, detail_type, content, is_required, sort_order, remark, writer,
inspection_code, inspection_method, unit, lower_limit, upper_limit,
duration_minutes, input_type, lookup_target, display_fields, selected_bom_items,
process_inspection_apply, equip_inspection_apply)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)
process_inspection_apply, equip_inspection_apply,
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data)
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)
RETURNING *
`;
@@ -545,6 +554,11 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
bomItemsJson,
process_inspection_apply || null,
equip_inspection_apply || null,
condition_unit || null,
condition_base_value || null,
condition_tolerance || null,
condition_auto_collect || null,
condition_plc_data || null,
]);
logger.info("작업 항목 상세 생성", { companyCode, id: result.rows[0].id });
@@ -571,6 +585,9 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
inspection_code, inspection_method, unit, lower_limit, upper_limit,
duration_minutes, input_type, lookup_target, display_fields,
selected_bom_items, process_inspection_apply, equip_inspection_apply,
// 설비조건(equip_condition) 전용 5개 필드 — TASK:ERP-015
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data,
} = req.body;
const bomItemsJson = Array.isArray(selected_bom_items) ? JSON.stringify(selected_bom_items) : selected_bom_items ?? null;
@@ -594,6 +611,11 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
selected_bom_items = $17,
process_inspection_apply = $18,
equip_inspection_apply = $19,
condition_unit = $20,
condition_base_value = $21,
condition_tolerance = $22,
condition_auto_collect = $23,
condition_plc_data = $24,
updated_date = NOW()
WHERE id = $6 AND company_code = $7
RETURNING *
@@ -619,6 +641,11 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
bomItemsJson,
process_inspection_apply || null,
equip_inspection_apply || null,
condition_unit ?? null,
condition_base_value ?? null,
condition_tolerance ?? null,
condition_auto_collect ?? null,
condition_plc_data ?? null,
]);
if (result.rowCount === 0) {
@@ -733,8 +760,11 @@ export async function saveAll(req: AuthenticatedRequest, res: Response) {
`INSERT INTO process_work_item_detail
(company_code, work_item_id, detail_type, content, is_required, sort_order, remark, writer,
inspection_code, inspection_method, unit, lower_limit, upper_limit,
duration_minutes, input_type, lookup_target, display_fields)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)`,
duration_minutes, input_type, lookup_target, display_fields,
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17,
$18, $19, $20, $21, $22)`,
[
companyCode,
workItemId,
@@ -753,6 +783,12 @@ export async function saveAll(req: AuthenticatedRequest, res: Response) {
detail.input_type || null,
detail.lookup_target || null,
detail.display_fields || null,
// 설비조건(equip_condition) 전용 5개 — TASK:ERP-015
detail.condition_unit || null,
detail.condition_base_value || null,
detail.condition_tolerance || null,
detail.condition_auto_collect || null,
detail.condition_plc_data || null,
]
);
}

View File

@@ -665,7 +665,9 @@ export async function getWorkStandard(req: AuthenticatedRequest, res: Response)
`SELECT id, wi_work_item_id AS 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
process_inspection_apply, equip_inspection_apply,
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data
FROM wi_process_work_item_detail
WHERE wi_work_item_id = $1 AND company_code = $2
ORDER BY sort_order`,
@@ -690,7 +692,9 @@ export async function getWorkStandard(req: AuthenticatedRequest, res: Response)
`SELECT id, 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
process_inspection_apply, equip_inspection_apply,
condition_unit, condition_base_value, condition_tolerance,
condition_auto_collect, condition_plc_data
FROM process_work_item_detail
WHERE work_item_id = $1 AND company_code = $2
ORDER BY sort_order`,
@@ -771,9 +775,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, 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)`,
[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, 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, 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]
);
}
}
@@ -838,9 +842,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, 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)`,
[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, 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, 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]
);
}
}