feat: 자재투입 모달 복수 작업지시품목 병렬 조회 지원

- workOrderItemIds prop 추가 (절곡 등 복수 item 공정 대응)
- Promise.all로 복수 item 자재 병렬 조회 후 합치기
- 각 자재에 소스 workOrderItemId 태깅 (submit 시 올바른 item에 등록)
- 기존 단일 workOrderItemId 호환 유지
This commit is contained in:
2026-02-22 03:04:35 +09:00
parent 90ff585a2e
commit 559af1334b
4 changed files with 83 additions and 15 deletions

View File

@@ -37,7 +37,8 @@ interface MaterialInputModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
order: WorkOrder | null;
workOrderItemId?: number; // 개소(작업지시품목) ID
workOrderItemId?: number; // 개소(작업지시품목) ID (첫 번째 item, 호환용)
workOrderItemIds?: number[]; // 개소 내 모든 작업지시품목 IDs (절곡 등 복수 item)
workOrderItemName?: string; // 개소명 (모달 헤더 표시용)
onComplete?: () => void;
isCompletionFlow?: boolean;
@@ -69,6 +70,7 @@ export function MaterialInputModal({
onOpenChange,
order,
workOrderItemId,
workOrderItemIds,
workOrderItemName,
onComplete,
isCompletionFlow = false,
@@ -190,15 +192,44 @@ export function MaterialInputModal({
return;
}
// 개소별 API vs 전체 API 분기
const result = workOrderItemId
? await getMaterialsForItem(order.id, workOrderItemId)
: await getMaterialsForWorkOrder(order.id);
// 복수 item IDs가 있으면 각각 병렬 조회 후 합치기 (절곡 공정 등)
const itemIds = workOrderItemIds && workOrderItemIds.length > 0
? workOrderItemIds
: workOrderItemId ? [workOrderItemId] : null;
if (result.success) {
setMaterials(result.data);
if (itemIds && itemIds.length > 0) {
// 개소 내 모든 items의 자재를 병렬 조회
const results = await Promise.all(
itemIds.map((id) => getMaterialsForItem(order.id, id))
);
const allMaterials: MaterialForItemInput[] = [];
let hasError = false;
for (let i = 0; i < results.length; i++) {
const result = results[i];
if (result.success) {
// 각 자재에 소스 work_order_item_id 태깅 (submit 시 올바른 item에 등록하기 위함)
const tagged = result.data.map((m) => ({
...m,
workOrderItemId: m.workOrderItemId || itemIds[i],
}));
allMaterials.push(...tagged);
} else {
hasError = true;
}
}
if (allMaterials.length > 0) {
setMaterials(allMaterials);
} else if (hasError) {
toast.error('자재 목록 조회에 실패했습니다.');
}
} else {
toast.error(result.error || '자재 목록 조회에 실패했습니다.');
// 전체 작업지시 기준 조회
const result = await getMaterialsForWorkOrder(order.id);
if (result.success) {
setMaterials(result.data);
} else {
toast.error(result.error || '자재 목록 조회에 실패했습니다.');
}
}
} catch (error) {
if (isNextRedirectError(error)) throw error;
@@ -207,7 +238,7 @@ export function MaterialInputModal({
} finally {
setIsLoading(false);
}
}, [order, workOrderItemId]);
}, [order, workOrderItemId, workOrderItemIds]);
// 모달이 열릴 때 데이터 로드 + 선택 초기화
useEffect(() => {
@@ -246,10 +277,34 @@ export function MaterialInputModal({
setIsSubmitting(true);
try {
// 개소별 API vs 전체 API 분기
const result = workOrderItemId
? await registerMaterialInputForItem(order.id, workOrderItemId, inputs)
: await registerMaterialInput(order.id, inputs);
// 복수 item IDs가 있으면 item별로 그룹핑하여 각각 등록
let result: { success: boolean; error?: string };
if (workOrderItemIds && workOrderItemIds.length > 1) {
// inputs를 work_order_item_id별로 그룹핑
const grouped = new Map<number, { stock_lot_id: number; qty: number }[]>();
for (const input of inputs) {
const woItemId = input.work_order_item_id;
if (woItemId) {
if (!grouped.has(woItemId)) grouped.set(woItemId, []);
grouped.get(woItemId)!.push({ stock_lot_id: input.stock_lot_id, qty: input.qty });
}
}
// 각 item별로 병렬 등록
const results = await Promise.all(
Array.from(grouped.entries()).map(([itemId, itemInputs]) =>
registerMaterialInputForItem(order.id, itemId, itemInputs)
)
);
const hasFailure = results.some((r) => !r.success);
result = hasFailure
? { success: false, error: results.find((r) => !r.success)?.error }
: { success: true };
} else if (workOrderItemId) {
result = await registerMaterialInputForItem(order.id, workOrderItemId, inputs);
} else {
result = await registerMaterialInput(order.id, inputs);
}
if (result.success) {
toast.success('자재 투입이 등록되었습니다.');