119 lines
4.9 KiB
JavaScript
119 lines
4.9 KiB
JavaScript
// 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 };
|
||
})();
|