feat(est): reproduce native tables and calculations

This commit is contained in:
2026-11-05 06:07:05 +09:00
parent 646eab6245
commit 60ccedcd9a
30 changed files with 5249 additions and 22 deletions
+133
View File
@@ -0,0 +1,133 @@
// 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 };
}
return { calculate };
})();