auto: 2026-11-12 12:24 (EOMSANGDON-HOME)
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
// 원본 기성 원가계산서(TfrmWongaAccomH 0xd48408 / V 0xd46914) 편집 — 회차별 원가 서식 사본(round.wonga)에만 적용.
|
||||
// 한줄 삽입·추가(0xd49758: 새 행 Index 0·CalcIndex 0·Checked 끔, 앞 행 끝줄 표지 이어받기), 한줄 삭제(확인 뒤, 재계산 없음),
|
||||
// 내용 삭제(LinkIndex·Cost·Biul·Youl·VSusic·Diget 비움), 선택항목 절하(0xd094dc, 기성은 도급액 행도 허용),
|
||||
// 선택데이타 절하(popupTrunc: 직전 자리수를 그대로, CalcBase 행 제외), 공사비역산 자리수(목표 = 10^n 미만 절하 → 이윤 역산),
|
||||
// 초기화(사본 지움 = 도급 원가 서식), 되살리기(직전 조작 전), 산출근거 약어로 표시(A8: 수식칸 편집 잠금만).
|
||||
// 원본의 번호·참조 재작성(0xc97398·0xc971e8)은 하지 않는다 — 웹 수식은 Gubun·이름으로 찾으므로 새 행은 기호를 직접 적는다.
|
||||
const EstProgressWonga = (() => {
|
||||
const num = (value) => Number(value) || 0;
|
||||
|
||||
function template(project, round) {
|
||||
return round.contract?.wonga || project.tables.Wonga || [];
|
||||
}
|
||||
function edit(project, round, mutate) {
|
||||
if (round.locked) throw Error("확정한 회차는 고칠 수 없습니다.");
|
||||
round.wongaUndo = round.wonga ? structuredClone(round.wonga) : null;
|
||||
round.wonga ||= structuredClone(template(project, round));
|
||||
return mutate(round.wonga);
|
||||
}
|
||||
function blank(list, at) {
|
||||
const row = { Index: 0, Gubun: "", Name: "", Youl: "", VSusic: "", Cost: 0, CalcIndex: 0 };
|
||||
const prev = list[at - 1],
|
||||
end = num(prev?.EndLineIndex);
|
||||
row.EndLineIndex = end === 1 ? 1 : end === 3 ? 3 : 0;
|
||||
if (end === 3) prev.EndLineIndex = 0;
|
||||
row.Checked = false;
|
||||
return row;
|
||||
}
|
||||
|
||||
const ops = {
|
||||
insert(project, round, at) {
|
||||
edit(project, round, (list) => list.splice(at, 0, blank(list, at)));
|
||||
},
|
||||
append(project, round) {
|
||||
edit(project, round, (list) => list.push(blank(list, list.length)));
|
||||
},
|
||||
remove(project, round, at) {
|
||||
edit(project, round, (list) => {
|
||||
if (!list[at]) throw Error("삭제할 줄을 고르세요.");
|
||||
list.splice(at, 1);
|
||||
});
|
||||
},
|
||||
clear(project, round, at) {
|
||||
edit(project, round, (list) => {
|
||||
if (!list[at]) throw Error("내용을 지울 줄을 고르세요.");
|
||||
Object.assign(list[at], {
|
||||
LinkIndex: 0,
|
||||
Cost: 0,
|
||||
Biul: "",
|
||||
Youl: "",
|
||||
VSusic: "",
|
||||
Diget: 0,
|
||||
Checked: false,
|
||||
});
|
||||
});
|
||||
},
|
||||
position(project, round, at, method, digits) {
|
||||
edit(project, round, (list) =>
|
||||
EstWongaTools.position(
|
||||
{ tables: { Wonga: list }, options: project.options },
|
||||
[at],
|
||||
method,
|
||||
digits,
|
||||
true,
|
||||
),
|
||||
);
|
||||
},
|
||||
trunc(project, round, at) {
|
||||
edit(project, round, (list) => {
|
||||
if (!list[at]) throw Error("절하할 줄을 고르세요.");
|
||||
if (!list[at].CalcBase) list[at].Diget = num(project.options.WongaLastDiget);
|
||||
});
|
||||
},
|
||||
total(project, round, digits) {
|
||||
if (round.locked) throw Error("확정한 회차는 고칠 수 없습니다.");
|
||||
if (!Number.isInteger(digits) || digits < 0 || digits > 9)
|
||||
throw Error("자리수는 0~9 사이로 입력하세요.");
|
||||
if (digits) round.wongaTotalDigits = digits;
|
||||
else delete round.wongaTotalDigits;
|
||||
},
|
||||
init(project, round) {
|
||||
if (round.locked) throw Error("확정한 회차는 고칠 수 없습니다.");
|
||||
round.wongaUndo = round.wonga ? structuredClone(round.wonga) : null;
|
||||
delete round.wonga;
|
||||
delete round.wongaTotalDigits;
|
||||
},
|
||||
undo(project, round) {
|
||||
if (round.locked) throw Error("확정한 회차는 고칠 수 없습니다.");
|
||||
if (!("wongaUndo" in round)) throw Error("되살릴 조작이 없습니다.");
|
||||
if (round.wongaUndo) round.wonga = round.wongaUndo;
|
||||
else delete round.wonga;
|
||||
delete round.wongaUndo;
|
||||
},
|
||||
field(project, round, at, name, value) {
|
||||
edit(project, round, (list) => {
|
||||
if (!list[at] || !["Gubun", "Name", "Youl", "VSusic", "Cost"].includes(name))
|
||||
throw Error("고칠 칸이 없습니다.");
|
||||
list[at][name] = name === "Cost" ? num(String(value).replaceAll(",", "")) : value;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// 화면
|
||||
function render(state, round, output) {
|
||||
const { esc, number } = EstApp,
|
||||
project = state.project,
|
||||
list = round.wonga || template(project, round),
|
||||
locked = round.locked,
|
||||
lockFormula = project.options.WongaSNameShow,
|
||||
pick = state.progressWongaRow;
|
||||
const b = (action, label) =>
|
||||
`<button data-action="${action}" ${locked ? "disabled" : ""}>${label}</button>`;
|
||||
const tools = [
|
||||
b("pw-insert", "한줄 삽입"),
|
||||
b("pw-append", "한줄 추가"),
|
||||
b("pw-delete", "한줄 삭제"),
|
||||
b("pw-clear", "내용 삭제"),
|
||||
b("pw-position", "선택항목 절하하기"),
|
||||
b("pw-trunc", "선택데이타 절하하기"),
|
||||
b("pw-total", "공사비역산 자리수 맞추기"),
|
||||
b("pw-init", "원가계산서 초기화"),
|
||||
b("pw-undo", "되살리기"),
|
||||
`<label><input type="checkbox" data-pw-sname ${lockFormula ? "checked" : ""}> 산출근거 약어로 표시</label>`,
|
||||
round.wonga ? "<span>회차 서식 사용 중</span>" : "",
|
||||
round.wongaTotalDigits ? `<span>역산 ${round.wongaTotalDigits}자리</span>` : "",
|
||||
].join("");
|
||||
const input = (i, name, value, disabled) =>
|
||||
`<input data-pw-field="${name}" data-i="${i}" value="${esc(value ?? "")}" ${locked || disabled ? "disabled" : ""}${name === "Name" || name === "VSusic" ? "" : ' size="8"'}>`;
|
||||
const body = (output.costs || [])
|
||||
.map((row, i) => {
|
||||
const tpl = list[i] || {};
|
||||
return `<tr><td><input type="radio" name="pw-row" data-pw-pick="${i}" ${pick === i ? "checked" : ""}></td><td>${input(i, "Gubun", tpl.Gubun, false)}</td><td>${input(i, "Name", tpl.Name, false)}</td><td>${input(i, "Youl", tpl.Youl, false)}</td><td>${input(i, "VSusic", tpl.VSusic, lockFormula)}</td><td>${num(tpl.Diget) ? `${tpl.Diget}/${num(tpl.DigetIndex)}` : ""}</td><td>${number(row.ContractCost)}</td><td>${number(row.JunCost)}</td><td>${number(row.GumCost)}</td><td>${number(row.SaCost)}</td><td>${number(row.TotalCost)}</td></tr>`;
|
||||
})
|
||||
.join("");
|
||||
return `<h4>기성 원가계산서</h4><div class="business-tools">${tools}</div><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></tr></thead><tbody>${body}</tbody></table>`;
|
||||
}
|
||||
|
||||
function round(state) {
|
||||
return state.project.progress.find((r) => r.id === state.roundId);
|
||||
}
|
||||
function at(state) {
|
||||
const i = state.progressWongaRow;
|
||||
if (!Number.isInteger(i)) throw Error("기성 원가계산서에서 줄을 고르세요.");
|
||||
return i;
|
||||
}
|
||||
|
||||
const names = new Set([
|
||||
"pw-insert",
|
||||
"pw-append",
|
||||
"pw-delete",
|
||||
"pw-clear",
|
||||
"pw-position",
|
||||
"pw-trunc",
|
||||
"pw-total",
|
||||
"pw-init",
|
||||
"pw-undo",
|
||||
]);
|
||||
function handle(name, state) {
|
||||
if (!names.has(name)) return false;
|
||||
const r = round(state);
|
||||
if (!r) throw Error("기성 회차를 고르세요.");
|
||||
const run = (fn) => EstApp.change((project) => fn(project, project.progress.find((x) => x.id === r.id)));
|
||||
if (name === "pw-insert") run((p, x) => ops.insert(p, x, at(state)));
|
||||
else if (name === "pw-append") run((p, x) => ops.append(p, x));
|
||||
else if (name === "pw-delete") {
|
||||
const i = at(state);
|
||||
if (confirm("현재 줄을 삭제하시겠습니까?")) {
|
||||
run((p, x) => ops.remove(p, x, i));
|
||||
state.progressWongaRow = undefined;
|
||||
}
|
||||
} else if (name === "pw-clear") run((p, x) => ops.clear(p, x, at(state)));
|
||||
else if (name === "pw-trunc") run((p, x) => ops.trunc(p, x, at(state)));
|
||||
else if (name === "pw-init") {
|
||||
if (confirm("이 회차의 원가계산서를 도급 원가 서식으로 되돌리시겠습니까?")) run(ops.init);
|
||||
} else if (name === "pw-undo") run(ops.undo);
|
||||
else if (name === "pw-position") {
|
||||
const i = at(state);
|
||||
EstActions.form(
|
||||
"선택항목 절하하기",
|
||||
[
|
||||
{
|
||||
name: "method",
|
||||
label: "처리방식",
|
||||
value: 2,
|
||||
choices: [
|
||||
[1, "사사오입"],
|
||||
[2, "절하"],
|
||||
[3, "절상"],
|
||||
[0, "초기화"],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "digits",
|
||||
label: "자리 (0 원 · 1 십원 … 6 백만원)",
|
||||
type: "number",
|
||||
value: num(state.project.options.WongaLastDiget),
|
||||
},
|
||||
],
|
||||
(values) =>
|
||||
run((p, x) => ops.position(p, x, i, Number(values.method), Number(values.digits))),
|
||||
);
|
||||
} else if (name === "pw-total")
|
||||
EstActions.form(
|
||||
"공사비역산 자리수 맞추기",
|
||||
[
|
||||
{
|
||||
name: "digits",
|
||||
label: "도급액 절하 자리 (0 해제 · 3 천원 · 4 만원 …)",
|
||||
type: "number",
|
||||
value: r.wongaTotalDigits ?? 3,
|
||||
},
|
||||
],
|
||||
(values) => run((p, x) => ops.total(p, x, Number(values.digits))),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function install() {
|
||||
document.addEventListener("change", (event) => {
|
||||
const t = event.target,
|
||||
state = EstApp.state;
|
||||
if (t.dataset.pwPick !== undefined) {
|
||||
state.progressWongaRow = Number(t.dataset.pwPick);
|
||||
return;
|
||||
}
|
||||
if (t.dataset.pwSname !== undefined) {
|
||||
EstApp.change((project) => (project.options.WongaSNameShow = t.checked), false);
|
||||
return;
|
||||
}
|
||||
if (t.dataset.pwField === undefined) return;
|
||||
const r = round(state);
|
||||
try {
|
||||
EstApp.change((project) =>
|
||||
ops.field(
|
||||
project,
|
||||
project.progress.find((x) => x.id === r.id),
|
||||
Number(t.dataset.i),
|
||||
t.dataset.pwField,
|
||||
t.value,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
state.notice = error.message;
|
||||
EstApp.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof document !== "undefined" && document.addEventListener) install();
|
||||
return { template, ops, render, handle };
|
||||
})();
|
||||
Reference in New Issue
Block a user