feat: 견적 V2 품목 검색 API 연동 및 수동 품목 관리 개선

- ItemSearchModal: API 프록시 라우트 연동, 검색 유효성 검사 (영문/한글 1자 이상)
- items.ts: HttpOnly 쿠키 인증을 위한 프록시 라우트 사용
- LocationDetailPanel: 수동 품목 추가 시 subtotals/grouped_items 동시 업데이트
- QuoteRegistrationV2: 견적 산출 시 수동 추가 품목(is_manual) 보존
- 상세별 합계에서 수동 추가 품목이 카테고리별로 표시되도록 개선
This commit is contained in:
2026-01-27 14:28:17 +09:00
parent 815ed9267e
commit 05fd5b32f2
6 changed files with 389 additions and 105 deletions

View File

@@ -618,7 +618,91 @@ export function LocationDetailPanel({
open={itemSearchOpen}
onOpenChange={setItemSearchOpen}
onSelectItem={(item) => {
console.log(`[테스트] 품목 선택: ${item.code} - ${item.name} (탭: ${activeTab})`);
if (!location) return;
// 탭 → process_group_key 매핑
const tabToProcessGroup: Record<string, string> = {
body: "screen",
"guide-rail": "bending",
case: "steel",
bottom: "electric",
motor: "motor",
accessory: "accessory",
};
const processGroupKey = tabToProcessGroup[activeTab] || "screen";
const processGroupLabel = DETAIL_TABS.find((t) => t.value === activeTab)?.label || activeTab;
// 새 품목 생성 (수동 추가 플래그 포함)
const newItem: BomCalculationResultItem & { process_group_key?: string; is_manual?: boolean } = {
item_code: item.code,
item_name: item.name,
specification: item.specification || "",
unit: "EA",
quantity: 1,
unit_price: 0,
total_price: 0,
process_group: processGroupLabel,
process_group_key: processGroupKey,
is_manual: true, // 수동 추가 품목 표시
};
// 기존 bomResult 가져오기
const existingBomResult = location.bomResult || {
finished_goods: { code: location.productCode || "", name: location.productName || "" },
subtotals: {},
grouped_items: {},
grand_total: 0,
items: [],
};
// 기존 items에 새 아이템 추가
const existingItems = existingBomResult.items || [];
const updatedItems = [...existingItems, newItem];
// subtotals 업데이트 (해당 카테고리의 count, subtotal 증가)
const existingSubtotals = existingBomResult.subtotals || {};
const categorySubtotal = existingSubtotals[processGroupKey] || {
name: processGroupLabel,
count: 0,
subtotal: 0,
};
const updatedSubtotals = {
...existingSubtotals,
[processGroupKey]: {
...categorySubtotal,
name: processGroupLabel,
count: (categorySubtotal.count || 0) + 1,
subtotal: (categorySubtotal.subtotal || 0) + (newItem.total_price || 0),
},
};
// grouped_items 업데이트 (해당 카테고리의 items 배열에 추가)
const existingGroupedItems = existingBomResult.grouped_items || {};
const categoryGroupedItems = existingGroupedItems[processGroupKey] || { items: [] };
const updatedGroupedItems = {
...existingGroupedItems,
[processGroupKey]: {
...categoryGroupedItems,
items: [...(categoryGroupedItems.items || []), newItem],
},
};
// grand_total 업데이트
const updatedGrandTotal = (existingBomResult.grand_total || 0) + (newItem.total_price || 0);
const updatedBomResult = {
...existingBomResult,
items: updatedItems,
subtotals: updatedSubtotals,
grouped_items: updatedGroupedItems,
grand_total: updatedGrandTotal,
};
// location 업데이트
onUpdateLocation(location.id, { bomResult: updatedBomResult });
console.log(`[품목 추가] ${item.code} - ${item.name}${processGroupLabel} (${processGroupKey})`);
}}
tabLabel={DETAIL_TABS.find((t) => t.value === activeTab)?.label}
/>