- 호표 1 단위 펼친 항목 합과 호표 금액 대조, 10원 넘게 어긋나면 표시 - 당초값·계산값(노무·재료·경비·실적)과 항목 목록 - 동원 계산에 호표 단위 펼치기(roots) 추가 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
6.4 KiB
JavaScript
123 lines
6.4 KiB
JavaScript
// 원본 동원인원·자재·장비 「확인」·「내역서」 탭(TfrmDongWon Button1~4 — spec_part §4): 동원 계산이 펼친 중간 결과 보기.
|
||
// 장비·단산·일위 = 호표마다 1 단위를 동원 항목으로 펼쳐 금액 합을 내고, 호표 금액(TC)과 10원 넘게 어긋나면 표시(1095cdc
|
||
// Checked := |TC − Trunc(Σ Cost)| > 10). 행을 고르면 당초값(호표 TC·NC·GC·JC)과 계산값(항목 금액을 노무·재료·경비·실적으로
|
||
// 나눠 더한 값)·항목 목록. 내역서 = 내역 줄마다 펼친 항목(점검 없음). 웹 동원 계산은 기계경비를 더 쪼개지 않아 장비는 늘 맞음.
|
||
const EstDongwonCheck = (() => {
|
||
const num = (v) => Number(v) || 0;
|
||
const TITLE = { 103: "HeadTitleDat", 104: "CostTitleDat", 105: "MachTitleDat" };
|
||
const KIND = { 5: "자재", 6: "노임", 21: "표준", 101: "견적", 105: "기계" };
|
||
// 항목 → 비목: 노임·노무비 금액분 = 노무, 자재·견적·재료비 금액분 = 재료, 기계·경비 = 경비, 표준 = 실적
|
||
const part = (item) =>
|
||
item.Code === "LZZZZZZZZZZ" || item.kind === 6
|
||
? "NC"
|
||
: item.Code === "EZZZZZZZZZZ" || item.kind === 105
|
||
? "JC"
|
||
: item.Code === "SZZZZZZZZZZ" || item.kind === 21
|
||
? "SC"
|
||
: "GC";
|
||
|
||
function check(project, kind) {
|
||
const titles = (project.tables[TITLE[kind]] || []).filter((t) => t.Code);
|
||
const out = EstResources.calculate(
|
||
project,
|
||
true,
|
||
titles.map((t) => ({ kind, code: t.Code, WorkCode: t.Code, WorkName: t.Name })),
|
||
);
|
||
const items = new Map();
|
||
for (const row of out.rows)
|
||
(items.get(row.WorkCode) || items.set(row.WorkCode, []).get(row.WorkCode)).push(row);
|
||
return {
|
||
errors: out.errors,
|
||
rows: titles.map((t) => {
|
||
const list = items.get(t.Code) || [],
|
||
sum = list.reduce((s, r) => s + num(r.amount), 0),
|
||
calc = { NC: 0, GC: 0, JC: 0, SC: 0 };
|
||
for (const r of list) calc[part(r)] += num(r.amount);
|
||
return {
|
||
Code: t.Code,
|
||
Name: t.Name,
|
||
Size: t.Size,
|
||
TC: num(t.TC),
|
||
NC: num(t.NC),
|
||
GC: num(t.GC),
|
||
JC: num(t.JC),
|
||
sum,
|
||
calc,
|
||
checked: Math.abs(num(t.TC) - Math.trunc(sum)) > 10,
|
||
items: list,
|
||
};
|
||
}),
|
||
};
|
||
}
|
||
function estimate(project) {
|
||
const out = EstResources.calculate(project, true),
|
||
groups = new Map();
|
||
for (const row of out.rows) {
|
||
const key = `${row.WorkCode}\u0000${row.WorkName}`;
|
||
if (!groups.has(key)) groups.set(key, { Code: row.WorkCode, Name: row.WorkName, items: [] });
|
||
groups.get(key).items.push(row);
|
||
}
|
||
return { errors: out.errors, rows: [...groups.values()] };
|
||
}
|
||
|
||
let shown = null;
|
||
function draw(state) {
|
||
const { esc, number } = EstApp,
|
||
{ kind, data, pick, only } = shown;
|
||
const list = data.rows.map((r, i) => [r, i]).filter(([r]) => !only || r.checked);
|
||
const chosen = data.rows[pick];
|
||
const head =
|
||
kind === "est"
|
||
? "<tr><th>코드</th><th>공종</th><th>항목 수</th></tr>"
|
||
: "<tr><th>코드</th><th>품명</th><th>규격</th><th>호표 금액</th><th>항목 합</th><th>어긋남</th></tr>";
|
||
const body = list
|
||
.map(([r, i]) =>
|
||
kind === "est"
|
||
? `<tr data-dw-pick="${i}" style="cursor:pointer${i === pick ? ";background:#dde8ff" : ""}"><td>${esc(r.Code)}</td><td style="text-align:left">${esc(r.Name)}</td><td>${r.items.length}</td></tr>`
|
||
: `<tr data-dw-pick="${i}" style="cursor:pointer${i === pick ? ";background:#dde8ff" : ""}${r.checked ? ";color:#b00" : ""}"><td>${esc(r.Code)}</td><td style="text-align:left">${esc(r.Name)}</td><td>${esc(r.Size ?? "")}</td><td>${number(r.TC)}</td><td>${number(Math.trunc(r.sum))}</td><td>${r.checked ? "✔" : ""}</td></tr>`,
|
||
)
|
||
.join("");
|
||
const detail = chosen
|
||
? (kind === "est"
|
||
? ""
|
||
: `<p>당초값 계 ${number(chosen.TC)} · 노무 ${number(chosen.NC)} · 재료 ${number(chosen.GC)} · 경비 ${number(chosen.JC)}<br>계산값 계 ${number(Math.trunc(chosen.sum))} · 노무 ${number(Math.trunc(chosen.calc.NC))} · 재료 ${number(Math.trunc(chosen.calc.GC))} · 경비 ${number(Math.trunc(chosen.calc.JC))}${chosen.calc.SC ? ` · 실적 ${number(Math.trunc(chosen.calc.SC))}` : ""}</p>`) +
|
||
`<table class="sheet"><thead><tr><th>종류</th><th>코드</th><th>품명</th><th>규격</th><th>소요량</th><th>금액</th></tr></thead><tbody>${chosen.items.map((r) => `<tr><td>${esc(KIND[r.kind] || r.kind)}</td><td>${esc(r.Code)}</td><td style="text-align:left">${esc(r.Name)}</td><td>${esc(r.Size ?? "")}</td><td>${number(r.quantity)}</td><td>${number(r.amount)}</td></tr>`).join("")}</tbody></table>`
|
||
: "<p>위에서 행을 고르세요.</p>";
|
||
const title = { 105: "확인 — 장비", 104: "확인 — 단산", 103: "확인 — 일위", est: "내역서" }[
|
||
kind
|
||
];
|
||
EstApp.showDialog(
|
||
`동원인원,자재,장비 · ${title}`,
|
||
`${kind === "est" ? "" : `<label><input type="checkbox" data-dw-only ${only ? "checked" : ""}> 어긋난 것만 (${data.rows.filter((r) => r.checked).length}건)</label>`} <span>${data.rows.length}건</span><div style="max-height:260px;overflow:auto"><table class="sheet"><thead>${head}</thead><tbody>${body}</tbody></table></div><div style="max-height:260px;overflow:auto;margin-top:6px">${detail}</div>${data.errors.length ? `<p class="business-note">${esc(data.errors.slice(0, 5).join(" / "))}</p>` : ""}`,
|
||
);
|
||
}
|
||
function install() {
|
||
document.addEventListener("click", (event) => {
|
||
const tr = event.target.closest?.("[data-dw-pick]");
|
||
if (!tr || !shown) return;
|
||
shown.pick = Number(tr.dataset.dwPick);
|
||
draw(EstApp.state);
|
||
});
|
||
document.addEventListener("change", (event) => {
|
||
if (event.target.dataset?.dwOnly === undefined || !shown) return;
|
||
shown.only = event.target.checked;
|
||
draw(EstApp.state);
|
||
});
|
||
}
|
||
if (typeof document !== "undefined" && document.addEventListener) install();
|
||
|
||
function handle(name, state, node) {
|
||
if (name !== "dongwon-check") return false;
|
||
const kind = node.dataset.kind === "est" ? "est" : Number(node.dataset.kind);
|
||
shown = {
|
||
kind,
|
||
data: kind === "est" ? estimate(state.project) : check(state.project, kind),
|
||
pick: -1,
|
||
only: false,
|
||
};
|
||
draw(state);
|
||
return true;
|
||
}
|
||
return { check, estimate, handle };
|
||
})();
|