feat(est-test): 단가산출 전역변수·새내역서 파일로저장·산출서 삽입과 선택 삭제
- 전역변수: 표지 대입 변수를 호표 넘어 유지, 지정·해제, 목록·이동 - 새내역서 파일로저장: 당초값·신규 표시·당초 사본 지운 새 작업 따로 저장 - 수량산출서 삽입(공종명칭·규격)·선택공종 삭제 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""EST 테스트 앱 단가산출 전역변수(원본 \x05 표지 — 호표를 넘어 값 유지, 지정·해제, 목록)."""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def test_global_variable_crosses_groups_and_toggle():
|
||||
code = r"""
|
||||
const fs = require('fs'), vm = require('vm');
|
||||
const ctx = vm.createContext({ structuredClone });
|
||||
for (const f of ['native-math.js', 'native-formula.js', 'native-text.js', 'native-global-vars.js'])
|
||||
vm.runInContext(fs.readFileSync('tools/est-test/' + f, 'utf8'), ctx, { filename: f });
|
||||
const T = vm.runInContext('EstText', ctx), G = vm.runInContext('EstGlobalVars', ctx);
|
||||
const rows = [
|
||||
{ Index: 1, Code: 'D1', LineData: '호표1' },
|
||||
{ Index: 0, SCheck: 0, LineData: '\x05Q\x05 = 2 × 3 = \x126\x12' },
|
||||
{ Index: 1, Code: 'D2', LineData: '호표2' },
|
||||
{ Index: 0, SCheck: -1, LineData: '노무 = Q × 10 = \x120\x12' },
|
||||
{ Index: 1, Code: 'D3', LineData: '호표3' },
|
||||
{ Index: 0, SCheck: 0, LineData: 'R = 1 = \x121\x12' },
|
||||
{ Index: 1, Code: 'D4', LineData: '호표4' },
|
||||
{ Index: 0, SCheck: -1, LineData: '노무 = R × 10 = \x120\x12' },
|
||||
];
|
||||
const r = T.calculate(rows, {});
|
||||
let bad = ''; try { G.toggle('가 = 3', 0); } catch (e) { bad = e.message.slice(0, 12); }
|
||||
process.stdout.write(JSON.stringify({
|
||||
used: r.rows[3].LineData.match(/\x12([^\x12]+)\x12/)[1], kept: r.rows[1].LineData.startsWith('\x05Q\x05'),
|
||||
local: r.errors.length, globals: r.globals,
|
||||
on: G.toggle('Q = 2', 0), off: G.toggle('⟦전역⟧Q⟦전역⟧ = 2', 5), bad,
|
||||
clean: T.clean('\x05Q\x05 = 1'), shown: T.display('\x05V1\x05'), back: T.encode('⟦전역⟧V1⟦전역⟧') === '\x05V1\x05',
|
||||
}));
|
||||
"""
|
||||
out = json.loads(subprocess.check_output(["node", "-e", code], cwd=ROOT).decode("utf-8"))
|
||||
assert out["used"] == "60"
|
||||
assert out["kept"] is True
|
||||
assert out["local"] == 1 # R 은 전역이 아니라 D4 에서 못 찾음
|
||||
assert out["globals"] == [{"name": "Q", "value": 6, "index": 1}]
|
||||
assert out["on"] == "⟦전역⟧Q⟦전역⟧ = 2"
|
||||
assert out["off"] == "Q = 2"
|
||||
assert out["bad"] == "선택한 변수는 전역변수"
|
||||
assert out["clean"] == "Q = 1"
|
||||
assert out["shown"] == "⟦전역⟧V1⟦전역⟧" and out["back"] is True
|
||||
@@ -489,6 +489,12 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD
|
||||
- 남음: 구성비·요율 전환(CalcBase), 약어 표시(SName), 제비율 기준 문서 보기(원본 한글 파일).
|
||||
- [x] 수량산출서 검색·전체보기(원본 TFRMMULLANG btnSerch·btnAllDisplay — 빈칸·|·괄호 무시 포함 검색) — 고르기 줄을 `native-quantity-sheets.js` 로 옮겨 700줄 제한 유지.
|
||||
- 검증완료: `test_est_quantity_sheets.py` 검색 시험 포함 수량 시험 22개 통과. ORCA 브라우저에서 「집수」 검색 → 항목에 집수정이 있는 「배수」만·선택 이동, 전체보기 → 3개 복귀, 샘플 복구.
|
||||
- [x] 수량산출서 삽입·선택공종 삭제(원본 TFRMMULLANG btnInsert·btnSelectDel) — 「삽입」: 현재 산출서 앞에 공종명칭·규격을 물어 새 산출서, 「선택공종 삭제」: 고른 여러 산출서를 한 번에 지움.
|
||||
- 검증완료: 산출서·바인딩 시험 7개 통과. ORCA 브라우저에서 둘째 위치에 「끼운 공종/1m」 삽입, 1·3번째 고른 삭제 → 「끼운 공종」만 남음, 샘플 복구.
|
||||
- [x] 새내역서 파일로저장(원본 mnuSaveNewEst 0x113384c) — 현재 화면은 그대로, 표 12개의 「_」 당초값 칸 비움·신규 표시 끔·원가 안전관리비 특례 줄(CalcIndex 4)→1·당초 사본 뺀 새 작업을 이름 붙여 서버에 따로 저장하고 내려받기. 재계산 없음.
|
||||
- 검증완료: 바인딩 시험 통과. ORCA 브라우저에서 GN_ 5·신규 표시·당초 사본 둔 샘플 → 새 작업에는 GN_ 없음·신규 끔·당초 사본 없음, 현재 화면은 GN_ 5 그대로, 시험 작업 지우고 샘플 복구.
|
||||
- [x] 단가산출 전역변수(원본 mnuCostSetGlobalVar 0xf053cc · mnuCostGlobalvarList · 전역표 0x93c22c — spec_cost_tools §2) — LineData 의 이름(화면 ⟦전역⟧) 로 대입한 변수는 호표가 바뀌어도 남아 다른 호표에서 표지 없이 써도 찾음(그 호표 변수 → 전역표 차례). 「전역변수 지정,해제」: 마지막 산출근거 칸 커서 아래 낱말(영문자 포함 [0-9A-Za-z_])을 감싸거나 풂(원본 거부 문구). 「전역변수 목록」: 처음부터 계산한 대입마다 변수명·값·이동. 원본처럼 전역표는 저장하지 않고 계산 때마다 다시 만듦.
|
||||
- 검증완료: `test_est_global_vars.py`(다른 호표 Q×10 = 60, 표지 유지, 비전역 R 은 못 찾음, 지정·해제·거부, 표시·되돌림) 및 REA 대조 39개 통과. ORCA 브라우저에서 샘플 단가산출 q 줄 지정 → 목록 「q 0.7」·이동으로 그 줄 선택, 도급액 불변, 샘플 복구.
|
||||
- [x] JDLExcel과 단가비교(원본 ActionJDLCompare, 숨은 Ctrl+Alt+J → TfrmJDLExcelCompare) — 일위대가·단가산출·기계경비 탭, 시트번호·코드칼럼(A)·합계셀칼럼(H), Excel(.xlsx·.xls — `/api/read-xls`) 1행~(호표 수+50)행에서 코드를 품은 칸의 같은 행 합계와 TC 비교, 차이·없음 표시. 보기 전용. 웹은 메뉴 단추로 엶.
|
||||
- 검증완료: `test_est_jdl_compare.py`(같음·차이 1·없음·+50행 한계·xls 셀 변환) 통과. ORCA 브라우저에서 일위대가 8개 중 같음 1·차이 10·없음 6 표시.
|
||||
- 제외: TfrmMultiEditor(인쇄 부품의 설계용 편집기 — 실행 파일에 클래스 없음), Est Plus(구버전 .est) 단가비교·내보내기(숨은 단축키, 394바이트 레코드 형식 — 구버전 불러오기와 함께 남음).
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
<script defer src="native-row-links.js"></script>
|
||||
<script defer src="native-es-info.js"></script>
|
||||
<script defer src="native-jdl-compare.js"></script>
|
||||
<script defer src="native-global-vars.js"></script>
|
||||
<script defer src="native-new-estimate.js"></script>
|
||||
<script defer src="native-wonga-tools.js"></script>
|
||||
<script defer src="native-estimate-print.js"></script>
|
||||
<script defer src="native-xml-check.js"></script>
|
||||
@@ -96,6 +98,7 @@
|
||||
<hr />
|
||||
<button data-action="save">저장하기 Ctrl+S</button
|
||||
><button data-action="save-as">다른 이름으로 저장</button
|
||||
><button data-action="save-new-estimate">새내역서 파일로저장</button
|
||||
><button data-action="sample">REA 샘플 열기</button>
|
||||
<hr />
|
||||
<button data-action="csv">현재 자료 CSV 내보내기</button
|
||||
@@ -145,7 +148,9 @@
|
||||
><button data-action="schedule-mark">선택 행 예정공정표에 표시/해제</button
|
||||
><button data-action="data-link">하위데이타 연결/끊기</button
|
||||
><button data-action="sum-index">SUM 색인지정</button
|
||||
><button data-action="cost-part">비목별 단가 불러오기(일위대가)</button>
|
||||
><button data-action="cost-part">비목별 단가 불러오기(일위대가)</button
|
||||
><button data-action="global-var">전역변수 지정,해제</button
|
||||
><button data-action="global-var-list">전역변수 목록</button>
|
||||
<hr />
|
||||
<button data-action="find">찾기·다음찾기(Ctrl+F)</button>
|
||||
<button data-action="multi-search">통합 검색(저장된 다른 작업)</button>
|
||||
|
||||
@@ -634,7 +634,9 @@ const EstBusinessActions = (() => {
|
||||
(typeof EstCellClip !== "undefined" && EstCellClip.handle(name, state)) ||
|
||||
(typeof EstRowLinks !== "undefined" && EstRowLinks.handle(name, state)) ||
|
||||
(typeof EstEsInfo !== "undefined" && EstEsInfo.handle(name, state)) ||
|
||||
(typeof EstJdlCompare !== "undefined" && EstJdlCompare.handle(name, state, node))
|
||||
(typeof EstJdlCompare !== "undefined" && EstJdlCompare.handle(name, state, node)) ||
|
||||
(typeof EstGlobalVars !== "undefined" && EstGlobalVars.handle(name, state, node)) ||
|
||||
(typeof EstNewEstimate !== "undefined" && EstNewEstimate.handle(name, state))
|
||||
) {
|
||||
EstApp.render();
|
||||
} else if (name === "insert-annual") {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// 원본 단가산출 「전역변수 지정,해제」(mnuCostSetGlobalVar 0xf053cc)·「전역변수 목록」(mnuCostGlobalvarList → TfrmGlobalVarList).
|
||||
// 지정·해제: 마지막으로 누른 산출근거 칸의 커서 아래 낱말([0-9A-Za-z_], 영문자 하나 이상)을 ⟦전역⟧ 표지로 감싸거나 푼다.
|
||||
// 목록: 단가산출을 처음부터 계산해 전역변수 대입마다 변수명·값과 그 줄로 이동.
|
||||
const EstGlobalVars = (() => {
|
||||
const MARK = "⟦전역⟧";
|
||||
let last = null;
|
||||
if (typeof document !== "undefined" && document.addEventListener)
|
||||
document.addEventListener("focusin", (event) => {
|
||||
const t = event.target.closest?.('[data-table="CostMainDat"][data-field="LineData"]');
|
||||
if (t) last = t;
|
||||
});
|
||||
|
||||
function toggle(text, caret) {
|
||||
const word = /[0-9A-Za-z_]/;
|
||||
let a = caret,
|
||||
b = caret;
|
||||
while (a > 0 && word.test(text[a - 1])) a--;
|
||||
while (b < text.length && word.test(text[b])) b++;
|
||||
const name = text.slice(a, b);
|
||||
if (!name || !/[A-Za-z]/.test(name))
|
||||
throw Error(
|
||||
'선택한 변수는 전역변수로 지정할수 없습니다.\n\n변수는 알파벳과 숫자,"_"로 이루어져야합니다.\n\n예) Q , V1 , L_1',
|
||||
);
|
||||
const before = text.slice(0, a),
|
||||
after = text.slice(b);
|
||||
if (before.endsWith(MARK) && after.startsWith(MARK))
|
||||
return before.slice(0, -MARK.length) + name + after.slice(MARK.length);
|
||||
return `${before}${MARK}${name}${MARK}${after}`;
|
||||
}
|
||||
|
||||
function list(state) {
|
||||
const out = EstText.calculate(state.project.tables.CostMainDat || [], state.project.options);
|
||||
const { esc, number } = EstApp;
|
||||
EstApp.showDialog(
|
||||
"전역변수 목록",
|
||||
out.globals.length
|
||||
? `<table class="sheet"><thead><tr><th>변수명</th><th>값</th><th>이동</th></tr></thead><tbody>${out.globals.map((g) => `<tr><td>${esc(g.name)}</td><td>${number(g.value)}</td><td><button data-action="global-var-go" data-row="${g.index}">이동</button></td></tr>`).join("")}</tbody></table>`
|
||||
: "<p>전역변수가 없습니다. 단가산출 산출근거의 변수에 커서를 두고 「전역변수 지정,해제」를 누르세요.</p>",
|
||||
);
|
||||
}
|
||||
|
||||
function handle(name, state, node) {
|
||||
if (name === "global-var") {
|
||||
if (!last) throw Error("단가산출 산출근거 칸의 변수에 커서를 두세요.");
|
||||
const index = Number(last.dataset.row),
|
||||
text = last.value,
|
||||
next = toggle(text, last.selectionStart ?? 0);
|
||||
EstApp.change(
|
||||
(project) => (project.tables.CostMainDat[index].LineData = EstText.encode(next)),
|
||||
);
|
||||
} else if (name === "global-var-list") list(state);
|
||||
else if (name === "global-var-go") {
|
||||
EstApp.byId("dialog").close();
|
||||
const index = Number(node.dataset.row);
|
||||
state.view = "CostMainDat";
|
||||
const rows = state.project.tables.CostMainDat;
|
||||
let head = index;
|
||||
while (head > 0 && !(Number(rows[head].Index) > 0)) head--;
|
||||
state.group = rows[head]?.Code || state.group;
|
||||
state.selection.clear();
|
||||
state.selection.add(index);
|
||||
EstApp.render();
|
||||
document
|
||||
.querySelector(`[data-table="CostMainDat"][data-row="${index}"][data-field="LineData"]`)
|
||||
?.scrollIntoView({ block: "center" });
|
||||
} else return false;
|
||||
return true;
|
||||
}
|
||||
return { toggle, handle };
|
||||
})();
|
||||
@@ -0,0 +1,54 @@
|
||||
// 원본 「새내역서 파일로저장」(mnuSaveNewEst 0x113384c) — 현재 문서는 그대로 두고 설계변경 흔적을 지운 새 작업을 따로 저장.
|
||||
// 표 12개(노임·자재·견적·표준·기계기준·기계 본문/호표·단산 본문/호표·일위 본문/호표·내역서)의 「_」로 끝나는 칸(당초값)을 비우고
|
||||
// 신규 표시(NewItem)를 끄며, 원가 안전관리비 특례 줄(CalcIndex 4)은 1 로, 당초 사본(baseline)은 뺀다. 재계산 없음.
|
||||
const EstNewEstimate = (() => {
|
||||
const TABLES = [
|
||||
"LaborcDat",
|
||||
"StuffcDat",
|
||||
"EstiMateDat",
|
||||
"SilJukDat",
|
||||
"MachGijunDat",
|
||||
"MachMainDat",
|
||||
"MachTitleDat",
|
||||
"CostMainDat",
|
||||
"CostTitleDat",
|
||||
"HeadMainDat",
|
||||
"HeadTitleDat",
|
||||
"EstMainDat",
|
||||
];
|
||||
function build(project, name) {
|
||||
const next = structuredClone(project);
|
||||
delete next.id;
|
||||
delete next.baseline;
|
||||
next.name = name;
|
||||
for (const table of TABLES)
|
||||
for (const row of next.tables[table] || []) {
|
||||
for (const key of Object.keys(row)) if (key.endsWith("_")) row[key] = null;
|
||||
if ("NewItem" in row) row.NewItem = false;
|
||||
}
|
||||
for (const row of next.tables.Wonga || [])
|
||||
if ((Number(row.CalcIndex) & 0xff) === 4) row.CalcIndex = 1;
|
||||
return next;
|
||||
}
|
||||
async function run(state) {
|
||||
const name = prompt("새 내역서 이름", `${state.project.name || "내역서"} (새 내역서)`)?.trim();
|
||||
if (!name) return;
|
||||
const result = await EstApp.api("/api/save", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(build(state.project, name)),
|
||||
});
|
||||
EstApp.download(`${result.name}.estweb.json`, JSON.stringify(result, null, 2));
|
||||
state.notice = `새 내역서 「${result.name}」를 따로 저장했습니다. 지금 화면은 그대로입니다.`;
|
||||
EstApp.render();
|
||||
}
|
||||
function handle(name, state) {
|
||||
if (name !== "save-new-estimate") return false;
|
||||
run(state).catch((error) => {
|
||||
state.notice = error.message;
|
||||
EstApp.render();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return { build, handle };
|
||||
})();
|
||||
@@ -129,7 +129,7 @@ const EstQuantitySheets = (() => {
|
||||
`<option value="${esc(sheet.Code)}" ${state.quantitySheet === sheet.Code ? "selected" : ""}>${esc(sheet.Name || sheet.Code)}</option>`,
|
||||
)
|
||||
.join("");
|
||||
return `<div class="business-tools"><label>수량산출서 <select id="quantity-sheet-choice"><option value="manual">수식표 직접 작성</option>${options}</select></label><input data-quantity-query value="${esc(state.quantityQuery || "")}" placeholder="명칭·규격" aria-label="산출서 검색"><button data-action="quantity-sheet-search">검색</button><button data-action="quantity-sheet-all">전체보기</button><button data-quantity-new>새 산출서</button><button data-quantity-excel>Excel 셀 수량 연결</button><button data-quantity-excel-links>Excel 연결 관리</button></div>`;
|
||||
return `<div class="business-tools"><label>수량산출서 <select id="quantity-sheet-choice"><option value="manual">수식표 직접 작성</option>${options}</select></label><input data-quantity-query value="${esc(state.quantityQuery || "")}" placeholder="명칭·규격" aria-label="산출서 검색"><button data-action="quantity-sheet-search">검색</button><button data-action="quantity-sheet-all">전체보기</button><button data-quantity-new>새 산출서</button><button data-action="quantity-sheet-insert">삽입</button><button data-action="quantity-sheet-multi-delete">선택공종 삭제</button><button data-quantity-excel>Excel 셀 수량 연결</button><button data-quantity-excel-links>Excel 연결 관리</button></div>`;
|
||||
}
|
||||
|
||||
function handle(name, state, node) {
|
||||
@@ -153,6 +153,55 @@ const EstQuantitySheets = (() => {
|
||||
state.quantitySheet = first.Code;
|
||||
return true;
|
||||
}
|
||||
if (name === "quantity-sheet-insert") {
|
||||
// 삽입(TFRMMULLANG btnInsert): 현재 산출서 앞에 새 산출서, 공종명칭(품명·규격) 입력
|
||||
const title = prompt("공종명칭", "새 수량산출서");
|
||||
if (title == null) return true;
|
||||
const size = prompt("규격", "") ?? "";
|
||||
EstApp.change((project) => {
|
||||
const sheets = (project.quantitySheets ||= []),
|
||||
at = Math.max(
|
||||
0,
|
||||
sheets.findIndex((s) => s.Code === code),
|
||||
),
|
||||
next = `Q${EstApp.uid()}`;
|
||||
sheets.splice(at, 0, {
|
||||
Code: next,
|
||||
Name: title.trim() || "새 수량산출서",
|
||||
Size: size.trim(),
|
||||
rows: [],
|
||||
items: [],
|
||||
options: { digits: 2, roundMode: 2 },
|
||||
});
|
||||
state.quantitySheet = next;
|
||||
}, false);
|
||||
return true;
|
||||
}
|
||||
if (name === "quantity-sheet-multi-delete") {
|
||||
// 선택공종 삭제(btnSelectDel): 고른 산출서들을 한 번에 지움
|
||||
const { esc } = EstApp;
|
||||
EstApp.showDialog(
|
||||
"선택공종 삭제",
|
||||
`<div style="max-height:360px;overflow:auto">${(state.project.quantitySheets || []).map((s) => `<label style="display:block"><input type="checkbox" name="qd-sheet" value="${esc(s.Code)}">${esc(s.Name || s.Code)} ${esc(s.Size || "")}</label>`).join("")}</div><button data-action="quantity-sheet-multi-delete-ok">삭제</button>`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (name === "quantity-sheet-multi-delete-ok") {
|
||||
const codes = new Set(
|
||||
[...document.querySelectorAll('#dialog-content [name="qd-sheet"]:checked')].map(
|
||||
(n) => n.value,
|
||||
),
|
||||
);
|
||||
if (!codes.size) throw Error("지울 산출서를 고르세요.");
|
||||
if (!confirm(`산출서 ${codes.size}개를 삭제하시겠습니까?`)) return true;
|
||||
EstApp.byId("dialog").close();
|
||||
EstApp.change((project) => {
|
||||
project.quantitySheets = project.quantitySheets.filter((s) => !codes.has(s.Code));
|
||||
if (codes.has(state.quantitySheet))
|
||||
state.quantitySheet = project.quantitySheets[0]?.Code || "manual";
|
||||
}, false);
|
||||
return true;
|
||||
}
|
||||
if (name === "quantity-sheet-copy") {
|
||||
EstApp.change((project) => {
|
||||
state.quantitySheet = copy(project, code, EstApp.uid());
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// REA f2a1fc/f254c8: LineData 표지와 일반·성분·마지막 합계의 정밀도.
|
||||
// 전역변수(spec_cost_tools §2 · 0x93c22c): \x05이름\x05 표지로 대입한 변수는 호표가 바뀌어도 남아(전역표) 다른 호표에서 표지 없이 써도 찾는다.
|
||||
// 찾는 차례 = 그 호표 변수 → 전역표. 표지는 계산 동안 떼고 계산 뒤 같은 낱말에 다시 붙인다.
|
||||
const EstText = (() => {
|
||||
const { num, components, floating, sum } = EstMath;
|
||||
const marker = /\x12([-\d.,]+)\x12/;
|
||||
@@ -6,7 +8,7 @@ const EstText = (() => {
|
||||
const clean = (s) =>
|
||||
String(s || "")
|
||||
.replace(/[\x06\x07][^\x06\x07]*[\x06\x07]/g, "")
|
||||
.replace(/[\x03\x0c\x0e\x11\x12\x13\x15\x1a]/g, "");
|
||||
.replace(/[\x03\x05\x0c\x0e\x11\x12\x13\x15\x1a]/g, "");
|
||||
const numeric = (s) => Number(String(s).replaceAll(",", ""));
|
||||
const sumCurrency = (values) =>
|
||||
values.reduce((n, value) => n + EstMath.even(num(value) * 10000), 0) / 10000;
|
||||
@@ -56,8 +58,10 @@ const EstText = (() => {
|
||||
titles = [],
|
||||
errors = [],
|
||||
checks = [];
|
||||
const globals = Object.create(null),
|
||||
globalList = [];
|
||||
let head = null,
|
||||
variables = Object.create(null),
|
||||
variables = Object.create(globals),
|
||||
changed = new Map(),
|
||||
currentSource = null,
|
||||
finalOverride = null,
|
||||
@@ -94,7 +98,7 @@ const EstText = (() => {
|
||||
if (num(row.Index) > 0) {
|
||||
finish();
|
||||
head = row;
|
||||
variables = Object.create(null);
|
||||
variables = Object.create(globals);
|
||||
changed = new Map();
|
||||
currentSource = null;
|
||||
finalOverride = null;
|
||||
@@ -170,7 +174,10 @@ const EstText = (() => {
|
||||
errors.push(`${head.Code}:${index + 1} ${error.message}`);
|
||||
}
|
||||
}
|
||||
let text = original.replace(/\{([+-])\}/g, "$1");
|
||||
const globalNames = [...original.matchAll(/\x05([A-Za-z][A-Za-z0-9_]*)\x05/g)].map(
|
||||
(m) => m[1],
|
||||
);
|
||||
let text = original.replace(/\x05/g, "").replace(/\{([+-])\}/g, "$1");
|
||||
let selectionError = "";
|
||||
if (num(row.LinkIndex) === 62) {
|
||||
text = text.replace(/(\x07([A-Z])\x07\s*)\x1a[^\x1a]*\x1a/g, (whole, reference, group) => {
|
||||
@@ -599,16 +606,28 @@ const EstText = (() => {
|
||||
errors.push(`${head.Code}:${index + 1} ${error.message}`);
|
||||
row.Error = error.message;
|
||||
}
|
||||
for (const name of globalNames) {
|
||||
if (Object.hasOwn(variables, name)) {
|
||||
globals[name] = variables[name];
|
||||
globalList.push({ name, value: variables[name], index });
|
||||
}
|
||||
if (!String(row.LineData).includes(`\x05${name}\x05`))
|
||||
row.LineData = String(row.LineData).replace(
|
||||
new RegExp(`(?<![A-Za-z0-9_])${name}(?![A-Za-z0-9_])`),
|
||||
`\x05${name}\x05`,
|
||||
);
|
||||
}
|
||||
if (!hasAutoTotals && component[sc])
|
||||
plainTotals[component[sc]] = num(plainTotals[component[sc]]) + num(row.TC);
|
||||
}
|
||||
finish();
|
||||
return { rows, titles, errors, checks };
|
||||
return { rows, titles, errors, checks, globals: globalList };
|
||||
}
|
||||
const names = {
|
||||
"\x06": "구간지정",
|
||||
"\x07": "구간합계",
|
||||
"\x03": "단가",
|
||||
"\x05": "전역",
|
||||
"\x0c": "다른줄",
|
||||
"\x0e": "품명",
|
||||
"\x11": "변수",
|
||||
@@ -620,7 +639,7 @@ const EstText = (() => {
|
||||
};
|
||||
const display = (text) =>
|
||||
String(text || "").replace(
|
||||
/[\x03\x06\x07\x0c\x0e\x11\x12\x13\x15\x16\x1a]/g,
|
||||
/[\x03\x05\x06\x07\x0c\x0e\x11\x12\x13\x15\x16\x1a]/g,
|
||||
(ch) => `⟦${names[ch]}⟧`,
|
||||
);
|
||||
const encode = (text) =>
|
||||
|
||||
@@ -85,6 +85,8 @@ const allowed = new Set([
|
||||
"native-row-links.js",
|
||||
"native-es-info.js",
|
||||
"native-jdl-compare.js",
|
||||
"native-global-vars.js",
|
||||
"native-new-estimate.js",
|
||||
"native-wonga-tools.js",
|
||||
"native-estimate-print.js",
|
||||
"native-xml-check.js",
|
||||
|
||||
Reference in New Issue
Block a user