feat: Enhance work item detail management with additional inspection fields
- Updated the processWorkStandardController and workInstructionController to include new fields for process_inspection_apply and equip_inspection_apply in SQL queries and data handling. - Modified the DetailFormModal and WorkItemDetailList components to support individual registration of inspection items and equipment inspections, improving the flexibility of the inspection process. - Implemented logic to handle automatic content generation for inspection and equipment inspection types, enhancing user experience and data accuracy. - These changes aim to improve the management of work item details and streamline the inspection process across multiple company implementations.
This commit is contained in:
@@ -463,7 +463,7 @@ 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, created_date
|
||||
selected_bom_items, process_inspection_apply, equip_inspection_apply, created_date
|
||||
FROM process_work_item_detail
|
||||
WHERE work_item_id = $1 AND company_code = $2
|
||||
ORDER BY sort_order, created_date
|
||||
@@ -492,7 +492,7 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
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,
|
||||
selected_bom_items, process_inspection_apply, equip_inspection_apply,
|
||||
} = req.body;
|
||||
|
||||
if (!work_item_id || !content) {
|
||||
@@ -515,8 +515,9 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
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, selected_bom_items)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
|
||||
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)
|
||||
RETURNING *
|
||||
`;
|
||||
|
||||
@@ -542,6 +543,8 @@ export async function createWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
lookup_target || null,
|
||||
display_fields || null,
|
||||
bomItemsJson,
|
||||
process_inspection_apply || null,
|
||||
equip_inspection_apply || null,
|
||||
]);
|
||||
|
||||
logger.info("작업 항목 상세 생성", { companyCode, id: result.rows[0].id });
|
||||
@@ -567,7 +570,7 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
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,
|
||||
selected_bom_items, process_inspection_apply, equip_inspection_apply,
|
||||
} = req.body;
|
||||
|
||||
const bomItemsJson = Array.isArray(selected_bom_items) ? JSON.stringify(selected_bom_items) : selected_bom_items ?? null;
|
||||
@@ -589,6 +592,8 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
lookup_target = $15,
|
||||
display_fields = $16,
|
||||
selected_bom_items = $17,
|
||||
process_inspection_apply = $18,
|
||||
equip_inspection_apply = $19,
|
||||
updated_date = NOW()
|
||||
WHERE id = $6 AND company_code = $7
|
||||
RETURNING *
|
||||
@@ -612,6 +617,8 @@ export async function updateWorkItemDetail(req: AuthenticatedRequest, res: Respo
|
||||
lookup_target || null,
|
||||
display_fields || null,
|
||||
bomItemsJson,
|
||||
process_inspection_apply || null,
|
||||
equip_inspection_apply || null,
|
||||
]);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
|
||||
@@ -443,7 +443,8 @@ export async function getWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
const detailsResult = await pool.query(
|
||||
`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
|
||||
duration_minutes, input_type, lookup_target, display_fields,
|
||||
process_inspection_apply, equip_inspection_apply
|
||||
FROM wi_process_work_item_detail
|
||||
WHERE wi_work_item_id = $1 AND company_code = $2
|
||||
ORDER BY sort_order`,
|
||||
@@ -467,7 +468,8 @@ export async function getWorkStandard(req: AuthenticatedRequest, res: Response)
|
||||
const detailsResult = await pool.query(
|
||||
`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
|
||||
duration_minutes, input_type, lookup_target, display_fields,
|
||||
process_inspection_apply, equip_inspection_apply
|
||||
FROM process_work_item_detail
|
||||
WHERE work_item_id = $1 AND company_code = $2
|
||||
ORDER BY sort_order`,
|
||||
@@ -548,9 +550,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 (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, writer)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)`,
|
||||
[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, userId]
|
||||
`INSERT INTO wi_process_work_item_detail (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 ($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]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -612,9 +614,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 (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, writer)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)`,
|
||||
[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, userId]
|
||||
`INSERT INTO wi_process_work_item_detail (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 ($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]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-1
@@ -270,14 +270,40 @@ export function DetailFormModal({
|
||||
|
||||
const submitData = { ...formData };
|
||||
|
||||
// content 자동 설정 (UI에서 직접 입력이 없는 유형들)
|
||||
// 검사항목 적용 → 품목검사정보 각각 개별 등록
|
||||
if (type === "inspection" && submitData.process_inspection_apply === "apply") {
|
||||
if (itemInspections.length > 0) {
|
||||
for (const insp of itemInspections) {
|
||||
onSubmit({
|
||||
...submitData,
|
||||
detail_type: "inspection",
|
||||
content: `${insp.inspection_item_name || insp.inspection_standard || "-"} | ${insp.pass_criteria || ""}`.trim(),
|
||||
is_required: submitData.is_required || "Y",
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
submitData.content = submitData.content || "품목별 검사정보 (자동 연동)";
|
||||
}
|
||||
if (type === "lookup") {
|
||||
submitData.content = submitData.content || "품목 등록 문서 (자동 연동)";
|
||||
}
|
||||
// 설비점검 적용 → 점검항목 각각 개별 등록
|
||||
if (type === "equip_inspection" && submitData.equip_inspection_apply === "apply") {
|
||||
if (equipInspItems.length > 0) {
|
||||
for (const item of equipInspItems) {
|
||||
const range = (item.lower_limit || item.upper_limit) ? `${item.lower_limit || ""} ~ ${item.upper_limit || ""}${item.unit ? ` ${item.unit}` : ""}` : "";
|
||||
onSubmit({
|
||||
...submitData,
|
||||
detail_type: "equip_inspection",
|
||||
content: `${item.inspection_item || "-"}${range ? ` | ${range}` : ""}`.trim(),
|
||||
is_required: submitData.is_required || "Y",
|
||||
});
|
||||
}
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
submitData.content = submitData.content || "설비 점검항목 (설비정보 연동)";
|
||||
}
|
||||
if (type === "production_result") {
|
||||
|
||||
+23
-6
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useRef } from "react";
|
||||
import { Plus, Pencil, Trash2 } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -34,6 +34,7 @@ export function WorkItemDetailList({
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalMode, setModalMode] = useState<"add" | "edit">("add");
|
||||
const [editTarget, setEditTarget] = useState<WorkItemDetail | null>(null);
|
||||
const editFirstRef = useRef(false);
|
||||
|
||||
if (!workItem) {
|
||||
return (
|
||||
@@ -57,6 +58,7 @@ export function WorkItemDetailList({
|
||||
const handleOpenEdit = (detail: WorkItemDetail) => {
|
||||
setModalMode("edit");
|
||||
setEditTarget(detail);
|
||||
editFirstRef.current = false;
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
@@ -64,14 +66,31 @@ export function WorkItemDetailList({
|
||||
if (modalMode === "add") {
|
||||
onCreateDetail({ ...data, sort_order: details.length + 1 });
|
||||
} else if (editTarget) {
|
||||
onUpdateDetail(editTarget.id, data);
|
||||
const isApplyGroup = editTarget.detail_type === "inspection" && editTarget.process_inspection_apply === "apply"
|
||||
|| editTarget.detail_type === "equip_inspection" && editTarget.equip_inspection_apply === "apply"
|
||||
|| editTarget.detail_type === "material_input";
|
||||
|
||||
if (isApplyGroup && data.is_required !== undefined) {
|
||||
// 같은 유형의 apply 그룹 전체 일괄 업데이트 (필수여부 등)
|
||||
const siblings = details.filter(d =>
|
||||
d.detail_type === editTarget.detail_type &&
|
||||
(d.detail_type === "inspection" ? d.process_inspection_apply === "apply" :
|
||||
d.detail_type === "equip_inspection" ? d.equip_inspection_apply === "apply" :
|
||||
d.detail_type === "material_input")
|
||||
);
|
||||
for (const sib of siblings) {
|
||||
onUpdateDetail(sib.id, { ...sib, is_required: data.is_required });
|
||||
}
|
||||
} else {
|
||||
onUpdateDetail(editTarget.id, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getContentSummary = (detail: WorkItemDetail): string => {
|
||||
const type = detail.detail_type;
|
||||
if (type === "inspection") {
|
||||
if (detail.process_inspection_apply === "apply") return "품목별 검사정보 (자동 연동)";
|
||||
if (detail.process_inspection_apply === "apply") return detail.content || "품목별 검사정보 (자동 연동)";
|
||||
const parts = [detail.content];
|
||||
if (detail.inspection_method) parts.push(`[${detail.inspection_method}]`);
|
||||
if (detail.base_value) {
|
||||
@@ -93,9 +112,7 @@ export function WorkItemDetailList({
|
||||
}
|
||||
if (type === "lookup") return "품목 등록 문서 (자동 연동)";
|
||||
if (type === "equip_inspection") {
|
||||
return detail.equip_inspection_apply === "apply"
|
||||
? "설비 점검항목 (설비정보 연동)"
|
||||
: detail.content || "설비점검";
|
||||
return detail.content || (detail.equip_inspection_apply === "apply" ? "설비 점검항목 (설비정보 연동)" : "설비점검");
|
||||
}
|
||||
if (type === "equip_condition") {
|
||||
const parts = [detail.content];
|
||||
|
||||
Reference in New Issue
Block a user