fix(WEB): 견적 품목 삭제/수량변경 기능 수정 - 참조비교를 값비교로 변경

- 삭제: grouped_items에서 index로 대상 특정 후 값 비교로 items에서 제거
- 수량변경: 동일하게 참조비교 → 값비교로 수정
- 삭제/수량변경 시 grouped_items, subtotals, grand_total 동기 업데이트
This commit is contained in:
2026-02-04 11:28:35 +09:00
parent 5af6500671
commit b5095602ca

View File

@@ -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<string, any> = { ...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<string, any> = { ...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,
},
});
}}