From b783e44618f3cfbcee70af16876eb18a4d56a86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sat, 21 Mar 2026 08:00:05 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[=EC=9E=91=EC=97=85=EC=A7=80=EC=8B=9C]?= =?UTF-8?q?=20=EC=A0=88=EA=B3=A1=20=EA=B3=B5=EC=A0=95=20=EB=8B=A8=EA=B3=84?= =?UTF-8?q?=EB=A5=BC=20BD=20=EC=BD=94=EB=93=9C=20=EC=A2=85=EB=A5=98?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20=ED=95=84=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../production/WorkOrders/WorkOrderDetail.tsx | 11 +++- src/components/production/WorkOrders/types.ts | 51 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/components/production/WorkOrders/WorkOrderDetail.tsx b/src/components/production/WorkOrders/WorkOrderDetail.tsx index f67ee716..c74258fd 100644 --- a/src/components/production/WorkOrders/WorkOrderDetail.tsx +++ b/src/components/production/WorkOrders/WorkOrderDetail.tsx @@ -34,6 +34,7 @@ import { SCREEN_PROCESS_STEPS, SLAT_PROCESS_STEPS, BENDING_PROCESS_STEPS, + filterBendingSteps, type WorkOrder, type ProcessType, type ProcessStep, @@ -56,13 +57,15 @@ function ProcessStepPills({ processType, currentStep, workSteps, + itemCodes, }: { processType: ProcessType; currentStep: number; workSteps?: ProcessStep[]; + itemCodes?: string[]; }) { // 동적 workSteps 우선 사용, 없으면 하드코딩 폴백 - const steps = workSteps && workSteps.length > 0 + let steps = workSteps && workSteps.length > 0 ? workSteps : processType === 'screen' ? SCREEN_PROCESS_STEPS @@ -70,6 +73,11 @@ function ProcessStepPills({ ? SLAT_PROCESS_STEPS : BENDING_PROCESS_STEPS; + // 절곡 공정: BD 코드에 따라 필요한 단계만 필터링 + if (processType === 'bending' && itemCodes && itemCodes.length > 0) { + steps = filterBendingSteps(itemCodes, BENDING_PROCESS_STEPS); + } + if (steps.length === 0) { return

공정 단계가 설정되지 않았습니다.

; } @@ -495,6 +503,7 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) { processType={order.processType} currentStep={order.currentStep} workSteps={order.workSteps} + itemCodes={order.items?.map((i) => i.itemCode).filter(Boolean) as string[]} /> diff --git a/src/components/production/WorkOrders/types.ts b/src/components/production/WorkOrders/types.ts index 35d4e496..d47ff561 100644 --- a/src/components/production/WorkOrders/types.ts +++ b/src/components/production/WorkOrders/types.ts @@ -95,6 +95,54 @@ export const BENDING_PROCESS_STEPS: { key: BendingProcessStep; label: string; or { key: 'inspection', label: '검사', order: 4 }, ]; +// BD 코드 종류코드 → 필요 작업단계 매핑 +export const BENDING_STEP_MAP: Record = { + 'R': ['guide_rail', 'inspection'], // 가이드레일-벽면 + 'S': ['guide_rail', 'inspection'], // 가이드레일-측면 + 'C': ['case', 'inspection'], // 케이스 + 'B': ['bottom_finish', 'inspection'], // 하단마감재-스크린 + 'T': ['bottom_finish', 'inspection'], // 하단마감재-철재 + 'L': ['guide_rail', 'inspection'], // L-Bar + 'G': ['guide_rail', 'inspection'], // 연기차단재 +}; + +/** + * BD 코드에서 종류코드 추출 (BD-{종류코드}{규격코드}-{길이코드}) + * 예: BD-RS-30 → 'R', BD-CF-35 → 'C' + */ +export function extractBendingTypeCode(itemCode: string): string | null { + const match = itemCode.match(/^BD-([A-Z])/); + return match ? match[1] : null; +} + +/** + * 작업지시 품목들의 BD 코드로 필요한 작업단계 필터링 + * 여러 품목이면 합집합(union), BD 코드 아닌 품목이면 전체 반환 + */ +export function filterBendingSteps( + itemCodes: string[], + allSteps: typeof BENDING_PROCESS_STEPS +): typeof BENDING_PROCESS_STEPS { + if (!itemCodes.length) return allSteps; + + const neededKeys = new Set(); + let hasNonBdItem = false; + + for (const code of itemCodes) { + const typeCode = extractBendingTypeCode(code); + if (typeCode && BENDING_STEP_MAP[typeCode]) { + BENDING_STEP_MAP[typeCode].forEach((k) => neededKeys.add(k)); + } else { + hasNonBdItem = true; + } + } + + // BD 코드가 아닌 품목이 있거나 매핑 결과가 없으면 전체 반환 + if (hasNonBdItem || neededKeys.size === 0) return allSteps; + + return allSteps.filter((step) => neededKeys.has(step.key)); +} + // 품목 상태 export type ItemStatus = 'waiting' | 'in_progress' | 'completed'; @@ -109,6 +157,7 @@ export interface WorkOrderItem { id: string; no: number; status: ItemStatus; + itemCode: string; productName: string; floorCode: string; // 층/부호 specification: string; // 규격 @@ -255,6 +304,7 @@ export interface WorkOrderItemApi { id: number; work_order_id: number; item_id: number | null; + item?: { id: number; code: string } | null; item_name: string; specification: string | null; quantity: number; @@ -471,6 +521,7 @@ export function transformApiToFrontend(api: WorkOrderApi): WorkOrder { id: String(item.id), no: idx + 1, status: (item.status as ItemStatus) || 'waiting', + itemCode: item.item?.code || '', productName: item.item_name, floorCode: [item.source_order_item?.floor_code, item.source_order_item?.symbol_code].filter(Boolean).join('/') || '-', specification: item.specification || '-',