import type { DataFieldValueCallback, ComputedFieldConfig } from "@/components/type/DataGrid.type"; /** 값 → 숫자 변환 (null/undefined/빈문자열 → 0) */ function toNum(v: unknown): number { if (v == null || v === "") return 0; if (typeof v === "number") return isNaN(v) ? 0 : v; const n = parseFloat(String(v).replace(/,/g, "")); return isNaN(n) ? 0 : n; } /** 소수점 첫째 자리 이후 버림 (반올림 없음) */ function truncateToFirstDecimal(value: number): number { if (!Number.isFinite(value)) return 0; return Math.floor(value * 10) / 10; } /** 합계용 valueCallback 생성 */ export function createSumValueCallback(fieldNames: string[]): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { let sum = 0; for (const f of fieldNames) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) sum += toNum(values[idx]); } return sum; }; } /** 평균용 valueCallback 생성 */ export function createAvgValueCallback(fieldNames: string[]): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { let sum = 0; let count = 0; for (const f of fieldNames) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) { const val = toNum(values[idx]); sum += val; count += 1; } } return count > 0 ? sum / count : 0; }; } /** 차이값: fields[0] − fields[1] (예: 매설연장 − 철거연장) */ export function createDifferenceValueCallback(fields: string[]): DataFieldValueCallback { const a = fields[0] ?? ""; const b = fields[1] ?? ""; return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { if (!a || !b) return 0; const ia = fieldNamesArr.indexOf(a); const ib = fieldNamesArr.indexOf(b); const va = ia >= 0 ? toNum(values[ia]) : 0; const vb = ib >= 0 ? toNum(values[ib]) : 0; return va - vb; }; } /** (여러 필드 합) - (여러 필드 합) */ export function createDifferenceOfSumsValueCallback( minuendFields: string[], subtrahendFields: string[], ): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { const sumOf = (fields: string[]) => { let sum = 0; for (const f of fields) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) sum += toNum(values[idx]); } return sum; }; return sumOf(minuendFields) - sumOf(subtrahendFields); }; } /** 최대값용 valueCallback 생성 */ export function createMaxValueCallback(fieldNames: string[]): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { let max = Number.NEGATIVE_INFINITY; for (const f of fieldNames) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) { const val = toNum(values[idx]); max = Math.max(max, val); } } return max === Number.NEGATIVE_INFINITY ? 0 : max; }; } /** 단일 필드값 vs 여러 필드 합계 중 최대값 */ export function createMaxOfFieldAndSumValueCallback( baseField: string, sumFields: string[], ): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { const baseIdx = fieldNamesArr.indexOf(baseField); const baseVal = baseIdx >= 0 ? toNum(values[baseIdx]) : 0; let sumVal = 0; for (const f of sumFields) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) sumVal += toNum(values[idx]); } return Math.max(baseVal, sumVal); }; } /** 여러 그룹의 max(baseField, sum(sumFields)) 값을 모두 합산 */ export function createSumOfMaxOfFieldAndSumGroupsValueCallback( groups: Array<{ baseField: string; sumFields: string[] }>, ): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { let total = 0; for (const group of groups) { const baseIdx = fieldNamesArr.indexOf(group.baseField); const baseVal = baseIdx >= 0 ? toNum(values[baseIdx]) : 0; let sumVal = 0; for (const f of group.sumFields) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) sumVal += toNum(values[idx]); } total += Math.max(baseVal, sumVal); } return total; }; } // 공공하수처리구역인구보급률(%), 고도처리인구보급률(%) valueCallback export function createCoverageRateValueCallback( numeratorFields: string[], denominatorFields: string[] ): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { const getSum = (fields: string[]) => { let sum = 0; for (const f of fields) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) sum += toNum(values[idx]); } return sum; }; const total2 = getSum(numeratorFields); const total1 = getSum(denominatorFields); return total1 === 0 ? 0 : (total2 / total1) * 100; }; } // 하수도 설치율(%) valueCallback — 항목·중간·결과 모두 소수 첫째 자리 이후 버림(반올림 없음) export function createSwrgInstlrtRateValueCallback( numeratorFields: string[], denominatorFields: string[], thirdFields: string[], ): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { const getSum = (fields: string[]) => { let sum = 0; for (const f of fields) { const idx = fieldNamesArr.indexOf(f); if (idx >= 0) { sum += truncateToFirstDecimal(toNum(values[idx])); } } return truncateToFirstDecimal(sum); }; const sum1 = getSum(numeratorFields); const sum2 = getSum(denominatorFields); const piplSupprt = truncateToFirstDecimal( toNum(values[fieldNamesArr.indexOf(thirdFields[0] || "piplSupprt")]), ); const ratioPercent = sum1 === 0 ? 0 : truncateToFirstDecimal((sum2 / sum1) * 100); const result = truncateToFirstDecimal((ratioPercent * piplSupprt) / 100); return result; }; } /** 연산 타입에 맞는 valueCallback 반환 */ export function getComputedValueCallback(config: ComputedFieldConfig): DataFieldValueCallback { switch (config.type) { case 'sum': return createSumValueCallback(config.fields); case 'avg': return createAvgValueCallback(config.fields); case 'max': return createMaxValueCallback(config.fields); case 'difference': return createDifferenceValueCallback(config.fields); case 'differenceOfSums': return createDifferenceOfSumsValueCallback( config.minuendFields, config.subtrahendFields, ); case 'maxOfFieldAndSum': return createMaxOfFieldAndSumValueCallback( config.baseField, config.sumFields, ); case 'sumOfMaxOfFieldAndSumGroups': return createSumOfMaxOfFieldAndSumGroupsValueCallback( config.groups, ); case 'pblswrgPopltnSuprtRate': case 'adtrtPopltnSuprtRate': return createCoverageRateValueCallback(config.numeratorFields, config.denominatorFields); case 'swrgInstlrtRate': return createSwrgInstlrtRateValueCallback(config.numeratorFields, config.denominatorFields, config.thirdFields); } }