Merge remote-tracking branch 'origin/CODEX' into main_laptop_1

This commit is contained in:
2026-11-13 09:03:51 +09:00
7 changed files with 42 additions and 7 deletions
+1 -1
View File
@@ -307,7 +307,7 @@ def test_wonga_scan_parity_with_calculate_forward_refs_old_cost_bases_ranges_and
// C가 '100/B'를 계산할 때, 오래된 Cost(0)를 쓰면 0으로 나눔 오류이지만,
// calculate와 동일하게 재귀 평가 시 B=20이 되어 C=5 정상 계산되고 오탐 없어야 함.
const w1 = [
{Gubun:'B',Cost:0,VSusic:'A*2'},
{Gubun:'B',Cost:0,VSusic:'A*2.0'}, // 두 자리 이하 정수는 행 참조(원본 c9af48)라 소수로
{Gubun:'A',Cost:10},
{Gubun:'C',VSusic:'100/B'}
];
+27
View File
@@ -55,3 +55,30 @@ def test_install_wonga_formats_read():
result = json.loads(out)
assert result["g"] == 30 and result["s"] == 30
assert result["rows"] > 10 and result["cost"] is False and result["first"] == 54
@pytest.mark.skipif(not INSTALL.exists(), reason="원본 설치 폴더 없음")
def test_standard_template_unknown_symbol_is_zero_like_original():
rows = subprocess.check_output(
[
"node",
"-e",
"const w=require(process.argv[1]);console.log(JSON.stringify(w.get(true,4).rows))",
str(ROOT / "tools/est-test/wonga-formats.cjs"),
],
text=True,
encoding="utf-8",
)
result = run(
"math",
f"""(()=>{{
const rows={rows};rows[0].Cost=1000000;rows[1].Cost=300000; // 직접공사비·직접노무비(엔진이 내역에서 채우는 값)
const c=EstWonga.calculate(rows,{{VerFormat:false}});
const r=(n)=>c.rows.find(x=>x.NumStr===String(n));
return {{errors:c.errors,safety:r(9).Cost,two:r(2).Cost,v19:r(19).Cost,total:c.total}};
}})()""",
)
assert result["errors"] == []
# (A × 0.496 + 2 + 19/1.1) × 0.0248 — A 는 원본처럼 0
assert result["safety"] == int((result["two"] + result["v19"] / 1.1) * 0.0248)
assert result["total"] > 0
+2 -3
View File
@@ -504,7 +504,8 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD
- [x] 인쇄범위·인쇄양식목록(spec_pc §6.2·§6.4) — 미리보기 「인쇄범위」: 부수 · 현재면(보이는 쪽) · 현재항목 전체(쪽 범위, 0 = 전체, 넘침·거꾸로 알림, 부수마다 그 서식 간지) · 일괄인쇄(묶음 인쇄). 묶음 인쇄 창 「인쇄양식목록」: 지금 인쇄표·설정·묶음 고르기를 이름 붙여 등록 · 열면 통째로 바꿈 · 삭제 · .pfot 파일 불러오기/저장. 기성·연차 따로 목록은 두지 않음.
- 검증완료: 시험 2개·묶음 인쇄 시험 통과. ORCA 브라우저에서 3쪽 중 1~2쪽 × 2부 = 4쪽, 범위 넘침 알림, 양식 등록·열기(묶음 고르기 되돌아옴)·삭제 확인, 원복.
- [x] 작업모드 표준내역서·표준설계변경·최근 계약심사목록(spec_pc §4) — 작업모드 메뉴 내역서·설계변경·표준내역서·표준설계변경이 모드 값(CurrentWorkMode, ESTX 읽기·쓰기)을 바꿈. 일반↔표준 묶음이 바뀌면 지금 원가계산서를 옛 묶음 칸에 두고 새 묶음 것을 꺼냄(없으면 설치 양식목록 — 일반 WongaFileList·표준 SWongaFileList 각 30종 — 에서 고름, 번호 기준 계산). 원본은 모드를 바꿀 때 문서를 닫고 최근 목록을 띄우나 웹은 지금 작업 유지. 최근 계약심사목록 = 저장한 작업 중 계약심사 입력이 있는 것.
- 미결: 표준 양식의 산업안전보건관리비 식 「A × 0.496」 의 A 가 무엇인지 미확인 — 웹은 「참조 항목 없음: A」 로 알림(원본 확인 필요).
- 표준 양식 「A」 확인(원본 c9af48·c9a310): 원가 수식은 열쇠 칸 하나(세로 Gubun · 가로 NumStr)만 찾고, 영문 식별자·두 자리 이하 정수가 없으면 오류 없이 0 — 안전관리비 = (2 + 19/1.1) × 0.0248 (A × 0.496 은 원본도 빠짐). 웹 계산을 같게 맞춤(자료 오류 검색은 계속 알림). 원가 연결 54 행은 내역 직접공사비를 받게 함 — 설치 양식 1번 행 참조. 웹 데모 양식 요율은 소수로(10 → 10.0).
- 검증완료: 원가·수식 시험 300개·실물 estx 6건 저장값 대조 차이 0. ORCA 브라우저에서 표준 양식 4번: 오류 없음·직접공사비 44,128,795·안전관리비 1,113,460·도급 86,013,553, 원복.
- 검증완료: 작업모드 시험 2개·ESTX 저장 시험 34개 통과. ORCA 브라우저에서 표준내역서로 바꿈 → 표준 양식 30종 → 열기, 내역서로 돌아오면 원래 원가계산서 31행 복원, 계약심사목록 창 확인, 원복.
- [x] 동원 확인·내역서 탭(TfrmDongWon Button1~4 · spec_part §4) — 동원 화면 단추 확인 장비·단산·일위: 호표마다 1 단위를 펼친 항목 금액 합과 호표 금액을 대조해 10원 넘게 어긋나면 표시(어긋난 것만 보기), 행을 고르면 당초값·계산값(노무·재료·경비·실적)·항목 목록. 내역서: 내역 줄마다 펼친 항목. 웹 동원 계산은 기계경비를 더 쪼개지 않아 장비 확인은 늘 맞음.
- 검증완료: 확인 시험 1개·동원 관련 시험 35개 통과. ORCA 브라우저에서 일위 8건 모두 일치·당초값=계산값, 항목 3개 표시.
@@ -513,11 +514,9 @@ ative-standard.js에 표준시장단가 원천(SilJukDat)과 내역 행(EstMainD
- [x] 환경설정 전체 창(TfrmOption btnLoad · spec_pb2 §9) — 탭 다섯(공통적용·내역서·일위단산기계·표준자재원가·설계변경수량심사) 92칸. 웹 계산·표시에 이어진 칸은 그 옵션에(일위 자리수·단산 금액란·중간 자리수·외화 절하·재료비 처리·이윤 사사오입·관급 조달수수료·원가 방향·자동계산·재계산 범위·정산금액·비목순서·정렬·두줄·Enter·코드 열·0 표시·바탕색·레벨·발췌근거·집계 진하게), 나머지는 값만(Env), PC 설정은 이 브라우저. 바꾼 칸만 씀. 적용 때 원본 알림(자동계산 끔/켬·표준단가비목·조달청 계산방식·산식 표시·공종+품명 합치기)과 순공사비 명칭 바꾸면 순공사비 행 이름도 바꿈. 기성 화면에서는 기성 환경설정. 순공사비 상단/하단 이동·제잡비 행 넣기는 값만(웹 계산이 순공사비 위치를 아래로 가정).
- 검증완료: 환경설정 시험 1개·표시·기성 시험 4개 통과. ORCA 브라우저에서 탭 5·칸 92, 코드표시 켬 → 자재 표에 코드 열, 순공사비 명칭 → 행 이름 바뀜, 원복.
- [x] 구버전 EST Plus 자료 불러오기·저장·단가비교(spec_misc §2 · 원본 역분석 spec_estplus — 레코드 394/565 바이트, Extended·ShortString) — 불러오기: .est/.ebk 를 풀어 내역·일위·단산·기계·노임·자재·표준·견적(565)·원가와 설계변경 c* 를 표로, 저장 금액 유지(자동계산 끔), 호표 머리에 코드를 매기고 본문 연결 행은 같은 품명·규격 원천 코드를 붙임(웹 보충). 저장(Ctrl+Shift+F12): 394 판 .est. 단가비교(Ctrl+Alt+E): 기계경비·단가산출·일위대가·내역서 중 골라 구형 파일 머리 레코드와 차례 짝, 합계 다르면 표시, 더블클릭 이동. 메모(.MEM)·기계 기준표(.mot)는 읽지 않음.
- 미결: 실제 구버전 .est 표본이 없어 웹이 만든 파일로만 왕복 확인 — 원본 파일 대조 필요(문자 코드 cp949 가정).
- 검증완료: 구버전 시험 2개 통과(Extended 왕복, 샘플 공사 쓰기→읽기 금액 일치, 단가산출 글줄·머리 꼬리표, 품명·규격 연결, 단가비교 자리). ORCA 브라우저에서 .est 저장 17,864 바이트 → 다시 불러오기 76행, 단가비교 창·일위 8건 일치, 원복.
- 경쟁사 실물 시험(지식DB 경쟁사 폴더 estx 3건 — 예제 2 · 다산소프트 main 22MB, 구버전 .est 실물은 없음): 저장값 대조 차이 0(예제1 원본 괄호 오류 1건은 원본 그대로) · ESTX 그대로 저장 = 원본과 같은 바이트 · 근거 메모 125건 글자 일치·그림 42개 보존·고친 메모 ESTX 저장 왕복 · 구버전 .est 쓰기→읽기 내역·일위 금액 차이 0 · 단가비교 불일치 0 · 화면 13종·환경설정·인쇄 미리보기·작업모드 전환 오류 없음.
- 고친 것: 구버전 저장 때 cp949 에 없는 글자는 「?」, 기계경비 본문 Index(−3·−2·−1) 유지, % 단위 수량은 소수로 써서 음수·큰 값도 되돌아옴.
- 남은 관찰: 동원 확인에서 단산·일위 일부(예제 15·1건, main 133·57건)가 10원 넘게 어긋남 — 웹 동원 계산의 줄별 절사 차이로 보임, 원본 확인 탭도 같은지 미확인.
- [x] 다른 공사 불러오기 창(원본 barbtnSelfLoad → TfrmOtherLoad, 찾기 popSerch) — 저장된 다른 작업을 골라 같은 표를 띄우고 찾기(품명·규격, A&B 둘 다)·고른 줄을 하위 자료와 함께 가져오기(내역서는 고른 줄 아래에 줄, 나머지는 자료·호표 합침, 같은 코드는 이 공사 것). 검증완료: 바인딩 통과, ORCA 브라우저에서 산불진화임도 작업의 일위대가 3개 → 「취수장」 1개 → 가져오기 호표 8→9·하위 41건, 샘플 복구.
- [x] 사용위치 검색 창(원본 TfrmUsesDataSerch btnSerch·btnAllDisplay) — 현재 표 항목을 품명·규격(빈칸·|·괄호 무시)으로 거르고 골라 사용위치 보기. 검증완료: 바인딩 통과, ORCA 브라우저에서 노임 9개 → 「보 통」 1개 → 보통인부 사용위치 11곳.
- [x] 인쇄양식 불러오기·저장하기(원본 mnuPrintFormatLoad·Save 0x10ed318·0x10ed000 — spec_pc §3) — .pfot(인쇄정보표 MIDAS 이진) 읽기·쓰기 `print-format.cjs`(설명 BLOB·빈 칸 표지까지 보존, 원본 파일 그대로 다시 쓰면 바이트 같음). 불러오면 공사 인쇄표를 그 값으로, 저장은 지금 인쇄표를 원본 설치 main.pfot 필드 설명자 위에(`EST_PFOT_TEMPLATE`). .gfot(cxGrid 열 배치)는 웹 격자와 호환되지 않아 하지 않음.
+3 -1
View File
@@ -317,7 +317,9 @@ const EstNative = (() => {
)
continue;
const name = normalize(row.Name);
if (name.includes("직접재료")) row.Cost = directMaterial;
// 연결 54(순)직접공사비 행 = 내역 직접공사비 합(설치 원가 양식이 1번 행으로 참조)
if (num(row.LinkIndex) === 54) row.Cost = num(direct.TC);
else if (name.includes("직접재료")) row.Cost = directMaterial;
else if (name.includes("간접재료")) row.Cost = num(direct.GC) - directMaterial;
else if (name.includes("직접노무")) row.Cost = num(direct.NC);
else if (name.includes("산출경비")) row.Cost = num(direct.JC);
+4 -1
View File
@@ -42,8 +42,11 @@ const EstExpression = (() => {
const numeric = /^(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(key);
if (resolved !== undefined && (!numeric || force || settings.numericReferences))
return Number(resolved);
if (numeric && !force) return Number(key);
if (key === "PI") return Math.PI;
// 원가 수식(c9af48 · c9a310): 못 찾은 글자 식별자와 두 자리 이하 정수 참조는 오류 없이 0
if (settings.missingZero && !force && (/^[A-Za-z가-힣_]/.test(key) || /^\d{1,2}$/.test(key)))
return 0;
if (numeric && !force) return Number(key);
throw Error(`참조 항목 없음: ${key}`);
}
function atom() {
+3 -1
View File
@@ -569,6 +569,8 @@ const EstModel = (() => {
const bases = { 재료비: "GC", 노무비: "NC", 경비: "JC", 실적: "SC", 직접공사비: "TC" };
project.tables.Wonga = [{ Gubun: "기초", Name: "직접공사비", BaseField: "TC", Cost: 0 }];
const running = ["기초"];
// 원가 수식에서 두 자리 이하 정수는 행 참조라(원본 c9af48) 요율 숫자는 소수점을 붙여 씀
const literal = (n) => (Number.isInteger(Number(n)) ? `${Number(n)}.0` : String(n));
for (const [index, row] of data.costs.entries()) {
const id = row.id || `추가${index + 1}`;
project.tables.Wonga.push({
@@ -577,7 +579,7 @@ const EstModel = (() => {
Youl: `${row.rate}%`,
VSusic:
row.formula ||
`(${row.base === "누계" ? running.map((key) => `[${key}]`).join("+") : `[${bases[row.base] || "TC"}]`})*${row.rate}/100`,
`(${row.base === "누계" ? running.map((key) => `[${key}]`).join("+") : `[${bases[row.base] || "TC"}]`})*${literal(row.rate)}/100.0`,
});
running.push(id);
}
+2
View File
@@ -121,6 +121,8 @@ const EstWonga = (() => {
row,
EstExpression.evaluate(row.VSusic, (name) => resolve(name, index), {
numericReferences: true,
// 계산은 원본처럼 못 찾은 참조를 0 으로, 자료 오류 검색(scan)은 찾아서 알림
missingZero: !onError,
range: (a, b) => range(a, b, index),
}),
);