277 lines
9.0 KiB
JavaScript
277 lines
9.0 KiB
JavaScript
// REA popMainBlockCut, popMainBlockCopy, popMainBlockPaste, popMainBlockDel 및 블럭 범위 지정 (ActionBlockOneSet, 블럭시작/끝)
|
|
// 및 popMainSetStr (산출근거 주석 지정/해제), TFRMWONGABASE popupTotal (공사비역산 자리수 맞추기)
|
|
const EstBlockActions = (() => {
|
|
let blockStart = null;
|
|
let blockTable = null;
|
|
|
|
function setBlockStart(state) {
|
|
const rows = state.project.tables[state.view];
|
|
if (!rows) throw Error("현재 화면에서는 블럭을 지정할 수 없습니다.");
|
|
const selected = [...state.selection];
|
|
if (!selected.length) throw Error("블럭 시작 위치로 지정할 행을 선택하세요.");
|
|
blockStart = selected[0];
|
|
blockTable = state.view;
|
|
state.notice = `${blockStart + 1}행이 블럭 시작점으로 설정되었습니다. (블럭 끝 행을 선택 후 '블럭 끝'을 누르세요)`;
|
|
}
|
|
|
|
function setBlockEnd(state) {
|
|
const rows = state.project.tables[state.view];
|
|
if (!rows) throw Error("현재 화면에서는 블럭을 지정할 수 없습니다.");
|
|
if (blockStart == null || blockTable !== state.view) {
|
|
throw Error("먼저 '블럭 시작'을 지정하세요.");
|
|
}
|
|
const selected = [...state.selection];
|
|
if (!selected.length) throw Error("블럭 끝 위치로 지정할 행을 선택하세요.");
|
|
const blockEnd = selected[0];
|
|
const start = Math.min(blockStart, blockEnd);
|
|
const end = Math.max(blockStart, blockEnd);
|
|
|
|
state.selection.clear();
|
|
for (let i = start; i <= end; i++) {
|
|
state.selection.add(i);
|
|
}
|
|
state.notice = `${start + 1}행부터 ${end + 1}행까지 총 ${end - start + 1}개 행이 블럭으로 선택되었습니다.`;
|
|
blockStart = null;
|
|
blockTable = null;
|
|
}
|
|
|
|
function clearBlockSelection(state) {
|
|
state.selection.clear();
|
|
blockStart = null;
|
|
blockTable = null;
|
|
state.notice = "선택이 해제되었습니다.";
|
|
}
|
|
|
|
function selectAllRows(state) {
|
|
const rows = state.project.tables[state.view];
|
|
if (!rows) throw Error("현재 화면에서는 전체 선택을 할 수 없습니다.");
|
|
state.selection.clear();
|
|
for (let i = 0; i < rows.length; i++) {
|
|
state.selection.add(i);
|
|
}
|
|
state.notice = `전체 ${rows.length}개 행이 선택되었습니다.`;
|
|
}
|
|
|
|
function deleteBlockRows(state) {
|
|
const rows = state.project.tables[state.view];
|
|
if (!rows) throw Error("현재 화면에서는 블럭 삭제를 할 수 없습니다.");
|
|
const indexes = new Set([...state.selection]);
|
|
if (!indexes.size) throw Error("삭제할 블럭 행을 먼저 선택하세요.");
|
|
|
|
// 호표/기계경비 그룹이 있는 경우 호표 제목 행 포함 시 전체 호표 행 포함
|
|
if (["HeadMainDat", "CostMainDat", "MachMainDat"].includes(state.view)) {
|
|
for (const group of EstNative.groups(rows).values()) {
|
|
if (indexes.has(group.start)) {
|
|
for (let i = group.start; i < group.start + group.rows.length; i++) {
|
|
indexes.add(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const codes = new Set(
|
|
[...indexes]
|
|
.map((i) => rows[i]?.Code)
|
|
.filter(Boolean)
|
|
.map((c) => String(c).trim()),
|
|
);
|
|
const kind = Number(
|
|
Object.entries(EstNative.sourceTables).find(([, table]) => table === state.view)?.[0] ||
|
|
{ HeadMainDat: 103, CostMainDat: 104, MachMainDat: 105 }[state.view],
|
|
);
|
|
|
|
if (
|
|
kind &&
|
|
Object.entries(state.project.tables).some(([table, list]) =>
|
|
list.some((row, i) => {
|
|
if (table === state.view && indexes.has(i)) return false;
|
|
const rLink = Number(row.LinkIndex);
|
|
const rCode = row.Code ? String(row.Code).trim() : "";
|
|
return rLink === kind && rCode && codes.has(rCode);
|
|
}),
|
|
)
|
|
) {
|
|
throw Error("다른 항목에서 사용 중입니다. 연결을 먼저 해제하세요.");
|
|
}
|
|
|
|
if (!confirm(`${indexes.size}개 행을 블럭 삭제하시겠습니까?`)) return;
|
|
|
|
EstApp.change(() => {
|
|
[...indexes].sort((a, b) => b - a).forEach((i) => rows.splice(i, 1));
|
|
state.selection.clear();
|
|
blockStart = null;
|
|
blockTable = null;
|
|
});
|
|
}
|
|
|
|
function toggleCostComment(state) {
|
|
// REA popMainSetStr: 단가산출 산출근거 행을 주석(Index = -1)으로 지정 또는 일반 산출식으로 복원
|
|
const rows = state.project.tables.CostMainDat;
|
|
const indices = [...state.selection].sort((a, b) => a - b);
|
|
if (state.view !== "CostMainDat" || !rows) {
|
|
throw Error("단가산출 화면에서만 산출근거 행을 주석으로 지정할 수 있습니다.");
|
|
}
|
|
if (!indices.length || indices.some((i) => !rows[i])) {
|
|
throw Error("주석으로 지정하거나 해제할 산출근거 행을 선택하세요.");
|
|
}
|
|
|
|
const plans = [];
|
|
for (const index of indices) {
|
|
const row = rows[index];
|
|
const curIndex = Number(row.Index) || 0;
|
|
if (curIndex > 0) {
|
|
throw Error(
|
|
`행 ${index + 1}: 호표 제목 행은 주석으로 지정할 수 없습니다. 상세 산출근거 행을 선택하세요.`,
|
|
);
|
|
}
|
|
if (curIndex === -20) {
|
|
throw Error(`행 ${index + 1}: 소계 구분선 행은 주석으로 지정할 수 없습니다.`);
|
|
}
|
|
|
|
const curSCheck = Number(row.SCheck) || 0;
|
|
const isComment = curIndex === -1 || curSCheck === -1;
|
|
|
|
if (isComment) {
|
|
// 이미 주석인 행 -> 복원
|
|
let restoreIndex = 0;
|
|
if (row._origIndex !== undefined) {
|
|
restoreIndex = Number(row._origIndex);
|
|
} else if (row.Index_ !== undefined) {
|
|
restoreIndex = Number(row.Index_);
|
|
}
|
|
|
|
let restoreSCheck = null;
|
|
if (row._origSCheck !== undefined) {
|
|
restoreSCheck = Number(row._origSCheck);
|
|
} else if (row.SCheck_ !== undefined) {
|
|
restoreSCheck = Number(row.SCheck_);
|
|
} else if ([-1, -2, -3, -5].includes(Number(row.OCheck))) {
|
|
restoreSCheck = Number(row.OCheck);
|
|
}
|
|
|
|
plans.push({
|
|
index,
|
|
row,
|
|
isComment: false,
|
|
restoreIndex,
|
|
restoreSCheck,
|
|
});
|
|
} else {
|
|
// 일반 상세 행 또는 비목 행(-2, -3, -5 등) -> 주석(-1)으로 전환
|
|
plans.push({
|
|
index,
|
|
row,
|
|
isComment: true,
|
|
origIndex: curIndex,
|
|
origSCheck: row.SCheck !== undefined ? Number(row.SCheck) : null,
|
|
});
|
|
}
|
|
}
|
|
|
|
EstApp.change(() => {
|
|
for (const p of plans) {
|
|
if (p.isComment) {
|
|
p.row._origIndex = p.origIndex;
|
|
if (p.origSCheck !== null) {
|
|
p.row._origSCheck = p.origSCheck;
|
|
}
|
|
p.row.Index = -1;
|
|
p.row.SCheck = -1;
|
|
} else {
|
|
delete p.row._origIndex;
|
|
delete p.row._origSCheck;
|
|
p.row.Index = p.restoreIndex;
|
|
if (p.restoreSCheck !== null) {
|
|
p.row.SCheck = p.restoreSCheck;
|
|
} else {
|
|
delete p.row.SCheck;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function alignWongaTotalAction(state) {
|
|
// REA TFRMWONGABASE popupTotal: 공사비역산 자리수 맞추기
|
|
const rows = state.project.tables.Wonga;
|
|
if (!rows || !rows.length) {
|
|
throw Error("원가계산서 자료가 없습니다.");
|
|
}
|
|
const targetRow = rows.find((r) => Number(r.LinkIndex) === 55);
|
|
const curTotal = targetRow ? Number(targetRow.Cost) || 0 : 0;
|
|
|
|
EstActions.form(
|
|
"공사비역산 자리수 맞추기",
|
|
[
|
|
{
|
|
name: "targetCost",
|
|
label: "목표 도급공사비 (원)",
|
|
type: "number",
|
|
value: curTotal,
|
|
},
|
|
{
|
|
name: "unitDigits",
|
|
label: "단단위 절하 기준",
|
|
value: "3",
|
|
choices: [
|
|
["0", "원 단위 (절하 없음)"],
|
|
["1", "십원 단위 (10원 절하)"],
|
|
["2", "백원 단위 (100원 절하)"],
|
|
["3", "천원 단위 (1,000원 절하)"],
|
|
["4", "만원 단위 (10,000원 절하)"],
|
|
],
|
|
},
|
|
],
|
|
({ targetCost, unitDigits }) => {
|
|
EstApp.change(() => {
|
|
EstWonga.alignTotal(rows, targetCost, unitDigits);
|
|
state.project.options.TotalFix = true;
|
|
});
|
|
},
|
|
);
|
|
}
|
|
|
|
function handle(name, state) {
|
|
if (name === "block-start") {
|
|
setBlockStart(state);
|
|
return true;
|
|
}
|
|
if (name === "block-end") {
|
|
setBlockEnd(state);
|
|
return true;
|
|
}
|
|
if (name === "block-clear") {
|
|
clearBlockSelection(state);
|
|
return true;
|
|
}
|
|
if (name === "select-all-rows") {
|
|
selectAllRows(state);
|
|
return true;
|
|
}
|
|
if (name === "block-del") {
|
|
deleteBlockRows(state);
|
|
return true;
|
|
}
|
|
if (name === "toggle-cost-comment") {
|
|
toggleCostComment(state);
|
|
return true;
|
|
}
|
|
if (name === "align-wonga-total") {
|
|
alignWongaTotalAction(state);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return {
|
|
setBlockStart,
|
|
setBlockEnd,
|
|
clearBlockSelection,
|
|
selectAllRows,
|
|
deleteBlockRows,
|
|
toggleCostComment,
|
|
alignWongaTotalAction,
|
|
handle,
|
|
};
|
|
})();
|