diff --git a/src/components/production/WorkerScreen/MaterialInputModal.tsx b/src/components/production/WorkerScreen/MaterialInputModal.tsx index 09052c14..1d026227 100644 --- a/src/components/production/WorkerScreen/MaterialInputModal.tsx +++ b/src/components/production/WorkerScreen/MaterialInputModal.tsx @@ -99,16 +99,22 @@ export function MaterialInputModal({ const getLotKey = (material: MaterialForInput) => String(material.stockLotId ?? `item-${material.itemId}`); - // 품목별 그룹핑 + // 품목별 그룹핑 (BOM 엔트리별 고유키 사용 — 같은 item_id라도 category+partType 다르면 별도 그룹) const materialGroups: MaterialGroup[] = useMemo(() => { - // dynamic_bom 항목은 (itemId, workOrderItemId) 쌍으로 그룹핑 const groups = new Map(); for (const m of materials) { - const groupKey = m.workOrderItemId ? `${m.itemId}_${m.workOrderItemId}` : String(m.itemId); + const itemInput = m as unknown as MaterialForItemInput; + const groupKey = itemInput.bomGroupKey + ?? (m.workOrderItemId ? `${m.itemId}_${m.workOrderItemId}` : String(m.itemId)); const existing = groups.get(groupKey) || []; existing.push(m); groups.set(groupKey, existing); } + // 작업일지와 동일한 카테고리 순서 + const categoryOrder: Record = { + guideRail: 0, bottomBar: 1, shutterBox: 2, smokeBarrier: 3, + }; + return Array.from(groups.entries()).map(([groupKey, lots]) => { const first = lots[0]; const itemInput = first as unknown as MaterialForItemInput; @@ -129,6 +135,10 @@ export function MaterialInputModal({ partType: first.partType, category: first.category, }; + }).sort((a, b) => { + const catA = categoryOrder[a.category ?? ''] ?? 99; + const catB = categoryOrder[b.category ?? ''] ?? 99; + return catA - catB; }); }, [materials]); @@ -344,7 +354,15 @@ export function MaterialInputModal({ ) : (
- {materialGroups.map((group) => { + {materialGroups.map((group, groupIdx) => { + // 같은 카테고리 내 순번 계산 (①②③...) + const categoryIndex = group.category + ? materialGroups.slice(0, groupIdx).filter(g => g.category === group.category).length + : -1; + const circledNumbers = ['①','②','③','④','⑤','⑥','⑦','⑧','⑨','⑩']; + const circledNum = categoryIndex >= 0 && categoryIndex < circledNumbers.length + ? circledNumbers[categoryIndex] : ''; + const groupAllocated = group.lots.reduce( (sum, lot) => sum + (allocations.get(getLotKey(lot)) || 0), 0 @@ -372,6 +390,11 @@ export function MaterialInputModal({ group.category} )} + {group.partType && ( + + {circledNum}{group.partType} + + )} {group.materialName} diff --git a/src/components/production/WorkerScreen/actions.ts b/src/components/production/WorkerScreen/actions.ts index e6b04708..8ca84135 100644 --- a/src/components/production/WorkerScreen/actions.ts +++ b/src/components/production/WorkerScreen/actions.ts @@ -316,6 +316,7 @@ export async function registerMaterialInput( export interface MaterialForItemInput extends MaterialForInput { alreadyInputted: number; // 이미 투입된 수량 remainingRequiredQty: number; // 남은 필요 수량 + bomGroupKey?: string; // BOM 엔트리별 고유 그룹키 (category+partType 기반) } export async function getMaterialsForItem( @@ -336,6 +337,7 @@ export async function getMaterialsForItem( receipt_date: string | null; supplier: string | null; // dynamic_bom 추가 필드 work_order_item_id?: number; lot_prefix?: string; part_type?: string; category?: string; + bom_group_key?: string; } const result = await executeServerAction({ url: `${API_URL}/api/v1/work-orders/${workOrderId}/items/${itemId}/materials`, @@ -354,6 +356,7 @@ export async function getMaterialsForItem( remainingRequiredQty: item.remaining_required_qty, workOrderItemId: item.work_order_item_id, lotPrefix: item.lot_prefix, partType: item.part_type, category: item.category, + bomGroupKey: item.bom_group_key, })), }; }