// REA 10a7ad4/10a7d7c/10a82cc/10a8fa8, e42260, 10ad5ac. const EstEscalation = (() => { const { num, units, components, floating, sum } = EstMath; const special = (row) => [54, 55, 57, 58, 59, 62, 67].includes(num(row.LinkIndex)); const total = (p) => p.tables.Wonga.length ? num(p.calculation.total) : num(p.calculation.direct.TC); function calculate(project) { const key = EstBusiness.rowKey, errors = [], rows = structuredClone(project.tables.EstMainDat); const original = project.baseline?.tables.EstMainDat || rows.map((row) => { const old = { ...row }; for (const field of ["GN", "TU", "TC", ...units, ...components]) if (row[field + "_"] != null) old[field] = row[field + "_"]; return old; }); const identities = EstRowMatch.identities(original, rows, key), originalKeys = identities.original, rowKeys = identities.current; const contracts = new Map(original.map((row, index) => [originalKeys[index], row])); const design = structuredClone(original), excluded = structuredClone(original); rows.forEach((row, index) => { const old = contracts.get(rowKeys[index]) || row; units.forEach((unit) => (row[`Contract${unit}`] = num(old[unit]))); const auto = row.EscAuto !== false; const quantity = auto ? Math.max(num(row.EscPlanned), num(row.EscActual), num(row.EscProgress)) : num(row.EscManualQuantity ?? row.EscExcluded); row.EscExcluded = quantity; row.EscApplied = num(old.GN) - quantity; if (num(row.Level) > 0 || special(row)) return; const factor = num(old.GN) / (["%", "%"].includes(row.Unite) ? 100 : 1); units.forEach((unit, i) => { const A = num(row["A" + unit]), B = num(row[unit]), C = num(old[unit]); const D = A && B ? floating(((B - A) / A) * 100, 2, 2) : 0; let E = 0; if (A && B && A >= C && B >= A) E = Math.trunc((C * D) / 100); else if (A && B && A < C && B >= C) E = Math.trunc(B - C); row["D" + unit] = D; row["E" + unit] = E; row["F" + unit] = C + E; row["E" + components[i]] = Math.trunc(E * factor); if (![1, 3].includes(num(row.CalcIndex))) { row[unit] = C + E; row[components[i]] = Math.trunc((C + E) * factor); } }); if (![1, 3].includes(num(row.CalcIndex))) row.TC = sum(row); }); const current = new Map(rows.map((row, index) => [rowKeys[index], row])); design.forEach((row, index) => { if (num(row.Level) > 0 || special(row)) return; const now = current.get(originalKeys[index]); if (!now) return; units.forEach((unit) => { if (now["A" + unit] != null) row[unit] = num(now["A" + unit]); }); }); excluded.forEach((row, index) => { if (num(row.Level) > 0 || special(row)) return; const now = current.get(originalKeys[index]), quantity = num(now?.EscExcluded), oldQuantity = num(row.GN); if ([1, 3].includes(num(row.CalcIndex))) { const fraction = oldQuantity ? quantity / oldQuantity : 0; components.forEach((field) => (row[field] = Math.trunc(num(row[field]) * fraction))); row.TC = sum(row); } row.GN = quantity; }); function costCopy(source) { const copy = EstNative.empty(); copy.tables.EstMainDat = source; copy.tables.Wonga = project.baseline?.tables.Wonga || project.tables.Wonga; copy.options = { ...project.options, SourceRefresh: false, TotalFix: false }; const calculated = EstNative.calculate(copy); errors.push(...calculated.calculation.errors); return calculated; } const adjusted = costCopy(rows), contract = costCopy(structuredClone(original)), designProject = costCopy(design), excludedProject = costCopy(excluded); const originalTotal = total(contract), adjustedTotal = total(adjusted), excludedTotal = total(excludedProject); const target = project.tables.Wonga.find((row) => num(row.LinkIndex) === 55); const designTotal = num(project.options.EscDesignTotal) || num(target?.EsDesignCost) || total(designProject); if (!designTotal) errors.push("조사금액이 없습니다. 입찰 당시 단가 또는 조사 도급액을 입력하세요."); const applied = originalTotal - excludedTotal; const rate = designTotal ? floating(((adjustedTotal - originalTotal) / designTotal) * 100, 2, 2) : 0; return { rows: adjusted.tables.EstMainDat, direct: adjusted.calculation.direct, wonga: adjusted.tables.Wonga, contractWonga: contract.tables.Wonga, excludedWonga: excludedProject.tables.Wonga, designTotal, contractTotal: originalTotal, adjustedTotal, excludedTotal, applied, rate, adjustment: Math.trunc((applied * rate) / 100), errors: [...new Set(errors)], }; } return { calculate }; })();