187 lines
6.9 KiB
JavaScript
187 lines
6.9 KiB
JavaScript
const EstBusinessActions = (() => {
|
|
let purpose = "",
|
|
documentKey;
|
|
const currentRound = () =>
|
|
EstApp.state.project.progress.find((round) => round.id === EstApp.state.roundId);
|
|
function numeric(value) {
|
|
const n = Number(value);
|
|
if (!Number.isFinite(n) || Math.abs(n) > Number.MAX_SAFE_INTEGER)
|
|
throw Error("유효한 수치를 입력하세요.");
|
|
return n;
|
|
}
|
|
function change(node) {
|
|
const { state } = EstApp;
|
|
if (node.dataset.option === "EscDesignTotal") {
|
|
const value = numeric(node.value);
|
|
if (value < 0) throw Error("조사 도급액은 0 이상입니다.");
|
|
EstApp.change(() => (state.project.options.EscDesignTotal = value), false);
|
|
return true;
|
|
}
|
|
if (node.dataset.escAuto !== undefined) {
|
|
EstApp.change(
|
|
() =>
|
|
(state.project.tables.EstMainDat[Number(node.dataset.escAuto)].EscAuto = node.checked),
|
|
false,
|
|
);
|
|
return true;
|
|
}
|
|
if (node.id === "progress-components" || node.id === "progress-digits") {
|
|
const round = currentRound();
|
|
if (round.locked) throw Error("확정 회차입니다.");
|
|
const value = node.id === "progress-components" ? node.checked : numeric(node.value);
|
|
if (node.id === "progress-digits" && (!Number.isInteger(value) || value < 0 || value > 4))
|
|
throw Error("금액 소수자리는 0~4입니다.");
|
|
EstApp.change(
|
|
() =>
|
|
(round[node.id === "progress-components" ? "separateComponents" : "amountDigits"] =
|
|
value),
|
|
false,
|
|
);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
async function handle(name) {
|
|
const { state } = EstApp,
|
|
project = state.project,
|
|
round = currentRound();
|
|
if (name === "unlock-round") {
|
|
if (confirm("이 회차와 이후 회차의 확정을 해제하시겠습니까? 입력값은 유지됩니다."))
|
|
EstApp.change(() => (state.project = EstProgress.unlock(project, round.id)), false);
|
|
} else if (name === "delete-round") {
|
|
if (round.locked) throw Error("확정을 먼저 해제하세요.");
|
|
if (project.progress.at(-1) !== round) throw Error("마지막 회차부터 삭제하세요.");
|
|
if (confirm("선택한 기성 회차를 삭제하시겠습니까?"))
|
|
EstApp.change(() => {
|
|
project.progress.pop();
|
|
state.roundId = project.progress.at(-1)?.id;
|
|
}, false);
|
|
} else if (name === "rename-round") {
|
|
if (round.locked) throw Error("확정을 먼저 해제하세요.");
|
|
EstActions.form(
|
|
"기성 회차 이름",
|
|
[{ name: "name", label: "회차 이름", value: round.name }],
|
|
(values) => {
|
|
if (
|
|
!values.name.trim() ||
|
|
project.progress.some((item) => item !== round && item.name === values.name)
|
|
)
|
|
throw Error("회차 이름은 비어 있거나 중복될 수 없습니다.");
|
|
EstApp.change(() => (round.name = values.name.trim()), false);
|
|
},
|
|
);
|
|
} else if (name === "fill-progress") {
|
|
if (round.locked) throw Error("확정 회차입니다.");
|
|
EstActions.form(
|
|
"기성 일괄 입력",
|
|
[
|
|
{
|
|
name: "prefix",
|
|
label: "대상",
|
|
value: "Gum",
|
|
choices: [
|
|
["Gum", "금회기성"],
|
|
["Sa", "금회사정"],
|
|
],
|
|
},
|
|
{
|
|
name: "mode",
|
|
label: "방법",
|
|
value: "ratio",
|
|
choices: [
|
|
["ratio", "비율 입력"],
|
|
["full", "전량"],
|
|
],
|
|
},
|
|
{ name: "ratio", label: "비율 %", type: "number", value: 100 },
|
|
],
|
|
(values) =>
|
|
EstApp.change(() => {
|
|
const value = numeric(values.ratio);
|
|
project.tables.EstMainDat.forEach((row, index) => {
|
|
if (
|
|
Number(row.Level) > 0 ||
|
|
Number(row.CalcIndex) >= 2 ||
|
|
[54, 55, 58, 59, 62, 67].includes(Number(row.LinkIndex))
|
|
)
|
|
return;
|
|
if (values.mode === "full" && Number(row.LinkIndex) === 57) return;
|
|
const key = EstBusiness.rowKey(row, index);
|
|
round.inputs[key] ||= {};
|
|
round.inputs[key][values.prefix + "Mode"] =
|
|
values.mode === "full" ? "quantity" : "ratio";
|
|
round.inputs[key][values.prefix] =
|
|
values.mode === "full" ? Number(row.GN) || 0 : value;
|
|
});
|
|
}, false),
|
|
);
|
|
} else if (name === "load-progress-exclusion") {
|
|
const locked = project.progress.filter((round) => round.locked && round.result);
|
|
if (!locked.length) throw Error("확정한 기성 회차가 없습니다.");
|
|
EstActions.form(
|
|
"기성 누계수량 불러오기",
|
|
[
|
|
{
|
|
name: "id",
|
|
label: "기성 회차",
|
|
value: locked.at(-1).id,
|
|
choices: locked.map((round) => [round.id, round.name]),
|
|
},
|
|
],
|
|
(values) =>
|
|
EstApp.change(() => {
|
|
const result = locked.find((round) => round.id === values.id).result;
|
|
const rows = new Map(
|
|
result.rows.map((row, index) => [EstBusiness.rowKey(row, index), row]),
|
|
);
|
|
project.tables.EstMainDat.forEach(
|
|
(row, index) =>
|
|
(row.EscProgress = Number(rows.get(EstBusiness.rowKey(row, index))?.TotalGN) || 0),
|
|
);
|
|
}, false),
|
|
);
|
|
} else if (name === "load-contract" || name === "load-bid") {
|
|
purpose = name;
|
|
documentKey = state.documentKey;
|
|
document.getElementById("business-file").click();
|
|
}
|
|
}
|
|
function install() {
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.id = "business-file";
|
|
input.accept = ".json,.estx";
|
|
input.hidden = true;
|
|
document.body.append(input);
|
|
input.addEventListener("change", async () => {
|
|
try {
|
|
const file = input.files[0];
|
|
if (!file) return;
|
|
const raw = /\.estx$/i.test(file.name)
|
|
? await EstApp.api("/api/import?name=" + encodeURIComponent(file.name), {
|
|
method: "POST",
|
|
body: await file.arrayBuffer(),
|
|
})
|
|
: JSON.parse(await file.text());
|
|
const incoming = EstModel.legacy(raw);
|
|
if (documentKey !== EstApp.state.documentKey)
|
|
throw Error("문서가 변경되어 불러오기를 취소했습니다.");
|
|
EstApp.change(
|
|
() =>
|
|
(EstApp.state.project =
|
|
purpose === "load-contract"
|
|
? EstBusiness.importContract(EstApp.state.project, incoming)
|
|
: EstBusiness.importBid(EstApp.state.project, incoming)),
|
|
false,
|
|
);
|
|
} catch (error) {
|
|
EstApp.state.notice = error.message;
|
|
EstApp.render();
|
|
} finally {
|
|
input.value = "";
|
|
}
|
|
});
|
|
}
|
|
return { handle, change, install };
|
|
})();
|