70 lines
2.1 KiB
TypeScript
70 lines
2.1 KiB
TypeScript
import { useCallback } from "react";
|
|
import { CalculationRule } from "./types";
|
|
|
|
/**
|
|
* 계산 필드 자동 업데이트 훅
|
|
*/
|
|
export function useCalculation(calculationRules: CalculationRule[] = []) {
|
|
/**
|
|
* 단일 행의 계산 필드 업데이트
|
|
*/
|
|
const calculateRow = useCallback(
|
|
(row: any): any => {
|
|
if (calculationRules.length === 0) return row;
|
|
|
|
const updatedRow = { ...row };
|
|
|
|
for (const rule of calculationRules) {
|
|
try {
|
|
// formula에서 필드명 자동 추출 (영문자, 숫자, 언더스코어로 구성된 단어)
|
|
let formula = rule.formula;
|
|
const fieldMatches = formula.match(/[a-zA-Z_][a-zA-Z0-9_]*/g) || [];
|
|
|
|
// 추출된 필드명들을 사용 (dependencies가 없으면 자동 추출 사용)
|
|
const dependencies = rule.dependencies && rule.dependencies.length > 0
|
|
? rule.dependencies
|
|
: fieldMatches;
|
|
|
|
// 필드명을 실제 값으로 대체
|
|
for (const dep of dependencies) {
|
|
// 결과 필드는 제외
|
|
if (dep === rule.result) continue;
|
|
|
|
// 이전 계산 결과(updatedRow)를 우선 사용, 없으면 원본(row) 사용
|
|
const value = parseFloat(updatedRow[dep] ?? row[dep]) || 0;
|
|
// 정확한 필드명만 대체 (단어 경계 사용)
|
|
formula = formula.replace(new RegExp(`\\b${dep}\\b`, "g"), value.toString());
|
|
}
|
|
|
|
// 계산 실행 (Function 사용)
|
|
const result = new Function(`return ${formula}`)();
|
|
updatedRow[rule.result] = result;
|
|
} catch (error) {
|
|
console.error(`계산 오류 (${rule.formula}):`, error);
|
|
updatedRow[rule.result] = 0;
|
|
}
|
|
}
|
|
|
|
return updatedRow;
|
|
},
|
|
[calculationRules]
|
|
);
|
|
|
|
/**
|
|
* 전체 데이터의 계산 필드 업데이트
|
|
*/
|
|
const calculateAll = useCallback(
|
|
(data: any[]): any[] => {
|
|
return data.map((row) => calculateRow(row));
|
|
},
|
|
[calculateRow]
|
|
);
|
|
|
|
return {
|
|
calculateRow,
|
|
calculateAll,
|
|
};
|
|
}
|
|
|
|
|