From f695977cbcac1fcef485c7303d7650b8eecf8460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 19 Feb 2026 20:59:02 +0900 Subject: [PATCH] =?UTF-8?q?fix(WEB):=20=EC=9E=91=EC=97=85=EC=9D=BC?= =?UTF-8?q?=EC=A7=80=20=EB=8B=B4=EB=8B=B9=EC=9E=90=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=AC=EB=9E=AB=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B8=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 작업일지(스크린/슬랫/절곡): 담당자→수주담당자, 연락처→담당자연락처, 생산담당자 분리 표시 - SlatWorkLogContent: 방화유리 수량을 slatInfo.glassQty에서 표시 - SlatInfo 타입에 glassQty 추가 (WorkOrders/types, WorkerScreen/types) - WorkerScreen: salesManager/managerPhone API 연동 - slat_info 변환 로직에 glass_qty 매핑 추가 --- .../production/ProductionDashboard/types.ts | 2 + .../documents/BendingWorkLogContent.tsx | 4 +- .../documents/ScreenWorkLogContent.tsx | 4 +- .../documents/SlatWorkLogContent.tsx | 90 ++++++++++++------- src/components/production/WorkOrders/types.ts | 6 ++ .../production/WorkerScreen/actions.ts | 8 +- .../production/WorkerScreen/index.tsx | 16 ++-- .../production/WorkerScreen/mockData.ts | 4 +- .../production/WorkerScreen/types.ts | 1 + 9 files changed, 87 insertions(+), 48 deletions(-) diff --git a/src/components/production/ProductionDashboard/types.ts b/src/components/production/ProductionDashboard/types.ts index 04f429ad..bf3abc27 100644 --- a/src/components/production/ProductionDashboard/types.ts +++ b/src/components/production/ProductionDashboard/types.ts @@ -30,6 +30,8 @@ export interface WorkOrder { delayDays?: number; // 지연 일수 instruction?: string; // 지시사항 salesOrderNo?: string; // 수주번호 + salesManager?: string; // 수주 담당자 (orders.options.manager_name) + managerPhone?: string; // 담당자 연락처 (orders.client_contact) teamId?: number | null; // 배정 부서 ID (work_orders.team_id) teamName?: string; // 배정 부서명 processDepartment?: string; // 공정 담당부서명 (processes.department) diff --git a/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx b/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx index e86cba61..bc285a92 100644 --- a/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx +++ b/src/components/production/WorkOrders/documents/BendingWorkLogContent.tsx @@ -88,13 +88,13 @@ export function BendingWorkLogContent({ data: order }: BendingWorkLogContentProp 담당자 - {primaryAssignee} + {order.salesOrderWriter || '-'} 제품 LOT NO {order.lotNo} 연락처 - - + {order.clientContact || '-'} 생산담당자 {primaryAssignee} diff --git a/src/components/production/WorkOrders/documents/ScreenWorkLogContent.tsx b/src/components/production/WorkOrders/documents/ScreenWorkLogContent.tsx index 4dba5a84..e7064a17 100644 --- a/src/components/production/WorkOrders/documents/ScreenWorkLogContent.tsx +++ b/src/components/production/WorkOrders/documents/ScreenWorkLogContent.tsx @@ -223,13 +223,13 @@ export function ScreenWorkLogContent({ data: order, materialLots = [] }: ScreenW 담당자 - {primaryAssignee} + {order.salesOrderWriter || '-'} 제품 LOT NO {order.lotNo} 연락처 - - + {order.clientContact || '-'} 생산담당자 {primaryAssignee} diff --git a/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx b/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx index b2ff7a5d..6dfe356d 100644 --- a/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx +++ b/src/components/production/WorkOrders/documents/SlatWorkLogContent.tsx @@ -69,6 +69,26 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL const lotNoList = materialLots.map(lot => lot.lot_no).filter(Boolean); const lotNoDisplay = lotNoList.length > 0 ? lotNoList.join(', ') : ''; + // 슬랫 계산: 매수(세로) = floor(height / 72) + 1 + const calcSlatCount = (height?: number) => height ? Math.floor(height / 72) + 1 : 0; + + // 코일 사용량 = ((가로 + 4) × 매수) / 1000 + (304 × 3 × 조인트바) / 1000 + const calcCoilUsage = (width?: number, height?: number, jointBar?: number) => { + const slatCount = calcSlatCount(height); + const w = width || 0; + const jb = jointBar || 0; + return Math.round(((w + 4) * slatCount / 1000 + (304 * 3 * jb) / 1000) * 10) / 10; + }; + + // 합계 계산 + let totalCoilUsage = 0; + let totalJointBar = 0; + items.forEach(item => { + const jb = item.slatInfo?.jointBar || 0; + totalJointBar += jb; + totalCoilUsage += calcCoilUsage(item.width, item.height, jb); + }); + return (
{/* ===== 헤더 영역 ===== */} @@ -130,13 +150,13 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL 담당자 - {primaryAssignee} + {order.salesOrderWriter || '-'} 제품 LOT NO {order.lotNo} 연락처 - - + {order.clientContact || '-'} 생산담당자 {primaryAssignee} @@ -153,40 +173,46 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL - - - - - - - - + + + + + + + + - - - + + + {items.length > 0 ? ( - items.map((item, idx) => ( - - - - - - - - - - - - - )) + items.map((item, idx) => { + const slatCount = calcSlatCount(item.height); + const jointBar = item.slatInfo?.jointBar || 0; + const glassQty = item.slatInfo?.glassQty || 0; + const coilUsage = calcCoilUsage(item.width, item.height, jointBar); + return ( + + + + + + + + + + + + + ); + }) ) : ( - @@ -198,10 +224,10 @@ export function SlatWorkLogContent({ data: order, materialLots = [] }: SlatWorkL
No.입고 LOT
NO
방화유리
수량
제품명제작사이즈(mm) - 미미제외조인트바
수량
코일
사용량
설치홈/
부호
No.입고 LOT
NO
방화유리
수량
제품명제작사이즈(mm) - 미미제외조인트바
수량
코일
사용량
설치홈/
부호
가로세로매수
(세로)
가로세로매수
(세로)
{idx + 1}{lotNoDisplay}-{item.productName}{fmt(item.width)}{fmt(item.height)}---{getSymbolCode(item.floorCode)}
{idx + 1}{lotNoDisplay}{glassQty > 0 ? fmt(glassQty) : '-'}{item.productName}{fmt(item.width)}{fmt(item.height)}{slatCount > 0 ? fmt(slatCount) : '-'}{jointBar > 0 ? fmt(jointBar) : '-'}{coilUsage > 0 ? coilUsage.toFixed(1) : '-'}{getSymbolCode(item.floorCode)}
+ 등록된 품목이 없습니다.
- - - - + + + +
생산량 합계 [m²]조인트바 합계생산량 합계 [m²]{totalCoilUsage > 0 ? totalCoilUsage.toFixed(1) : ''}조인트바 합계{totalJointBar > 0 ? fmt(totalJointBar) : ''}
diff --git a/src/components/production/WorkOrders/types.ts b/src/components/production/WorkOrders/types.ts index 12e4a2fa..40202016 100644 --- a/src/components/production/WorkOrders/types.ts +++ b/src/components/production/WorkOrders/types.ts @@ -118,6 +118,7 @@ export interface WorkOrderItem { unit: string; // 단위 orderNodeId: number | null; // 개소 ID orderNodeName: string; // 개소명 + slatInfo?: { length: number; slatCount: number; jointBar: number; glassQty: number }; // 슬랫 공정 정보 } // 전개도 상세 (절곡용) @@ -347,6 +348,7 @@ export interface WorkOrderApi { created_at?: string; quantity?: number; root_nodes_count?: number; + options?: { manager_name?: string; [key: string]: unknown }; client?: { id: number; name: string }; writer?: { id: number; name: string }; }; @@ -467,6 +469,10 @@ export function transformApiToFrontend(api: WorkOrderApi): WorkOrder { unit: item.unit || '-', orderNodeId: item.source_order_item?.order_node_id ?? null, orderNodeName: item.source_order_item?.node?.name || '-', + slatInfo: item.options?.slat_info ? (() => { + const si = item.options.slat_info as { length?: number; slat_count?: number; joint_bar?: number; glass_qty?: number }; + return { length: si.length || 0, slatCount: si.slat_count || 0, jointBar: si.joint_bar || 0, glassQty: si.glass_qty || 0 }; + })() : undefined, })), bendingDetails: api.bending_detail ? transformBendingDetail(api.bending_detail) : undefined, issues: (api.issues || []).map(issue => ({ diff --git a/src/components/production/WorkerScreen/actions.ts b/src/components/production/WorkerScreen/actions.ts index 3dc71e45..07e3e6a9 100644 --- a/src/components/production/WorkerScreen/actions.ts +++ b/src/components/production/WorkerScreen/actions.ts @@ -39,6 +39,8 @@ interface WorkOrderApiItem { id: number; order_no: string; client?: { id: number; name: string }; + client_contact?: string; + options?: { manager_name?: string; [key: string]: unknown }; root_nodes_count?: number; }; team_id?: number | null; @@ -189,6 +191,8 @@ function transformToWorkerScreenFormat(api: WorkOrderApiItem): WorkOrder { delayDays, instruction: api.memo || undefined, salesOrderNo: api.sales_order?.order_no || undefined, + salesManager: api.sales_order?.options?.manager_name as string || undefined, + managerPhone: api.sales_order?.client_contact || undefined, teamId: api.team_id ?? null, teamName: api.team?.name || undefined, processDepartment: api.process?.department || undefined, @@ -635,8 +639,8 @@ export async function getWorkOrderDetail( workItem.cuttingInfo = { width: ci.width, sheets: ci.sheets }; } if (opts.slat_info) { - const si = opts.slat_info as { length: number; slat_count: number; joint_bar: number }; - workItem.slatInfo = { length: si.length, slatCount: si.slat_count, jointBar: si.joint_bar }; + const si = opts.slat_info as { length: number; slat_count: number; joint_bar: number; glass_qty: number }; + workItem.slatInfo = { length: si.length, slatCount: si.slat_count, jointBar: si.joint_bar, glassQty: si.glass_qty || 0 }; } if (opts.bending_info) { const bi = opts.bending_info as { diff --git a/src/components/production/WorkerScreen/index.tsx b/src/components/production/WorkerScreen/index.tsx index 60a54fba..b3efe076 100644 --- a/src/components/production/WorkerScreen/index.tsx +++ b/src/components/production/WorkerScreen/index.tsx @@ -118,7 +118,7 @@ const MOCK_ITEMS: Record = { { id: 'mock-l1', itemNo: 1, itemCode: 'KQTS01', itemName: '슬랫코일', floor: '1층', code: 'FSS-01', width: 8260, height: 8350, quantity: 2, processType: 'slat', - slatInfo: { length: 3910, slatCount: 40, jointBar: 4 }, + slatInfo: { length: 3910, slatCount: 40, jointBar: 4, glassQty: 2 }, steps: [ { id: 'l1-1', name: '자재투입', isMaterialInput: true, isCompleted: true }, { id: 'l1-2', name: '포밍/절단', isMaterialInput: false, isCompleted: false }, @@ -132,7 +132,7 @@ const MOCK_ITEMS: Record = { { id: 'mock-l2', itemNo: 2, itemCode: 'KQTS03', itemName: '슬랫코일(광폭)', floor: '2층', code: 'FSS-02', width: 10500, height: 6200, quantity: 3, processType: 'slat', - slatInfo: { length: 5200, slatCount: 55, jointBar: 6 }, + slatInfo: { length: 5200, slatCount: 55, jointBar: 6, glassQty: 3 }, steps: [ { id: 'l2-1', name: '자재투입', isMaterialInput: true, isCompleted: false }, { id: 'l2-2', name: '포밍/절단', isMaterialInput: false, isCompleted: false }, @@ -709,8 +709,8 @@ export default function WorkerScreen() { workItem.cuttingInfo = { width: ci.width, sheets: ci.sheets }; } if (opts.slat_info) { - const si = opts.slat_info as { length: number; slat_count: number; joint_bar: number }; - workItem.slatInfo = { length: si.length, slatCount: si.slat_count, jointBar: si.joint_bar }; + const si = opts.slat_info as { length: number; slat_count: number; joint_bar: number; glass_qty: number }; + workItem.slatInfo = { length: si.length, slatCount: si.slat_count, jointBar: si.joint_bar, glassQty: si.glass_qty || 0 }; } if (opts.bending_info) { const bi = opts.bending_info as { @@ -864,8 +864,8 @@ export default function WorkerScreen() { salesOrderNo: apiOrder.salesOrderNo || '-', siteName: apiOrder.projectName || '-', client: apiOrder.client || '-', - salesManager: apiOrder.assignees?.[0] || '-', - managerPhone: '-', + salesManager: apiOrder.salesManager || '-', + managerPhone: apiOrder.managerPhone || '-', shippingDate: apiOrder.dueDate ? new Date(apiOrder.dueDate).toLocaleDateString('ko-KR') : '-', }; } @@ -892,8 +892,8 @@ export default function WorkerScreen() { salesOrderNo: first.salesOrderNo || '-', siteName: first.projectName || '-', client: first.client || '-', - salesManager: first.assignees?.[0] || '-', - managerPhone: '-', + salesManager: first.salesManager || '-', + managerPhone: first.managerPhone || '-', shippingDate: first.dueDate ? new Date(first.dueDate).toLocaleDateString('ko-KR') : '-', }; }, [filteredWorkOrders, selectedSidebarOrderId, activeProcessTabKey]); diff --git a/src/components/production/WorkerScreen/mockData.ts b/src/components/production/WorkerScreen/mockData.ts index 34285d2d..fb0f9c02 100644 --- a/src/components/production/WorkerScreen/mockData.ts +++ b/src/components/production/WorkerScreen/mockData.ts @@ -60,7 +60,7 @@ export const MOCK_ITEMS: Record = { { id: 'mock-l1', itemNo: 1, itemCode: 'KQTS01', itemName: '슬랫코일', floor: '1층', code: 'FSS-01', width: 8260, height: 8350, quantity: 2, processType: 'slat', - slatInfo: { length: 3910, slatCount: 40, jointBar: 4 }, + slatInfo: { length: 3910, slatCount: 40, jointBar: 4, glassQty: 2 }, steps: [ { id: 'l1-1', name: '자재투입', isMaterialInput: true, isCompleted: true }, { id: 'l1-2', name: '포밍/절단', isMaterialInput: false, isCompleted: false }, @@ -73,7 +73,7 @@ export const MOCK_ITEMS: Record = { { id: 'mock-l2', itemNo: 2, itemCode: 'KQTS03', itemName: '슬랫코일(광폭)', floor: '2층', code: 'FSS-02', width: 10500, height: 6200, quantity: 3, processType: 'slat', - slatInfo: { length: 5200, slatCount: 55, jointBar: 6 }, + slatInfo: { length: 5200, slatCount: 55, jointBar: 6, glassQty: 3 }, steps: [ { id: 'l2-1', name: '자재투입', isMaterialInput: true, isCompleted: false }, { id: 'l2-2', name: '포밍/절단', isMaterialInput: false, isCompleted: false }, diff --git a/src/components/production/WorkerScreen/types.ts b/src/components/production/WorkerScreen/types.ts index 9b0846e2..235c6f33 100644 --- a/src/components/production/WorkerScreen/types.ts +++ b/src/components/production/WorkerScreen/types.ts @@ -77,6 +77,7 @@ export interface SlatInfo { length: number; // 길이 (mm) slatCount: number; // 슬랫 매수 jointBar: number; // 조인트바 개수 + glassQty: number; // 방화유리 수량 } // ===== 슬랫 조인트바 전용 정보 =====