Files
Aislo/tools/est-test/native-machine.js
T

237 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// REA f18804 → f16638 / f15868 / f15f40 / f16320 → f16ec8.
const EstMachine = (() => {
const { num, components, fixed, floating, even, sum } = EstMath;
const fuels = new Set([
"주연료",
"선박용경유",
"전력",
"경유",
"휘발유",
"중유",
"전",
"가스",
"가솔린",
"디젤",
"diesel",
"gasoline",
"heavyoil",
"lpg",
"l.p.g",
]);
function calculate(rows, criteria, options = {}) {
const settings = new Map(criteria.map((row) => [num(row.Index), num(row.TC)]));
const get = (index) => settings.get(index) ?? 0;
const output = structuredClone(rows),
titles = [],
errors = [];
let head = null,
body = [];
function finish() {
if (!head) return;
components.forEach(
(key) =>
(head[key] = floating(
body.filter((r) => num(r.CalcIndex) < 2).reduce((n, r) => n + num(r[key]), 0),
)),
);
head.TC = sum(head);
titles.push({
...head,
OTC: head.TC,
ONC: head.NC,
OGC: head.GC,
OJC: head.JC,
OSC: head.SC,
});
}
for (const row of output) {
const index = num(row.Index),
link = num(row.LinkIndex);
if (index > 0 && link !== 57) {
finish();
head = row;
body = [];
continue;
}
if (!head || row.Index == null) continue;
if (link === 52) {
finish();
const title = titles[titles.length - 1],
rate = num(row.GN) / 100;
components.forEach((key) => (head[key] = floating(num(title[key]) * rate)));
head.TC = sum(head);
Object.assign(title, head, { NachalRatio: num(row.GN) });
row.TU = title.OTC;
head = null;
body = [];
continue;
}
if (![0, 2].includes(num(row.CalcIndex))) {
body.push(row);
continue;
}
components.forEach((key) => (row[key] = 0));
const q = num(row.GN) / (["%", "%"].includes(row.Unite) ? 100 : 1),
rate = 1 + num(row.Ratio) / 100;
const digits = EstMath.even(get(index));
let value = 0,
field = "JC";
if (index === -3) {
const currency = String(row.Unite || "")[0];
if (currency === "\\") value = num(row.TU) * q * 1e-7;
else if ("$PEFLMY".includes(currency || "\u0000")) {
let exchange = get(currency.charCodeAt(0));
if (currency === "L" && !settings.has(76)) exchange = get(70);
if (currency === "Y") exchange /= 100;
if (!exchange) errors.push(`${head.Name}: 환율 없음 (${currency})`);
const cut = options.DollarCostCut ? 1000 : 1;
// f1580c receives two Currency integers; binary float multiplication can
// turn an exact thousand (226644000) into the previous thousand.
const converted =
Number(
(BigInt(even(num(row.TU) * 10000)) * BigInt(even(exchange * 10000))) /
BigInt(cut * 100000000),
) * cut;
value = converted * q * 1e-7;
} else value = num(row.TU) * q;
value = fixed((even(value * 10000) / 10000) * rate, digits);
} else if (index === -2) {
let unitPrice = num(row.TU),
quantity = q;
if (String(row.Name || "").includes("잡")) {
const fuel = [...body].reverse().find(
(r) =>
num(r.Index) === -2 &&
fuels.has(
String(r.Name || "")
.replace(/\s/g, "")
.toLowerCase(),
),
);
unitPrice = fuel ? num(fuel.GC) || (num(fuel.SCheck) === 3 ? num(fuel.JC) : 0) : 0;
row.TU = unitPrice;
row.Unite = "%";
quantity = num(row.GN) / 100;
}
value = fixed(even(even(unitPrice * quantity * 10000) * rate) / 10000, digits);
field = num(row.SCheck) === 3 ? "JC" : "GC";
} else if (index === -1) {
const coefficient = Math.trunc(get(num(row.NCGasu)) * 1e10) / 1e10;
let price = num(row.TU) * coefficient;
if (options.JDLCalcMethod) price = floating(price, 1);
value = fixed(even(even(price * q * 10000) * rate) / 10000, digits);
field = "NC";
}
row[field] = value;
row.TC = sum(row);
body.push(row);
}
finish();
return { rows: output, titles, errors };
}
let page = 0;
function usage(project) {
const titles = project.tables?.MachTitleDat || [];
const grps = EstNative.groups(project.tables?.MachMainDat || []);
return titles.map((title, index) => {
const code = title.Code || "";
const group = grps.get(code);
const appliedPrice = EstMath.num(title.TC);
const hasOriginal = title.TC_ != null;
const originalPrice = hasOriginal ? EstMath.num(title.TC_) : null;
const difference = hasOriginal ? appliedPrice - originalPrice : null;
return {
index,
Code: code,
Name: title.Name || "",
Size: title.Size || "",
Unite: title.Unite || "",
appliedPrice,
originalPrice,
difference,
NC: EstMath.num(title.NC),
GC: EstMath.num(title.GC),
JC: EstMath.num(title.JC),
detailCount: group ? group.rows.length : 0,
status: !hasOriginal ? "신규(당초없음)" : Math.abs(difference) > 1e-6 ? "당초차이" : "일치",
canApply: false, // 당초 계약값은 비교용이며 현재 단가 덮어쓰기 적용을 지원하지 않음
};
});
}
function changes(project) {
const labels = { new: "신규", deleted: "삭제", changed: "변경", unchanged: "동일" };
const titles = project.tables?.MachTitleDat || [];
return titles.map((title, index) => {
const appliedPrice = EstMath.num(title.TC);
const hasOriginal = title.TC_ != null;
const originalPrice = hasOriginal ? EstMath.num(title.TC_) : null;
const difference = hasOriginal ? appliedPrice - originalPrice : null;
const statusKey = !hasOriginal
? "new"
: Math.abs(difference) > 1e-6
? "changed"
: "unchanged";
return {
index,
Code: title.Code || "",
Name: title.Name || "",
Size: title.Size || "",
Unite: title.Unite || "",
originalTC: originalPrice,
TC: appliedPrice,
difference,
status: labels[statusKey] || statusKey,
};
});
}
function applyRowPrice() {
throw Error(
"기계경비 호표는 MachMainDat 산출 결과이며 당초 계약값으로의 덮어쓰기 적용은 지원하지 않습니다.",
);
}
function applyAllPrices() {
throw Error(
"기계경비 호표는 MachMainDat 산출 결과이며 당초 계약값으로의 덮어쓰기 적용은 지원하지 않습니다.",
);
}
function render() {
const { state, esc, number, byId } = EstApp,
isChange = state.view === "machine-change",
rows = isChange ? changes(state.project) : usage(state.project);
page = Math.max(0, Math.min(page, Math.ceil(rows.length / 100) - 1));
const visible = rows.slice(page * 100, page * 100 + 100),
value = (v) => (v == null ? "" : number(v));
if (isChange) {
byId("work-content").innerHTML =
`<div class="business-tools"><b>기계경비 설계변경</b><span>${rows.length}건 · ${page + 1}/${Math.max(1, Math.ceil(rows.length / 100))}쪽</span><button data-machine-page="-1" ${page === 0 ? "disabled" : ""}>이전</button><button data-machine-page="1" ${(page + 1) * 100 >= rows.length ? "disabled" : ""}>다음</button></div><table class="sheet"><thead><tr><th>구분</th><th>코드</th><th>품명</th><th>규격</th><th>단위</th><th>당초 금액</th><th>변경 금액</th><th>증감</th><th>작업</th></tr></thead><tbody>${visible.map((row) => `<tr><td>${esc(row.status)}</td><td>${esc(row.Code)}</td><td>${esc(row.Name)}</td><td>${esc(row.Size)}</td><td>${esc(row.Unite)}</td><td>${value(row.originalTC)}</td><td>${value(row.TC)}</td><td>${value(row.difference)}</td><td><button data-machine-group="${esc(row.Code)}">호표 열기</button></td></tr>`).join("")}</tbody></table>`;
return;
}
byId("work-content").innerHTML =
`<div class="business-tools"><b>기계경비 사용 내역</b><span>${rows.length}건 · ${page + 1}/${Math.max(1, Math.ceil(rows.length / 100))}쪽</span><button data-machine-page="-1" ${page === 0 ? "disabled" : ""}>이전</button><button data-machine-page="1" ${(page + 1) * 100 >= rows.length ? "disabled" : ""}>다음</button></div><p>기계경비 호표의 산출 단가와 당초 계약 단가를 비교합니다. (기계 호표 단가는 MachMainDat 산출 공식에 의해 결정되며 당초값 덮어쓰기는 지원하지 않습니다.)</p><table class="sheet"><thead><tr><th>코드</th><th>품명</th><th>규격</th><th>단위</th><th>적용 단가</th><th>당초 단가</th><th>차이</th><th>상태</th><th>노무비</th><th>재료비</th><th>경비</th><th>작업</th></tr></thead><tbody>${visible.map((row) => `<tr><td>${esc(row.Code)}</td><td>${esc(row.Name)}</td><td>${esc(row.Size)}</td><td>${esc(row.Unite)}</td><td>${value(row.appliedPrice)}</td><td>${value(row.originalPrice)}</td><td>${value(row.difference)}</td><td>${esc(row.status)}</td><td>${value(row.NC)}</td><td>${value(row.GC)}</td><td>${value(row.JC)}</td><td><button data-machine-group="${esc(row.Code)}">호표 열기</button></td></tr>`).join("")}</tbody></table>`;
}
function install() {
document.addEventListener("click", (event) => {
const step = event.target.closest("[data-machine-page]");
if (step) {
page += Number(step.dataset.machinePage);
render();
return;
}
const node = event.target.closest("[data-machine-group]");
if (!node) return;
const code = node.dataset.machineGroup;
if (!code) return;
EstApp.navigate("MachMainDat", code);
});
}
return { calculate, usage, changes, applyRowPrice, applyAllPrices, render, install };
})();