fix(WEB): 견적V2 자동 산출 로직 안정화

- pendingAutoCalculate를 useRef로 변경 (무한 렌더링 방지)
- BOM 계산 결과 매핑 로직 수정
- bomMaterials 변환 로직 단순화 (API 응답 직접 사용)
- DevFill 기본 상품코드 수정
This commit is contained in:
2026-01-26 15:30:07 +09:00
parent ada24eef66
commit ff93ab7fa2
2 changed files with 23 additions and 35 deletions

View File

@@ -167,7 +167,7 @@ export function QuoteRegistrationV2({
const [isSaving, setIsSaving] = useState(false);
const [isCalculating, setIsCalculating] = useState(false);
const [previewModalOpen, setPreviewModalOpen] = useState(false);
const [pendingAutoCalculate, setPendingAutoCalculate] = useState(false);
const pendingAutoCalculateRef = useRef(false);
// API 데이터
const [clients, setClients] = useState<Vendor[]>([]);
@@ -203,8 +203,8 @@ export function QuoteRegistrationV2({
code: `${randomPrefix}-${String(index + 1).padStart(2, "0")}`,
openWidth: randomWidth,
openHeight: randomHeight,
productCode: randomProduct?.item_code || "FG-001",
productName: randomProduct?.item_name || "방화셔터",
productCode: randomProduct?.item_code || "FG-SCR-001",
productName: randomProduct?.item_name || "방화 스크린 셔터 (소형)",
quantity: Math.floor(Math.random() * 3) + 1, // 1~3
guideRailType: guideRailTypes[Math.floor(Math.random() * guideRailTypes.length)],
motorPower: motorPowers[Math.floor(Math.random() * motorPowers.length)],
@@ -252,7 +252,7 @@ export function QuoteRegistrationV2({
toast.success(`[DevFill] 테스트 데이터가 채워졌습니다. (${locationCount}개 개소)`);
// 자동 견적 산출 트리거
setPendingAutoCalculate(true);
pendingAutoCalculateRef.current = true;
}, [clients, finishedGoods]));
// ---------------------------------------------------------------------------
@@ -455,20 +455,24 @@ export function QuoteRegistrationV2({
const result = await calculateBomBulk(bomItems);
if (result.success && result.data) {
// API 응답: { summary: { grand_total }, items: [{ index, result: BomCalculationResult }] }
const apiData = result.data as {
summary?: { grand_total: number };
items?: Array<{ index: number; result: BomCalculationResult }>;
};
const bomItems = apiData.items || [];
// 결과 반영
const updatedLocations = formData.locations.map((loc, index) => {
const bomResult = apiData.items?.find((item) => item.index === index);
if (bomResult?.result) {
const bomItem = bomItems.find((item) => item.index === index);
const bomResult = bomItem?.result;
if (bomResult) {
return {
...loc,
unitPrice: bomResult.result.grand_total,
totalPrice: bomResult.result.grand_total * loc.quantity,
bomResult: bomResult.result,
unitPrice: bomResult.grand_total,
totalPrice: bomResult.grand_total * loc.quantity,
bomResult: bomResult,
};
}
return loc;
@@ -494,15 +498,14 @@ export function QuoteRegistrationV2({
// DevFill 후 자동 견적 산출
useEffect(() => {
if (pendingAutoCalculate && formData.locations.length > 0 && calculateRef.current) {
setPendingAutoCalculate(false);
// 약간의 딜레이 후 산출 실행 (상태 업데이트 완료 대기)
const timer = setTimeout(() => {
calculateRef.current?.();
}, 100);
return () => clearTimeout(timer);
if (pendingAutoCalculateRef.current && formData.locations.length > 0) {
pendingAutoCalculateRef.current = false;
// 상태 업데이트 완료 후 산출 실행
setTimeout(() => {
handleCalculate();
}, 50);
}
}, [pendingAutoCalculate, formData.locations.length]);
}, [formData.locations, handleCalculate]);
// 저장 (임시/최종)
const handleSave = useCallback(async (saveType: "temporary" | "final") => {

View File

@@ -541,24 +541,9 @@ export function transformQuoteToFormData(quote: Quote): QuoteFormData {
};
}),
// BOM 자재 목록:
// - calcInputs가 있으면: quote.items에 BOM 자재가 저장되어 있음 (quote_items 테이블)
// - calcInputs가 없으면: quote.bomMaterials 사용 (별도 bom_materials 필드가 있는 경우)
bomMaterials: calcInputs.length > 0
? quote.items.map((item, index) => ({
itemIndex: index,
finishedGoodsCode: '',
itemCode: item.itemCode || '', // 품목코드 사용
itemName: item.productName,
itemType: '',
itemCategory: '',
specification: item.specification || '',
unit: item.unit || '',
quantity: item.quantity,
unitPrice: item.unitPrice,
totalPrice: item.totalAmount,
processType: '',
}))
: quote.bomMaterials,
// - API에서 bom_materials를 계산하여 반환 (item_type 포함)
// - quote.bomMaterials가 있으면 그대로 사용
bomMaterials: quote.bomMaterials,
};
}