diff --git a/src/components/quotes/LocationDetailPanel.tsx b/src/components/quotes/LocationDetailPanel.tsx index 7efcf2c9..d7045f51 100644 --- a/src/components/quotes/LocationDetailPanel.tsx +++ b/src/components/quotes/LocationDetailPanel.tsx @@ -559,32 +559,64 @@ export function LocationDetailPanel({ const existingBomResult = location.bomResult; if (!existingBomResult) return; - // 해당 아이템 찾아서 수량 및 금액 업데이트 - const updatedItems = (existingBomResult.items || []).map((bomItem: any, i: number) => { - if (bomItemsByTab[tab.value]?.[index] === bomItem) { - const newTotalPrice = (bomItem.unit_price || 0) * newQty; - return { - ...bomItem, - quantity: newQty, - total_price: newTotalPrice, - }; + const targetItem = item; + const targetCode = targetItem.item_code; + const targetName = targetItem.item_name; + + // grouped_items 내 해당 탭에서 index로 매칭하여 items에서 찾기 + const groupItems = existingBomResult.grouped_items?.[tab.value]?.items || []; + const matchedItem = groupItems[index]; + if (!matchedItem) return; + + // items 배열에서 같은 item_code + item_name 매칭 (동일 코드 복수 가능하므로 순서 기반) + let matchCount = 0; + const updatedItems = (existingBomResult.items || []).map((bomItem: any) => { + if (bomItem.item_code === targetCode && bomItem.item_name === targetName) { + matchCount++; + // grouped_items 내 순서와 items 내 순서가 같은 아이템 찾기 + if (bomItem.quantity === targetItem.quantity && bomItem.unit_price === targetItem.unit_price) { + const newTotalPrice = (bomItem.unit_price || 0) * newQty; + return { ...bomItem, quantity: newQty, total_price: newTotalPrice }; + } } return bomItem; }); + // grouped_items도 업데이트 + const updatedGroupedItems: Record = { ...existingBomResult.grouped_items }; + if (updatedGroupedItems[tab.value]) { + const updatedTabItems = [...(updatedGroupedItems[tab.value].items || [])]; + if (updatedTabItems[index]) { + const newTotalPrice = (updatedTabItems[index].unit_price || 0) * newQty; + updatedTabItems[index] = { ...updatedTabItems[index], quantity: newQty, total_price: newTotalPrice }; + } + updatedGroupedItems[tab.value] = { ...updatedGroupedItems[tab.value], items: updatedTabItems }; + } + // grand_total 재계산 const newGrandTotal = updatedItems.reduce( - (sum: number, item: any) => sum + (item.total_price || 0), - 0 + (sum: number, bi: any) => sum + (bi.total_price || 0), 0 ); - // location 업데이트 (unitPrice, totalPrice 포함) + // subtotals 재계산 + const updatedSubtotals = { ...existingBomResult.subtotals }; + Object.entries(updatedGroupedItems).forEach(([key, group]) => { + const groupItemsList = (group as any)?.items || []; + const subtotal = groupItemsList.reduce((s: number, gi: any) => s + (gi.total_price || 0), 0); + const existing = updatedSubtotals[key]; + if (typeof existing === 'object' && existing !== null) { + updatedSubtotals[key] = { ...existing, count: groupItemsList.length, subtotal }; + } + }); + onUpdateLocation(location.id, { unitPrice: newGrandTotal, totalPrice: newGrandTotal * location.quantity, bomResult: { ...existingBomResult, items: updatedItems, + grouped_items: updatedGroupedItems, + subtotals: updatedSubtotals, grand_total: newGrandTotal, }, }); @@ -602,18 +634,62 @@ export function LocationDetailPanel({ disabled={disabled} onClick={() => { if (!location) return; - // 품목 삭제 로직 const existingBomResult = location.bomResult; if (!existingBomResult) return; - const updatedItems = (existingBomResult.items || []).filter( - (_: any, i: number) => !(bomItemsByTab[tab.value]?.[index] === _) + // 삭제 대상: 현재 탭의 grouped_items에서 index번째 아이템 + const groupItems: any[] = existingBomResult.grouped_items?.[tab.value]?.items || []; + const targetItem = groupItems[index]; + if (!targetItem) return; + + // grouped_items에서 해당 아이템 제거 + const updatedGroupedItems: Record = { ...existingBomResult.grouped_items }; + if (updatedGroupedItems[tab.value]) { + const updatedTabItems = [...(updatedGroupedItems[tab.value].items || [])]; + updatedTabItems.splice(index, 1); + updatedGroupedItems[tab.value] = { ...updatedGroupedItems[tab.value], items: updatedTabItems }; + } + + // items 배열에서도 제거 (item_code + item_name + quantity + unit_price 매칭) + let removed = false; + const updatedItems = (existingBomResult.items || []).filter((bi: any) => { + if (!removed && + bi.item_code === targetItem.item_code && + bi.item_name === targetItem.item_name && + bi.quantity === targetItem.quantity && + bi.unit_price === targetItem.unit_price + ) { + removed = true; + return false; + } + return true; + }); + + // grand_total 재계산 + const newGrandTotal = updatedItems.reduce( + (sum: number, bi: any) => sum + (bi.total_price || 0), 0 ); + // subtotals 재계산 + const updatedSubtotals = { ...existingBomResult.subtotals }; + Object.entries(updatedGroupedItems).forEach(([key, group]) => { + const groupItemsList = (group as any)?.items || []; + const subtotal = groupItemsList.reduce((s: number, gi: any) => s + (gi.total_price || 0), 0); + const existing = updatedSubtotals[key]; + if (typeof existing === 'object' && existing !== null) { + updatedSubtotals[key] = { ...existing, count: groupItemsList.length, subtotal }; + } + }); + onUpdateLocation(location.id, { + unitPrice: newGrandTotal, + totalPrice: newGrandTotal * location.quantity, bomResult: { ...existingBomResult, items: updatedItems, + grouped_items: updatedGroupedItems, + subtotals: updatedSubtotals, + grand_total: newGrandTotal, }, }); }}