diff --git a/backend-node/src/controllers/popProductionController.ts b/backend-node/src/controllers/popProductionController.ts index 92cba89d..dabb2f1f 100644 --- a/backend-node/src/controllers/popProductionController.ts +++ b/backend-node/src/controllers/popProductionController.ts @@ -359,11 +359,21 @@ export const syncWorkInstructions = async ( }); // 미동기화 작업지시 조회: routing이 있지만 work_order_process가 없는 항목 + // header에 routing 없으면 detail에서 가져옴 (PC가 detail에만 저장하는 경우 대응) const unsyncedResult = await pool.query( - `SELECT wi.id, wi.work_instruction_no, wi.routing, wi.qty + `SELECT wi.id, wi.work_instruction_no, + COALESCE(wi.routing, wid.routing_version_id) AS routing, + COALESCE(NULLIF(wi.qty, ''), wid.qty) AS qty, + COALESCE(wi.item_id, (SELECT id FROM item_info WHERE item_number = wid.item_number AND company_code = $1 LIMIT 1)) AS item_id FROM work_instruction wi + LEFT JOIN LATERAL ( + SELECT routing_version_id, qty, item_number + FROM work_instruction_detail + WHERE work_instruction_no = wi.work_instruction_no AND company_code = $1 + LIMIT 1 + ) wid ON true WHERE wi.company_code = $1 - AND wi.routing IS NOT NULL + AND COALESCE(wi.routing, wid.routing_version_id) IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM work_order_process wop WHERE wop.wo_id = wi.id AND wop.company_code = $1 @@ -373,6 +383,20 @@ export const syncWorkInstructions = async ( const unsynced = unsyncedResult.rows; + // header에 routing/qty/item_id가 비어있으면 자동 보정 (detail → header 동기화) + for (const wi of unsynced) { + await pool.query( + `UPDATE work_instruction SET + routing = COALESCE(routing, $2), + qty = COALESCE(NULLIF(qty, ''), $3), + item_id = COALESCE(item_id, $4), + updated_date = NOW() + WHERE id = $1 AND company_code = $5 + AND (routing IS NULL OR qty IS NULL OR qty = '' OR item_id IS NULL)`, + [wi.id, wi.routing, wi.qty, wi.item_id, companyCode], + ); + } + if (unsynced.length === 0) { return res.json({ success: true, diff --git a/frontend/components/pop/hardcoded/production/ProcessWork.tsx b/frontend/components/pop/hardcoded/production/ProcessWork.tsx index 1675aad3..6f603e43 100644 --- a/frontend/components/pop/hardcoded/production/ProcessWork.tsx +++ b/frontend/components/pop/hardcoded/production/ProcessWork.tsx @@ -1208,34 +1208,45 @@ export function ProcessWork({ processId }: ProcessWorkProps) { behavior: "smooth", }); }} - className={`w-full flex items-center gap-2 px-3 py-2 text-left transition-all ${ + className={`w-full flex items-center gap-3 mx-2 mb-1.5 px-3 py-3 rounded-xl text-left transition-all ${ isSelected - ? "border-l-[3px] border-l-gray-900 bg-white" - : "border-l-[3px] border-l-transparent hover:bg-gray-50" + ? "bg-blue-50 border-2 border-blue-400 shadow-sm" + : isDone + ? "bg-green-50/50 border border-green-200" + : "bg-white border border-gray-200 hover:border-blue-300 hover:shadow-sm" }`} + style={{ width: "calc(100% - 16px)" }} > {g.title} {g.completed}/{g.total} @@ -1259,15 +1270,16 @@ export function ProcessWork({ processId }: ProcessWorkProps) { behavior: "smooth", }); }} - className={`w-full flex items-center gap-2 px-3 py-2.5 mb-2 text-left transition-all ${ + className={`w-full flex items-center gap-3 mx-2 mb-1.5 px-3 py-3 rounded-xl text-left transition-all ${ activeSection === "material" - ? "border-l-[3px] border-l-gray-900 bg-white" - : "border-l-[3px] border-l-transparent hover:bg-gray-50" + ? "bg-blue-50 border-2 border-blue-400 shadow-sm" + : "bg-white border border-gray-200 hover:border-blue-300 hover:shadow-sm" }`} + style={{ width: "calc(100% - 16px)" }} > - 📦 + 📦 자재 투입