Merge remote-tracking branch 'origin/dev' into sub_laptop_1
This commit is contained in:
@@ -6,21 +6,36 @@
|
||||
|
||||
| 문서 | 용도 | Orca 아티팩트 |
|
||||
|---|---|---|
|
||||
| 00 분석 요약과 읽는 법 (이 문서) | 전체 인덱스, 결론, Aislo 비교 | https://share.onorca.dev/a/uHEsfWTvedw1 |
|
||||
| 01 전체 구조 | 실행 기술, 모듈, 파일 흐름, 기능 구조 | https://share.onorca.dev/a/jjxDJTOtRGq7 |
|
||||
| 02 데이터셋과 파일형식 | DBF 스키마, 프로젝트 패키지, 기준자료, 암호화 경계 | https://share.onorca.dev/a/boO2-Fo4CFNi |
|
||||
| 03 계산로직과 Aislo 비교 | 확인된 공식, 계층 합산, 단가·제비율 모델, 차이와 권고 | https://share.onorca.dev/a/fyN0SVUu3Oum |
|
||||
| 04 계산 데이터계보 | 입력 필드 → 계산 → 결과 필드 단계별 추적 | https://share.onorca.dev/a/S1E38cIhGiz6 |
|
||||
| 05 재구현 판정기준과 근거목록 | 확정등급, 조사 표본, 파이프라인, 중간값 계약 | https://share.onorca.dev/a/8ktCQIIDvMuP |
|
||||
| 06 규칙1·7 | RATE 좌표, 간접비 순서, 끝전·VAT | https://share.onorca.dev/a/z2v9o5Wfaim0 |
|
||||
| 07 규칙2·3·4·5 | 직접비 단가, 중기, BDQTY, 자재 채택 | https://share.onorca.dev/a/o0HLxAXG8Lhg |
|
||||
| 08 규칙6·8·9 | 재계산, 변경설계, 일괄변경 | https://share.onorca.dev/a/z58ZjH9Nu_Pb |
|
||||
| 00 분석 요약과 읽는 법 (이 문서) | 전체 인덱스, 결론, Aislo 비교 | https://share.onorca.dev/a/jAFkUlfWjAOv |
|
||||
| 01 전체 구조 | 실행 기술, 모듈, 파일 흐름, 기능 구조 | https://share.onorca.dev/a/YrTXvA_VN7OI |
|
||||
| 02 데이터셋과 파일형식 | DBF 스키마, 프로젝트 패키지, 기준자료, 암호화 경계 | https://share.onorca.dev/a/dkOGvoT4jO8S |
|
||||
| 03 계산로직과 Aislo 비교 | 확인된 공식, 계층 합산, 단가·제비율 모델, 차이와 권고 | https://share.onorca.dev/a/CPOaNTIjmPDA |
|
||||
| 04 계산 데이터계보 | 입력 필드 → 계산 → 결과 필드 단계별 추적 | https://share.onorca.dev/a/j231bE3HQqNw |
|
||||
| 05 재구현 판정기준과 근거목록 | 확정등급, 조사 표본, 파이프라인, 중간값 계약 | https://share.onorca.dev/a/NbIR-TMqLPjc |
|
||||
| 06 규칙1·7 | RATE 좌표, 간접비 순서, 끝전·VAT | https://share.onorca.dev/a/U1-yAvd5qfbj |
|
||||
| 07 규칙2·3·4·5 | 직접비 단가, 중기, BDQTY, 자재 채택 | https://share.onorca.dev/a/c7n8XytHiqGt |
|
||||
| 08 규칙6·8·9 | 재계산, 변경설계, 일괄변경 | https://share.onorca.dev/a/G21A30euiRNl |
|
||||
| 09 통합 재구현 명세 | 내부 데이터셋, 계산 순서, 호환 어댑터, 골든시험 | https://share.onorca.dev/a/7eULzjbfyZHM |
|
||||
| 10 System 186개 파일 분석장부 | 전 파일별 판독 상태, 실제 형식, 핵심 모듈과 기준자료 구조 | https://share.onorca.dev/a/zHj5IbV4dfnL |
|
||||
| 11 CPM 계산코어 정적분석 | 식 평가, 반올림, 간접비·안전비·변경설계·끝전조정 재구현 명세 | https://share.onorca.dev/a/U6HIpHCbt9XF |
|
||||
| 12 원가계산 4형식과 RATE표 복원 | 일반·수공·실적일반·실적수공 공식, 화면 열, 내부 선택값 | https://share.onorca.dev/a/y-R9TQqgEFfo |
|
||||
| 13 통제시험 목록 | 정적 분석으로 못 가르는 경계를 시험 11개로 분리, 우선순위·완료판정 | https://share.onorca.dev/a/lcmxdrG2SWvE |
|
||||
| 14 최종 분석완료 보고서 | 도달 수준, A등급 확정 목록, 남은 것, 완료 판정 기준, 산출물 | https://share.onorca.dev/a/AmgldP7q_Qzc |
|
||||
| 15 재구현 가능성 검증 | **무엇을 만들 수 있고 무엇이 막혀 있나** — 층별 확보표, 문서 오류 정정, T1 좌표 확정 | https://share.onorca.dev/a/t6HGy0HlbEBl |
|
||||
| 16 골든셋 회귀 결과 | **원가계산을 다시 세워 원 단위로 맞춘 실증** — 프로젝트 6건 전 항목 일치 | (저장소 파일) |
|
||||
| 17 단가조립 검증 | **일위대가·중기 시간당 사용료 구조와 손료율 실측** | (저장소 파일) |
|
||||
| 18 누락 계산로직 점검 | **사슬에 빠진 칸 찾기** — 노임 환산식·단가산출 Q식·설정 스냅샷·집계표 반올림 | (저장소 파일) |
|
||||
| 19 진행률 장부 | **어디까지 봤고 무엇이 남았나** — 원천별 실측 진행률, 차단점 현황 | (저장소 파일) |
|
||||
| 20 설정파일과 보고서 카탈로그 | **`Start8x.Ini`·`SET_REP`** — 간접비 코드 사전 301~370, 서식 75종 | (저장소 파일) |
|
||||
| 21 정정과 트랙 통합 | **⛔ `SMT` 판정 정정 · 지식DB 트랙 셋 통합 · `PRJLST.SMT` 설정 원장 · 암호화의 성격** | (저장소 파일) |
|
||||
| 22 UI 폼 분석 | **폼 233개 중 3개만 봤던 것** — 선택축 272개, `ACTTYP`·자리수 키·일괄변경 규칙 | (저장소 파일) |
|
||||
| 23 `COSTN.SMT` 전수파싱 | **단가산출 원장** — 참조 1,570·품 1,460·Q식 187개(10꼴)·운반 사이클 | (저장소 파일) |
|
||||
| 24 모듈구조 전수 | **46개 모듈 export** — `DP_Common` 73함수, 플러그인 규약, 동글, 8차 해소 | (저장소 파일) |
|
||||
| 25 설정키 전수와 식템플릿 | **설정 사슬 닫기** — `PRJLST.SMT` 키 241종, 간접비 식 26개 | (저장소 파일) |
|
||||
|
||||
> **정본은 저장소 파일이다** — `resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/`.
|
||||
> 위 Orca 링크는 보기용이며, 문서를 지식DB 자리로 옮기면서 주소가 한 번 바뀌었다.
|
||||
> 09·10·11·12·13·14번 링크는 재발행이 속도제한에 걸려 **아직 옛 주소**다 — 그 여섯은 저장소 파일을 볼 것.
|
||||
|
||||
중요: C등급으로 표시한 규칙은 “분석 누락”이 아니라 보유 정적 자료에 입력 전후 대응값이 없거나 레코드가 난독화되어 확정 불가능한 경계다. 문서에는 각 경계를 확정할 STmate 통제 대조시험까지 명시했다. System 폴더는 186/186개 모두 내부 구조까지 판독했으며 미판독 파일은 0개다.
|
||||
|
||||
@@ -40,6 +55,55 @@
|
||||
| `JB_R` 35열 확장 시점 | "2020 이후" | **2020-05-27 확정** — 기존 1~20열은 한 셀도 안 바뀜 | 10번 §4 |
|
||||
| `JB_R` 열 의미 | 미상 | **1~5 간접노무비 / 6~10 기타경비** (공종 5개 순서 확정), 21·26 국가유산 | 10번 §4 |
|
||||
| 원가계산 설정 선택축 | 16축 목록 | **83축 전수** — 공통 29 + 형식별 12/11/16/15 | 12번 §10.1-2 |
|
||||
| `COSTN.DBF` 암호화 | 본문 암호화라고 적음 | **평문 — 읽힘.** 암호화는 `BDQTY`·`SYSINFO` 둘뿐 | 02번, 15번 §3 |
|
||||
| `TAMT=MAMT+LAMT+SAMT` | 예외 없는 불변조건 | **예외 있음** — 59행 중 2행(관급 흄관) | 11번 §3, 15번 §3 |
|
||||
| 프로젝트 RATE 표 | 4종 | **6종** — `RATE_A`·`RATE_B` 추가 발견 | 02번, 15번 §3 |
|
||||
| 보험 요율 좌표 | 미확정 | **A** — 산재·건강·연금·장기요양·고용 등급1~7·산안비 좌표 확정 | 12번 §9.1, 15번 §4 |
|
||||
| 원가계산 전 과정 | 규칙 서술 | **A 실증** — 프로젝트 6건을 원 단위로 다시 세워 전 항목 일치 | 16번 |
|
||||
| 내역 말단 행 판별 | 없음 | **A** — `BDCODE1` 접두 `T/E/C/:` 가 구조 행 | 16번 §4.1 |
|
||||
| 제외금액 구역 | 서술만 | **A** — `C*` 로 닫히는 구역은 직접비에서 뺌 | 16번 §4.2 |
|
||||
| 끝전조정 | B | **A 실증** — 차액 → `ROUND(차액/1.1)` → 이윤 차감 | 16번 §4.4 |
|
||||
| 차수 인계 | 「가정하지 말 것」 경고만 | **A 실증** — `BOQ12`가 현재안, 다음 파일 차수1로 인계 | 16번 §4.5·§4.6, 08번 |
|
||||
| 중기 시간당 사용료 | 손료 계수 없음 | **A** — 손료율·취득가·연료·잡품률 25개 장비 실측 | 17번 §3·§4 |
|
||||
| 호표 성분 소계 반올림 | 미확정 | **A** — 절사 (중기 398건 100%) | 17번 §2 |
|
||||
| 자재 6후보 자동채택 | C | **A 실증** — 0 아닌 후보 중 **최저가**. 복수후보 76/76 | 17번 §5.5, 07번 §5 |
|
||||
| 이윤 요율 좌표 | 미확정 | **A** — `RATE_R[20,16]`, 3건 일치 | 15번 §4 |
|
||||
| 요율의 출처 | 표에서 고른다고 가정 | **표에 없는 요율이 실재** — 사용자 수동 입력 경로 필수 | 15번 §4 |
|
||||
| 통제시험 T1~T11 | 「나중에 수행」 | **수행 불가 — STmate 라이선스 없음.** 표본 늘리기가 유일한 확장 수단 | 13번 머리, 15번 §6 |
|
||||
| 노임 시간당 환산 | **문서에 없었음** | **A** — `일노임 × 설정식`, 식은 `RXNM_`에 문자열 저장 | 18번 §2.1 |
|
||||
| 식 평가 순서 | 명시 없음 | **A** — **좌→우 순차.** 계수를 먼저 곱하면 1원 틀림 | 18번 §2.1 |
|
||||
| 단가산출 `D`코드 | **문서에 없었음** | **A** — `Q=3600qKfE/Cm`, 성분 = 절사(시간단가/Q, 소수1) | 18번 §2.2 |
|
||||
| 계산 설정 스냅샷 | 「화면 없이 재현 불가」 | **출력 XLSX `〓 INITIAL 〓` 에 있음** | 18번 §2.3 |
|
||||
| 집계표 행 금액 | 서술만 | **A** — 사사오입 100%. 호표 성분 소계(절사)와 다른 자리 | 18번 §2.4 |
|
||||
| 변경설계 간접비 모드 | 「모드가 여럿」까지만 | **A 실증** — 건강·장기요양·연금은 **당초액 유지**, 나머지는 재계산 | 19번 §6, 08번 §2 |
|
||||
| 간접비 항목 순서·코드 | 화면 텍스트 복원 | **A** — `KAN_ORD_1/2` 코드 목록, 301~370 사전 | 20번 §2.1·§2.2 |
|
||||
| 후보 슬롯 이름 | 「설정에 있을 것」 | **설치본마다 다름을 실물 확인** (유통 물가 ↔ 견적단가) | 20번 §2.3 |
|
||||
| 보고서 서식 | 30종 관찰 | **카탈로그 75종** — 수공양식·표준시장 서식 존재 | 20번 §3 |
|
||||
| `*.SMT` | ⛔ 암호화라고 적었음 | **정정 — 평문.** `COSTN.SMT` 단가 전개, `PRJLST.SMT` 설정 원장 | 21번 §1 |
|
||||
| 프로젝트 요율 | 기준표 좌표로 찾아야 한다고 봄 | **`PRJLST.SMT`에 값으로 저장** — 좌표는 읽기에 불필요 | 21번 §3 |
|
||||
| 수공형식 세부경비 8개 | 표본 없어 미상 | **항목 확정** — `R_S_SUDO`·`BOKRI`·`SOMO`·`TONGSIN`·`SEGUM`·`PEGI`·`DOSEO`·`JIGUP` | 21번 §3.2 |
|
||||
| 암호화의 성격 | 「기초단가가 막혔다」 | **사온 물가지 데이터 보호** — 뚫을 일이 아니라 살 일 | 21번 §4 |
|
||||
| 지식DB STmate 트랙 | 하나로 알고 있었음 | **셋이 서로 몰랐음** — ①계산로직 ②STC포맷 ③프로그램분석 | 21번 §2 |
|
||||
| UI 폼 | 3개만 뽑아 씀 | **선택축 272개 전수** — 폼 40개 | 22번 |
|
||||
| 일괄변경 수동값 (T10) | ⛔ 실행 필요 | **화면 안내문에 있음** — 보호표시·0원 제외 | 22번 §3.1 |
|
||||
| `ACTTYP`·`ACTOPT` | 뜻 모름 | **A** — 표준시장 적용 방식·끝전 보정 우선순위 | 22번 §3.3 |
|
||||
| `KCDNM` 등 6키 | 미확정 | **A** — 소수 자리수 설정 | 22번 §3.4 |
|
||||
| 변경 차수 | 최대 3차로 봄 | ⚠ **화면은 8차** — 저장 구조 재확인 필요 | 22번 §3.3 |
|
||||
| `COSTN.SMT` 내용 | 「평문」까지만 | **전수 파싱** — 중기 참조 1,504건, 성분 셋을 각각 | 23번 |
|
||||
| `Q` 식 | 한 꼴로 알았음 | ⚠ **최소 10가지 꼴** — 하드코딩 금지 | 23번 §3 |
|
||||
| 운반 사이클 | 안 봤음 | **적재+왕복+적하** 구조·인자 확인 | 23번 §4 |
|
||||
| 할증률 | `BDQTY` 암호화로 막힘 | ◐ **적용값 92건은 읽힘**. 편집은 **금액·율 두 갈래** | 23번 §5, 24번 §5 |
|
||||
| 모듈 분석 | 핵심 12개 | **46개 전수 export** | 24번 |
|
||||
| 식 평가기 함수 | `CALC_STR` 만 | **A** — `DP_Common` 73함수. 조건 `IIF`·중간변수·`MFRAC` 포함 | 24번 §2 |
|
||||
| 재계산 진입점 | 한 곳으로 봄 | ⚠ **31개 모듈이 `Calc_Kanjub` 보유** | 24번 §1 |
|
||||
| 라이선스 | 「없음」 | **Rainbow Sentinel SuperPro 하드웨어 동글** | 24번 §4 |
|
||||
| 엑셀 | 단방향 출력 | **양방향** — `CONV_XLS_2_ST`·`XLS_2_BDREC` | 24번 §3 |
|
||||
| 변경 차수 8차 | ⚠ 미해결 | ✅ **해소** — 한 파일 4벌, 8차는 분리형 파일 선택 | 24번 §7 |
|
||||
| 프로젝트 저장키 | 일부만 | **241종 전수** — `C_`금액 62·`R_`요율 39·`I_`선택 41·`D_`밑수·`S_`결과 | 25번 §1 |
|
||||
| 밑수 저장 | 안 남는 줄 알았음 | **`D_`(밑수)·`S_`(결과) 짝으로 저장** | 25번 §1.1 |
|
||||
| 추가금액 | 화면 문자열만 | **저장 구조** `ADD_AMT_단계_행_칸` | 25번 §1.3 |
|
||||
| 간접비 식 템플릿 | 일부 | **26개 전수** | 25번 §2 |
|
||||
| 식에 조건 분기 | 몰랐음 | ⚠ **`IF(...)` 존재** — 평가기 필수 요건 | 25번 §2.2 |
|
||||
|
||||
분석일: 2026-09-13
|
||||
대상: `C:\경영정보(주)`
|
||||
|
||||
@@ -47,7 +47,9 @@ STini.MEM, COSTN.SMT, PRJLST.SMT
|
||||
- 6개 원천/4개 안: `AMT_11..64`, `PG_11..54`
|
||||
- 표시·분류: `GUN`, `GUN2`, `IFORM`, `ACTTYP`, `EDT_DATE`
|
||||
|
||||
본문은 암호화되어 있으나 스키마는 평문이다. memo 내용은 `COSTN.SMT`에 저장된다.
|
||||
**2026-09-13 정정 — `COSTN.DBF` 본문은 암호화돼 있지 않다.** 맛보기 STC의 71개 레코드를 그대로 읽었다(`B00001 흄관기초 D=800mm M TAMT1=23441 MAMT1=2719 LAMT1=20722 SAMT1=0`). 실무 STC 7건도 같다. 암호화된 것은 `BDQTY.DBF`와 `SYSINFO.DBF` 둘뿐이다. memo 내용은 `COSTN.SMT`에 저장된다.
|
||||
|
||||
이 정정은 재구현 가능성에 크게 작용한다 — **단가의 네 성분이 프로젝트 파일 안에 평문으로 들어 있으므로 골든셋을 단가 수준까지 뽑을 수 있다.**
|
||||
|
||||
### `COSTA.DBF` — 별도 단가 자료
|
||||
|
||||
@@ -74,6 +76,25 @@ STini.MEM, COSTN.SMT, PRJLST.SMT
|
||||
|
||||
15필드. `CODE`, `NAME`, `BQCODE`, `QTY0..9`, `IFORM`, `XCODE`로 구성된다. 맛보기에는 레코드가 없으나 수량 계산식 또는 수량 슬롯을 코드화하는 자리로 보인다.
|
||||
|
||||
### `RATE*.DBF` — 프로젝트에 복사된 제비율 표 (6종)
|
||||
|
||||
맛보기 STC에는 **여섯 종**이 들어 있다. 기존 문서가 다루던 넷 말고 `RATE_A`·`RATE_B`가 더 있다.
|
||||
|
||||
| 테이블 | 형태 | 값이 든 행 | 대응 기준파일 | 비고 |
|
||||
|---|---|---:|---|---|
|
||||
| `RATE.DBF` | 41행 × 10열 | 37 | `JB_1` | |
|
||||
| `RATE_M.DBF` | 25행 × 10열 | 11 | `JB_M` | 행자부기준 간접노무비. 값이 `15.0`처럼 **백분율** |
|
||||
| `RATE_R.DBF` | 50행 × 35열 | 38 | `JB_R` 신형 | |
|
||||
| `RATE_RA.DBF` | 50행 × 20열 | 27 | `JB_RA` | |
|
||||
| **`RATE_A.DBF`** | 25행 × 10열 | 21 | **대응 JB 파일 없음** | 값이 `0.642 / 0.232 / 0.270 / 0.875 / 0.912`처럼 **0~1 계수**. 앞 5열만 씀 |
|
||||
| **`RATE_B.DBF`** | 35행 × 10열 | 35 | **대응 JB 파일 없음** | 같은 0~1 계수를 **10열 모두** 씀 |
|
||||
|
||||
`RATE_A`는 `RATE_M`과 형태가 같지만 **값의 성격이 다르다**(계수 vs 백분율). 전 행이 다르므로 같은 표가 아니다.
|
||||
|
||||
`RATE_B`의 10열은 RATE 편집 화면 `rGrd_RateB`의 숫자열 개수와 정확히 맞는다 — `재 / 직노 / 노 / 재+직노 / 재+노`가 토목·건축 두 열군으로 10개다. 따라서 **`RATE_B`는 수공형식(`S`)의 경비별 밑수 배분 계수표**로 보인다. **등급 B.**
|
||||
|
||||
⚠ **두 표는 맛보기 STC에만 있고 실무 STC 7건에는 없다.** 수공형식을 쓰지 않는 프로젝트에는 만들어지지 않는 것으로 보이며, 이는 **수공형식 재현 시험(T11)의 전제 자료가 실무 표본에 없다**는 뜻이다.
|
||||
|
||||
### `KONGJONG.DBF` — 공종 분류
|
||||
|
||||
`CODE`, `NAME`, `SIZE` 세 필드의 작은 분류표다.
|
||||
|
||||
@@ -89,7 +89,13 @@ price_candidate(material_id, revision_id, slot_no, source_name, page, observed_p
|
||||
price_adoption(material_id, revision_id, chosen_slot_no?, adopted_price, method, user_override)
|
||||
```
|
||||
|
||||
자동선정은 현재 C등급이다. 최저가·최신가·특정 슬롯 우선 중 어느 하나라고 단정할 근거가 없다. 동일 구현 전에는 후보 조합을 통제한 STmate 시험이 필수다: 6개 슬롯에 서로 다른 값, 동률, 0, 공란을 배치하고 채택값과 `PG_1` 변화를 기록한다.
|
||||
**2026-09-13 — 자동선정은 「0이 아닌 후보 중 최저가」로 확정됐다. A등급 실증.**
|
||||
|
||||
출력 XLSX의 `자재단가대비표`에 후보 다섯 칸과 적용 단가·출처가 나란히 있어 직접 검정했다. 실무 6건 전수에서 **후보 값이 2종 이상인 76건 전부, 3종 이상인 36건 전부 최저가**였고, 최저가가 아닌 후보를 택한 건은 0이다. 상세는 17번 §5.5.
|
||||
|
||||
`COSTN`의 여섯 칸 중 **여섯 번째가 적용 단가 칸**으로 보이며, 이는 「노무 L은 후보6과 채택가가 거의 일치」라는 위 관찰과 맞는다.
|
||||
|
||||
남은 것은 **사용자 수동 고정이 걸렸을 때의 우선순위**다. 표본에는 수동 고정 흔적이 없었다. 통제시험(13번 T10)에서 6개 슬롯에 서로 다른 값·동률·0·공란을 배치하고 수동 고정을 섞어 채택값과 `PG_1` 변화를 기록한다.
|
||||
|
||||
## 6. 반올림 경계
|
||||
|
||||
|
||||
@@ -35,7 +35,22 @@ STC에는 두 형태가 동시에 확인된다.
|
||||
1. 한 파일 안에 `BOQ11`, `BOQ12`를 함께 저장하는 통합형: 소광 표본은 각각 90행, 89행이다.
|
||||
2. 변경 차수를 별도 STC로 저장하면서 그 파일의 `BOQ11`을 현재안으로 쓰는 분리형: 소광 변경1차 표본은 115행이다.
|
||||
|
||||
또한 `COSTN`과 여러 필드의 접미사 1~4는 원설계와 최대 3개 변경차수의 값을 나란히 담을 수 있다. 따라서 “접미사 n=항상 BOQ1n”으로 기계적으로 가정하지 말고 프로젝트 메타데이터와 실제 존재 테이블을 함께 해석해야 한다.
|
||||
또한 `COSTN`과 여러 필드의 접미사 1~4는 원설계와 최대 3개 변경차수의 값을 나란히 담을 수 있다.
|
||||
|
||||
### 실증으로 확정된 차수 규칙 — A등급 (2026-09-13, 16번 §4.5·§4.6)
|
||||
|
||||
```text
|
||||
통합형 한 STC 안에 BOQ11(당초) + BOQ12(현재안)
|
||||
⚠ STmate 출력은 BOQ12 를 쓴다. BOQ11 을 현재안으로 잡으면 통째로 틀린다.
|
||||
|
||||
분리형 변경 차수를 새 STC 로 저장
|
||||
새 파일 BOQ11 차수1 = 앞 파일의 BOQ12 ← 소광 표본에서 원 단위 일치 확인
|
||||
새 파일 BOQ11 차수2 = 이번 변경분
|
||||
```
|
||||
|
||||
`PRJLST.TCOSTn = MCOSTn + LCOSTn + SCOSTn` 으로 **직접공사비 합계**다. 소광 변경1차는 당초 173,311,386 → 변경 257,410,433, 증감 +84,099,047이다.
|
||||
|
||||
따라서 “접미사 n=항상 BOQ1n”으로 기계적으로 가정하지 말고 **실제 존재하는 테이블과 `PRJLST` 를 함께 보고 현재안을 고를 것.**
|
||||
|
||||
### STmate의 실제 변경설계 계산모드 — A등급
|
||||
|
||||
@@ -67,7 +82,24 @@ STC에는 두 형태가 동시에 확인된다.
|
||||
- 비교표는 `original`, `previous`, `current`, `delta`를 모두 계산한다.
|
||||
- 삭제 항목은 물리 삭제하지 않고 현재 차수에 0/제외 상태로 남겨 코드 대응을 보존한다.
|
||||
- 신규 항목은 안정적인 내부 ID와 차수별 표시 순서를 가진다.
|
||||
- 간접비 항목마다 `recalculated`, `proportional_from_original`(방법 A), `delta_times_rate`(방법 B), `carry_original`, `manual`을 **계산모드 값으로 저장**한다. 절사 비선형성 때문에 모드가 다르면 원 단위 결과가 갈리므로, 모드를 하나로 고정하면 STmate 결과를 재현할 수 없다.
|
||||
- 간접비 항목마다 `recalculated`, `proportional_from_original`(방법 A), `delta_times_rate`(방법 B), `carry_original`, `manual`을 **계산모드 값으로 저장**한다.
|
||||
|
||||
### 실데이터로 확인된 모드 — A등급 (2026-09-13)
|
||||
|
||||
계류보전 2025(변경)의 `공사원가계산서`는 당초·변경·증감을 나란히 낸다. **직접노무비가 `32,976,276 → 41,778,438`(+8,802,162)로 늘었는데 세 항목은 증감이 0이다.**
|
||||
|
||||
| 항목 | 당초 | 변경 | 증감 | 재계산했다면 | 모드 |
|
||||
|---|---:|---:|---:|---:|---|
|
||||
| 간접노무비 | 4,715,607 | 5,974,316 | +1,258,709 | 5,974,316 ✅ | `recalculated` |
|
||||
| 산재보험료 | 1,341,831 | 1,699,998 | +358,167 | 1,699,998 ✅ | `recalculated` |
|
||||
| 고용보험료 | 380,688 | 482,302 | +101,614 | 482,302 ✅ | `recalculated` |
|
||||
| **건강보험료** | 1,328,500 | 1,328,500 | **0** | 1,683,108 | **`carry_original`** |
|
||||
| **노인장기요양보험료** | 172,040 | 172,040 | **0** | 217,961 | **`carry_original`** |
|
||||
| **연금보험료** | 1,686,389 | 1,686,389 | **0** | 2,136,526 | **`carry_original`** |
|
||||
|
||||
요율은 당초·변경이 같다(간접노무비 14.300%, 산재 3.560%, 고용 1.010%). 요율을 바꾼 것이 아니라 **항목마다 모드가 다르다.** 건강·장기요양·연금은 **사후정산 대상**이라 설계 단계에서 당초액을 그대로 가져간다 — §10의 「직접입력」 옵션이 이것이다.
|
||||
|
||||
⚠ **전부 재계산으로 두면 이 세 항목에서 85만 원이 어긋난다.** 모드를 하나로 고정하면 STmate 결과를 재현할 수 없다.
|
||||
- Aislo 자체 기본값으로는 전 차수 독립 재계산(`recalculated`)을 권장하되, **STmate 호환 모드와 구분해서** 둔다.
|
||||
|
||||
## 3. 일괄 단가변경·물가변동
|
||||
|
||||
@@ -60,6 +60,19 @@ CODE="W" 또는 복합행 → TAMT=MAMT+LAMT+SAMT
|
||||
|
||||
따라서 총액을 네 번째 독립 비목으로 저장하거나 계산하면 안 된다. 총액은 세 성분의 합이며, 단일 비목행에서는 해당 성분만 총액과 같고 나머지는 0이다. **등급 A**.
|
||||
|
||||
#### ⚠ 실데이터에는 예외가 있다 (2026-09-13 실측)
|
||||
|
||||
맛보기 STC의 `COSTN.DBF` 71행 중 값이 있는 59행을 대조하면 **57행은 맞고 2행이 어긋난다.**
|
||||
|
||||
| CODE | 명칭 | 규격 | `TAMT1` | `MAMT1+LAMT1+SAMT1` | 차이 |
|
||||
|---|---|---|---:|---:|---:|
|
||||
| `M00010` | 흄관 | 800mm | 65,880 | 41,608 | **+24,272** |
|
||||
| `M00011` | 흄관 | 900mm | 83,920 | 53,784 | **+30,136** |
|
||||
|
||||
둘 다 `CODE`가 `M`(자재)이고 흄관 — 관급자재로 다루는 품목이다. 즉 **바이너리의 조건식은 「항상 참인 불변조건」이 아니라 「어긋나면 경고할 검사식」**이며, 관급·비과세처럼 별도로 취급하는 자재에는 예외가 있다.
|
||||
|
||||
**재구현 지침** — 이 관계를 **하드 오류로 막으면 실데이터를 거부하게 된다.** 경고로 두고 `component_sum_exempt` 같은 사유 필드를 함께 저장할 것. 예외의 정확한 조건은 통제시험 **T7**(13번 문서) 대상이다.
|
||||
|
||||
## 4. 공통 계산 함수의 의미
|
||||
|
||||
### 4.1 식 평가
|
||||
|
||||
@@ -254,7 +254,32 @@ DBF 필드는 `RATE0..RATE9` 또는 `RATE_01..RATE_35`, 레코드는 고정 25/4
|
||||
- 2020-05-27 확장으로 **열 21 = 국가유산 간접노무비, 열 26 = 국가유산 기타경비**가 붙었고 기존 1~20열은 한 셀도 바뀌지 않았다.
|
||||
- `JB_RA`는 43개 전 기간 20열 그대로다. 실적형식에는 국가유산 공종이 없다.
|
||||
|
||||
**남은 것** — 각 부속표 그리드(고용보험·환경보전·보증수수료 계열)가 `JB_1`/`JB_R`의 몇 번째 행부터 적재되는지. 열 접근이 실행 중 문자열 조립이라 코드 상수로 읽을 수 없으므로, 저장 이벤트 역추적 또는 통제 동적 시험이 필요하다.
|
||||
### 9.1 확정된 좌표 (2026-09-13, 15번 문서)
|
||||
|
||||
출력 XLSX 「공사원가계산서」 비고 칸에 **실제 쓰인 밑수와 요율이 적혀 있다.** 그 값을 같은 프로젝트의 `RATE_*.DBF`에서 되찾아 좌표를 역추적하고, 실무 7건 교차로 후보가 하나뿐인 자리만 취했다.
|
||||
|
||||
| 항목 | 좌표 | 근거 |
|
||||
|---|---|---|
|
||||
| 간접노무비 | `RATE_R[10, 1]` | 2건 유일 |
|
||||
| 산재보험료 | `RATE_R[45, 1]` | 3건 유일 |
|
||||
| 건강보험료 | `RATE_R[46, 1]` | 2건 유일 |
|
||||
| 연금보험료 | `RATE_R[46, 6]` | 시계열 |
|
||||
| 노인장기요양보험료 | `RATE_R[46, 11]` | 1건 유일 + 시계열 |
|
||||
| 고용보험료 등급 1~7 | `RATE_R[45, 11..17]` | 4건 유일 + 시계열 |
|
||||
| 산업안전보건관리비 | `RATE_R[20, 17]` 율 · `[20, 18]` 기초액 | 3건 유일 |
|
||||
|
||||
**시계열이 실제 법정요율 변천과 일치해 A등급이다.**
|
||||
|
||||
| 좌표 | 2019 | 2022 | 2024 | 2025 | 2026 |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| `[45,1]` 산재 | 3.75 | 3.70 | 3.56 | 3.56 | 3.56 |
|
||||
| `[46,1]` 건강 | 3.23 | 3.495 | 3.545 | 3.545 | 3.595 |
|
||||
| `[46,6]` 연금 | 4.5 | 4.5 | 4.5 | 4.5 | 4.75 |
|
||||
| `[46,11]` 장기요양 | 8.51 | 12.27 | 12.95 | 12.95 | 13.14 |
|
||||
|
||||
⇒ **`RATE_R` 행 45~49는 요율 행렬이 아니라 부속표 영역**이고, 행 0~44가 규모·기간 행렬이다. `[45,11..17]`이 `rGrd_Rate_Kobo`(7행, 등급·요율) 그대로다.
|
||||
|
||||
**남은 것** — `연금보험료`·`일반관리비`·`이윤`·`환경보전비`·`부가가치세`는 같은 값이 표 안 여러 곳에 있어 단독으로는 못 가른다. 그 값만 특이한 숫자로 바꿔 저장하는 통제시험(13번 T1)이 필요하다. 부속표 그리드(환경보전·보증수수료 계열)의 시작 행도 같은 시험 대상이다.
|
||||
|
||||
## 10. 계산 설정 입력
|
||||
|
||||
|
||||
@@ -2,8 +2,31 @@
|
||||
|
||||
> 정적 분석만으로는 확정할 수 없는 경계를 **시험 단위로 갈라 놓은 유일한 목록**이다. 05~12번 문서에 흩어져 있던 "미확정·C등급" 항목을 여기로 모았다. 각 문서는 이제 이 목록을 가리킨다.
|
||||
|
||||
기준일: 2026-09-13
|
||||
대상: `C:\경영정보(주)` — **원본은 읽기만 한다.** 시험은 프로젝트 **복제본**에서 한다.
|
||||
기준일: 2026-09-13 (전제 갱신 2026-09-13)
|
||||
대상: `C:\경영정보(주)` — **원본은 읽기만 한다.**
|
||||
|
||||
## ⛔ 전제 정정 — 이 시험들은 **실행할 수 없다**
|
||||
|
||||
**STmate 라이선스가 없어 프로그램을 띄울 수 없다.** 따라서 아래 T1~T11은 **수행 불가**이며, 「나중에 시험으로 확정한다」는 계획은 성립하지 않는다.
|
||||
|
||||
이 목록은 버리지 않고 **두 가지 용도로 남긴다.**
|
||||
|
||||
1. **무엇이 왜 확정 불가인지**를 항목별로 밝혀 둔 장부 — 어떤 값이 「모르는 것」인지 분명히 해 두어야 Aislo가 그 자리를 정책 플래그로 열어 둘 수 있다.
|
||||
2. **라이선스가 생기거나 실무자가 대신 눌러 줄 수 있게 되면** 그대로 쓸 절차서.
|
||||
|
||||
**대신 쓸 수 있는 길**은 세 갈래이며, 이미 상당 부분 성과가 났다.
|
||||
|
||||
| 대체 경로 | 무엇을 풀었나 |
|
||||
|---|---|
|
||||
| **출력 XLSX 역추적** — 비고 칸에 밑수·요율이 적혀 있음 | 보험 요율 좌표 6개, 이윤 좌표 (15번 §4) |
|
||||
| **골든셋 회귀** — 내역으로 원가계산을 다시 세워 대조 | 원가계산 전 과정, 끝전조정, 차수 규칙 (16번) |
|
||||
| **출력 시트 전수 검정** — 일위대가·중기·자재단가대비표 | 성분 절사, 중기 손료율, 자재 채택=최저가 (17번) |
|
||||
|
||||
**표본을 늘리는 것이 통제시험을 대신하는 유일한 확장 수단이다.** 프로젝트마다 공사규모·기간·공종이 다르므로, 표본이 늘수록 좌표와 조건이 저절로 좁혀진다.
|
||||
|
||||
---
|
||||
|
||||
아래는 라이선스가 생겼을 때를 위한 원 절차서다. 시험은 프로젝트 **복제본**에서 한다.
|
||||
|
||||
## 0. 왜 시험이 필요한가
|
||||
|
||||
@@ -28,7 +51,19 @@
|
||||
| 확정되는 것 | 그리드 → 행렬 좌표의 완전 매핑. `rate_standard()` 기준표를 하드코딩 없이 만들 수 있게 됨 |
|
||||
| 근거 문서 | 12번 §8·§9, 10번 §4 |
|
||||
|
||||
이미 잡아 둔 것 — 행은 `0,5,10,…,45` 시작의 **5행 묶음**, `JB_R` 열 1~5=간접노무비·6~10=기타경비(토목/산설(토목)/조경/건축/산설(건축)), 신형 열 21·26=국가유산. **T1은 나머지 부속표의 시작 행만 남긴 시험이다.**
|
||||
**2026-09-13 — T1 의 절반이 정적으로 풀렸다.** 출력 XLSX 비고 칸의 실제 요율을 프로젝트 `RATE_*`에서 역추적해 다음이 확정됐다(15번·12번 §9.1).
|
||||
|
||||
```text
|
||||
간접노무비 RATE_R[10, 1]
|
||||
산재보험료 RATE_R[45, 1] 건강보험료 RATE_R[46, 1]
|
||||
연금보험료 RATE_R[46, 6] 장기요양 RATE_R[46, 11]
|
||||
고용보험 등급1~7 RATE_R[45, 11..17]
|
||||
산업안전보건관리비 RATE_R[20, 17] 율 · [20, 18] 기초액
|
||||
```
|
||||
|
||||
시계열이 실제 법정요율 변천과 맞아 **A등급**이다. 행 45~49가 부속표 영역, 행 0~44가 규모·기간 행렬임도 갈렸다.
|
||||
|
||||
**T1 에 남은 것** — `연금보험료`·`일반관리비`·`이윤`·`환경보전비`·`부가가치세`처럼 **같은 값이 표 안 여러 곳에 있는 항목**과, 환경보전·보증수수료 부속표의 시작 행. 이것들은 그 칸만 특이한 값(예: `99.99`)으로 바꿔 저장해 어디에 앉는지 보는 수밖에 없다.
|
||||
|
||||
### T2 — 금액·기간 구간 경계 방향
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
# 재구현 가능성 검증 — 모인 정보로 같은 프로그램을 만들 수 있나
|
||||
|
||||
검증일: 2026-09-13
|
||||
방법: 05~14번 문서의 주장을 믿지 않고, **구현에 꼭 필요한 것을 층별로 세어 원본 파일과 직접 대조**함.
|
||||
대상: `C:\경영정보(주)` 설치 폴더 186개 + 실무 STC 8건 + STmate 생성 XLSX 29건. **읽기만 함.**
|
||||
|
||||
## 1. 한 줄 답
|
||||
|
||||
**계산 엔진은 만들 수 있음. 기준자료를 갖춘 완제품은 못 만듦.**
|
||||
|
||||
STmate가 배포하는 **기초단가 원천이 전부 암호화**되어 있어 자재·노임·중기 단가를 한 건도 못 읽음. 반면 **프로젝트 파일(STC) 안에는 단가 결과가 평문으로 들어 있어** 「이 단가를 넣으면 이 내역·이 간접비가 나온다」는 재현은 가능함.
|
||||
|
||||
Aislo 입장에서는 이것으로 충분함 — Aislo는 **자기 기초단가 원천**(노임·자재·유가·환율)을 이미 따로 갖고 있으므로, STmate에서 가져올 것은 **계산 규칙**이지 기초자료가 아님.
|
||||
|
||||
## 2. 층별 확보 상태
|
||||
|
||||
| 층 | 구현에 필요한 것 | 확보 | 판정 |
|
||||
|---|---|---|---|
|
||||
| **1 파일 읽기** | STC = 무암호 ZIP | 8건 모두 암호 없음 | ✅ |
|
||||
| | 프로젝트 표 18종 스키마·본문 | 16종 평문 | ✅ |
|
||||
| | `BDQTY.DBF` (수량·할증 상세) | **본문 암호화** | ❌ |
|
||||
| | `SYSINFO.DBF` | **본문 암호화** | ❌ |
|
||||
| **2 기초단가 원천** | `JDCODE_M.BSC` 자재 57,580건 | **본문 암호화** | ❌ **치명** |
|
||||
| | `JDCODE_L.BSC` 노임 460건 | **본문 암호화** | ❌ |
|
||||
| | `JDCODE_X.BSC` 중기 833건 | **본문 암호화** | ❌ |
|
||||
| | `JDCODE_Z.BSC` 33,927건 · `GRPCOD_Z` 12,125건 | **본문 암호화** | ❌ |
|
||||
| | 표준시장단가 `*.CSTA` 4,921~4,961건 × 6 | **본문 암호화** | ❌ |
|
||||
| | `*.STBSC` 28개 (노임·기계경비 배포) | ZIP 컨테이너, 내부 암호 | ❌ |
|
||||
| **3 단가 계산** | `COSTN.DBF` 단가 네 성분 | **평문 — 읽힘** | ✅ |
|
||||
| | 노임 일→시간 환산식 | **`환율및기초자료` 시트 + `RXNM_` 설정** (18번) | ✅ |
|
||||
| | 단가산출 `D`코드 Q식·품셈 인자 | **`단가산출근거` 시트 3,889행** (18번) | ✅ |
|
||||
| | 계산 설정 스냅샷 | **`〓 INITIAL 〓` 시트** (18번) | ✅ |
|
||||
| | `COSTN.SMT` 구성 토큰 | 평문, 문법 문서화됨 | ✅ |
|
||||
| | 중기 시간당 손료율·연료·잡품률 | **출력 `중기사용료` 시트에 전부 있음** (17번) | ✅ |
|
||||
| | 자재 6후보 중 자동채택 규칙 | C등급 | ❌ |
|
||||
| **4 내역** | `BOQ11.DBF` 수량·단가·금액 | 평문 | ✅ |
|
||||
| | 계층 집계(`SORTCODE`·`ENDCODE`·`ITM_TYP`) | 문서화됨 | ✅ |
|
||||
| **5 간접비** | 항목 사전 30여 개 | 문서화됨 | ✅ |
|
||||
| | 형식별 계산 순서 | 네 형식 모두 번호 매겨 복원 | ✅ |
|
||||
| | 항목별 밑수 공식 | 화면 묶음 제목에서 확정 | ✅ |
|
||||
| | 요율표 좌표 | **6개 항목 확정** (아래 4장), 나머지 진행 중 | ⚠→✅ |
|
||||
| | `RATE_A`·`RATE_B` 표 | 이번에 발견, 의미 B등급 | ⚠ |
|
||||
| **6 반올림** | 내부 함수·음수·절반값 | 은행가 반올림 확정 | ✅ |
|
||||
| | 출력별 정책 | 확정 | ✅ |
|
||||
| **7 출력** | `SET_REP.DBF` 75행 × 28열 | 평문 | ✅ |
|
||||
|
||||
## 3. 이번 검증에서 나온 문서 오류 셋
|
||||
|
||||
기존 문서가 실제와 어긋난 자리를 찾아 고침.
|
||||
|
||||
### ① `COSTN.DBF`는 암호화돼 있지 않음
|
||||
|
||||
02번이 「본문은 암호화되어 있으나 스키마는 평문」이라 적었으나 **사실이 아님.** 맛보기 71행, 봉화 248행을 그대로 읽음.
|
||||
|
||||
```text
|
||||
B00001 흄관기초 D=800mm M TAMT1=23,441 MAMT1=2,719 LAMT1=20,722 SAMT1=0
|
||||
```
|
||||
|
||||
**이 정정이 결론을 바꿈** — 단가의 네 성분이 프로젝트 안에 평문으로 있으므로 **골든셋을 단가 수준까지 뽑을 수 있음.** 암호화된 것은 `BDQTY`와 `SYSINFO` 둘뿐임.
|
||||
|
||||
### ② `TAMT = MAMT + LAMT + SAMT` 는 예외가 있음
|
||||
|
||||
A등급 불변조건으로 적혀 있으나 실데이터에 **59행 중 2행이 어긋남.**
|
||||
|
||||
| CODE | 명칭 | `TAMT1` | `M+L+S` | 차이 |
|
||||
|---|---|---:|---:|---:|
|
||||
| `M00010` | 흄관 800mm | 65,880 | 41,608 | +24,272 |
|
||||
| `M00011` | 흄관 900mm | 83,920 | 53,784 | +30,136 |
|
||||
|
||||
둘 다 관급으로 다루는 자재임. **하드 오류로 막으면 실데이터를 거부하게 됨** — 경고로 두고 사유를 함께 저장할 것.
|
||||
|
||||
### ③ 프로젝트 RATE 표는 넷이 아니라 여섯
|
||||
|
||||
맛보기 STC에 **`RATE_A`(25×10)·`RATE_B`(35×10)** 가 더 있음. 값이 `0.642 / 0.232 / 0.875` 같은 **0~1 계수**로, 백분율인 다른 표와 성격이 다름. `RATE_B`의 10열은 화면 `rGrd_RateB`의 숫자열 개수(`재/직노/노/재+직노/재+노` × 토목·건축)와 정확히 맞아 **수공형식 경비 밑수 배분표**로 보임.
|
||||
|
||||
⚠ **두 표는 맛보기에만 있고 실무 STC 7건에는 없음** — 수공형식 재현 시험의 전제 자료가 표본에 없다는 뜻임.
|
||||
|
||||
## 4. T1(요율 좌표)을 정적으로 깬 결과
|
||||
|
||||
**출력 엑셀 「공사원가계산서」의 비고 칸에 쓰인 밑수와 요율이 그대로 적혀 있음.**
|
||||
|
||||
```text
|
||||
간접노무비 직접노무비 x 14.5%
|
||||
산재보험료 (직접노무비+간접노무비) x 3.56%
|
||||
노인장기요양보험료 건강보험료 x 12.95%
|
||||
이윤 (노무비+경비+일반관리비) x 15%
|
||||
```
|
||||
|
||||
이 값을 그 프로젝트의 `RATE_*.DBF` 안에서 되찾아 좌표를 역추적함. 실무 7건 전수 교차로 **후보가 하나뿐인 자리**만 취함.
|
||||
|
||||
| 항목 | 확정 좌표 | 근거 |
|
||||
|---|---|---|
|
||||
| 간접노무비 | `RATE_R[10, 1]` | 2건에서 유일 |
|
||||
| 산재보험료 | `RATE_R[45, 1]` | 3건에서 유일 |
|
||||
| 건강보험료 | `RATE_R[46, 1]` | 2건에서 유일 |
|
||||
| 고용보험료 | `RATE_R[45, 17]` | 4건에서 유일 |
|
||||
| 노인장기요양보험료 | `RATE_R[46, 11]` | 1건에서 유일 |
|
||||
| 산업안전보건관리비 | `RATE_R[20, 17]` (율), `[20, 18]` (기초액) | 3건에서 유일 |
|
||||
|
||||
### 시계열 교차 검증 — A등급
|
||||
|
||||
기준파일 `JB_R`의 같은 자리를 연도별로 늘어놓으면 **실제 법정요율 변천과 정확히 일치함.**
|
||||
|
||||
| 좌표 | 2019 | 2022 | 2024 | 2025 | 2026 | 무엇인가 |
|
||||
|---|---:|---:|---:|---:|---:|---|
|
||||
| `[45, 1]` | 3.75 | 3.70 | 3.56 | 3.56 | 3.56 | 산재보험료율 |
|
||||
| `[46, 1]` | 3.23 | 3.495 | 3.545 | 3.545 | 3.595 | 건강보험료율 |
|
||||
| `[46, 6]` | 4.5 | 4.5 | 4.5 | 4.5 | 4.75 | 연금보험료율 |
|
||||
| `[46, 11]` | 8.51 | 12.27 | 12.95 | 12.95 | 13.14 | 노인장기요양보험료율 |
|
||||
|
||||
`[45, 11..17]`은 **고용보험 등급 1~7**임. 값이 내림차순이고(2024: `1.57 / 1.30 / 1.13 / 1.06 / 1.03 / 1.02 / 1.01`) 개수가 화면 `rGrd_Rate_Kobo`의 7행과 맞음. 검증한 프로젝트는 모두 `열17`(7등급 이하)을 씀.
|
||||
|
||||
⇒ **`RATE_R`의 행 45~49는 요율 행렬이 아니라 부속표 영역**이고, 행 0~44가 규모·기간 행렬임.
|
||||
|
||||
### 항목이 두 부류로 갈림 — 통제시험 없이 밝힌 것
|
||||
|
||||
프로젝트 5건의 요율을 각자의 `RATE_*` 표에 겹쳐 보면 항목이 두 갈래로 나뉨.
|
||||
|
||||
**① 고정 칸** — 공사규모·기간과 무관. 전 프로젝트에서 같은 좌표.
|
||||
|
||||
```text
|
||||
산재보험료 RATE_R[45, 1] 건강보험료 RATE_R[46, 1]
|
||||
연금보험료 RATE_R[46, 6] 장기요양 RATE_R[46, 11]
|
||||
고용보험 등급1~7 RATE_R[45, 11..17]
|
||||
산업안전보건관리비 RATE_R[20, 17] 율 · [20, 18] 기초액 ← 3건 전부 유일
|
||||
이윤 RATE_R[20, 16] ← 3건 전부 일치
|
||||
```
|
||||
|
||||
**② 규모·기간에 따라 칸이 움직임** — 간접노무비·기타경비·일반관리비. 전 프로젝트에 공통인 좌표가 **없음.** 행이 `(공사규모 밴드, 공사기간 밴드)`로 정해지므로 고정 좌표라는 모델 자체가 틀림. 열은 공종(토목·산설·조경·건축·산설건축)이고 행이 움직인다.
|
||||
|
||||
### ⚠ 요율이 표에 아예 없는 경우가 실재함
|
||||
|
||||
| 프로젝트 | 항목 | 쓴 요율 | 그 프로젝트 `RATE_R` 안에 |
|
||||
|---|---|---:|---|
|
||||
| 2024 간선임도 (기번3) | 간접노무비 | 14.5% | **없음** |
|
||||
| 2025 소광 (기번8) | 이윤 | 13.435% | **없음** |
|
||||
|
||||
즉 **사용자가 표를 거치지 않고 요율을 직접 써 넣은 것**이다. 11번 §8이 「순환이 생기면 사용자 수동 확정으로 넘긴다」고 한 경로가 실데이터에 그대로 나타났다.
|
||||
|
||||
**재구현 필수 조건** — 요율을 표에서 못 찾았다고 오류로 막으면 안 된다. `rate_source ∈ {table(coord), manual}` 을 항상 함께 저장하고, 수동값이면 좌표 없이 값만 보존해야 한다.
|
||||
|
||||
## 5. 그래서 무엇을 만들 수 있나
|
||||
|
||||
### 지금 자료로 가능한 것
|
||||
|
||||
1. **STC 읽기·쓰기 어댑터** — 표 16종 평문, ZIP 무암호
|
||||
2. **내역 계산기** — 수량 × 단가 → 행 금액 → 계층 집계. 반올림 정책 확정
|
||||
3. **간접비 계산기** — 항목 사전·형식별 순서·밑수·반올림 확정, 보험 요율 좌표 6개 확정
|
||||
4. **원설계 골든셋 회귀** — 실무 7건의 `BOQ11` + `COSTN` + `RATE_*` + 출력 XLSX를 짝지어 **원 단위 대조** 가능
|
||||
5. **변경설계 비교** — 소광 원설계/통합변경/변경1차 세 벌 보유
|
||||
|
||||
### 지금 자료로 불가능한 것
|
||||
|
||||
1. **기초단가 배포 체계** — 자재 5.7만 건·노임·중기·표준시장단가를 못 읽음. 암호를 풀지 않는 한 영구 불가
|
||||
2. **수량 할증 재현** — `BDQTY` 암호화로 `X_QTY`·`X_HAL` 실값을 못 봄. 출력에는 **할증 후 수량만** 남음
|
||||
3. ~~중기 시간당 단가 재현~~ → **가능해짐** (17번). 출력 `중기사용료` 호표에 손료율·취득가·연료·잡품률이 다 들어 있음
|
||||
4. **자재 자동채택** — 6후보 중 무엇을 고르는지 규칙 없음
|
||||
5. **수공형식 검증** — `RATE_A`·`RATE_B`가 실무 표본에 없음
|
||||
|
||||
## 6. 판정
|
||||
|
||||
| 물음 | 답 |
|
||||
|---|---|
|
||||
| STmate와 **똑같은 프로그램**을 만들 수 있나 | ❌ — 기초단가 원천이 암호화됨 |
|
||||
| STmate와 **같은 답을 내는 계산 엔진**을 만들 수 있나 | ✅ **원가계산 부분은 실증 완료** (16번, 프로젝트 6건 원 단위 일치). 단가 조립·할증·자재채택은 여전히 불가 |
|
||||
| STmate 프로젝트 파일을 **읽고 검산**할 수 있나 | ✅ — `BDQTY` 빼고 전부 |
|
||||
| **Aislo 원가계산에 쓸 규칙**을 얻었나 | ✅ — 밑수·순서·반올림·요율 좌표 확보 |
|
||||
|
||||
**Aislo 목적으로는 필요한 정보가 모였음.** 「STmate 복제」는 애초에 불가능하고 목표도 아님.
|
||||
|
||||
### ⛔ 통제시험은 못 함 — 라이선스 없음
|
||||
|
||||
13번의 T1~T11은 **STmate를 띄워야 하는데 라이선스가 없어 수행 불가**다. 따라서 「나중에 시험으로 확정」이라 미뤄 둔 항목은 **다른 길로 풀거나, 모르는 채로 두고 Aislo에서 정책으로 여는 수밖에 없다.**
|
||||
|
||||
실제로 대체 경로가 잘 들었다 — 출력 XLSX 역추적·골든셋 회귀·출력 시트 전수 검정으로 09번이 꼽은 여섯 차단점 중 **둘을 완전히, 하나를 절반** 풀었다(17번 §7).
|
||||
|
||||
**남은 것을 늘릴 방법은 표본을 늘리는 것 하나뿐이다.** 공사규모·기간·공종·계산형식이 다른 프로젝트가 들어올수록 좌표와 조건이 저절로 좁혀진다. 특히 **수공형식(`S`)·실적형식(`TA`/`SA`) 표본이 하나도 없어** 그 세 형식은 지금 전혀 검증되지 않았다.
|
||||
|
||||
## 7. 다음에 할 것
|
||||
|
||||
| 순위 | 할 일 | 왜 |
|
||||
|---:|---|---|
|
||||
| ~~1~~ | ~~골든셋 회귀 구현~~ | ✅ **완료 — 16번.** 프로젝트 6건 전 항목 원 단위 일치 |
|
||||
| 2 | 남은 요율 좌표(연금·일반관리비·이윤·환경보전비) 확정 | 통제시험 T1 나머지 |
|
||||
| 3 | 중기 손료 계수를 **법령·품셈과 대조** | 값은 17번에서 확보했으나, Aislo 기본값은 현행 기준에서 가져와야 함 |
|
||||
| 4 | `BDQTY` 대조시험 (T7) | 할증 규칙 — 여전히 막힌 유일한 입력 |
|
||||
|
||||
3번의 값 자체는 17번 §4에 25개 장비분이 있음. 다만 **Aislo 기본값은 STmate 값이 아니라 현행 「건설기계 경비 산정 기준」에서** 확정해야 하며, 17번 표는 대조용임.
|
||||
@@ -0,0 +1,167 @@
|
||||
# 골든셋 회귀 결과 — 원가계산을 원 단위로 다시 세워 맞췄다
|
||||
|
||||
실행일: 2026-09-13
|
||||
방법: 프로젝트 STC의 내역 표(`BOQ11`/`BOQ12`)만 입력으로 삼아 **직접비 → 간접비 → 이윤 → 끝전조정 → 총공사비**를 처음부터 다시 계산하고, STmate가 뽑은 XLSX 「공사원가계산서」와 **원 단위로 대조**함.
|
||||
재현 스크립트: `40_스크립트/golden_regression.py`
|
||||
|
||||
## 1. 결과 한 줄
|
||||
|
||||
**실무 프로젝트 6건 전부, 모든 항목이 1원도 틀리지 않고 맞았다.**
|
||||
|
||||
09번 문서가 정한 「완료 정의」 중 **원설계 재현 조건이 실증으로 충족**됐다. 이제 규칙은 문서상의 주장이 아니라 **돌아가는 계산**이다.
|
||||
|
||||
## 2. 전수 결과
|
||||
|
||||
| 프로젝트 | 말단 행 | 행 금액 대조 | 직접재료비 | 직접노무비 | 산출경비 | 간접비 |
|
||||
|---|---:|---|:--:|:--:|:--:|:--:|
|
||||
| 봉화 2024 (기번41 현동) | 72 | 216건 일치 | ✅ | ✅ | ✅ | ✅ |
|
||||
| 영월 2024 (기번6) | 96 | 288건 일치 | ✅ | ✅ | ✅ | ✅ |
|
||||
| 울진 대흥 2024 (기번3) | 129 | 387건 일치 | ✅ | ✅ | ✅ | ✅ |
|
||||
| 영덕 계류보전 2025 (변경) | 98 | 294건 일치 | ✅ | ✅ | ✅ | — |
|
||||
| 소광 2025 원설계 (기번8) | 43 | 129건 일치 | ✅ | ✅ | ✅ | ✅ |
|
||||
| 소광 2025 변경 1차 (기번8) | 67 | 201건 일치 | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
**행 금액 `ROUNDDOWN(수량 × 단가)` 대조 1,515건 중 불일치 0.**
|
||||
|
||||
## 3. 봉화 2024 — 처음부터 끝까지
|
||||
|
||||
| 항목 | 다시 계산한 값 | STmate 출력 | |
|
||||
|---|---:|---:|:--:|
|
||||
| 직접재료비 | 191,867,113 | 191,867,113 | ✅ |
|
||||
| 직접노무비 | 426,802,539 | 426,802,539 | ✅ |
|
||||
| 산출경비 | 162,613,763 | 162,613,763 | ✅ |
|
||||
| 간접노무비 | 61,886,368 | 61,886,368 | ✅ |
|
||||
| 산재보험료 | 17,397,325 | 17,397,325 | ✅ |
|
||||
| 고용보험료 | 4,935,757 | 4,935,757 | ✅ |
|
||||
| 건강보험료 | 15,130,150 | 15,130,150 | ✅ |
|
||||
| 노인장기요양보험료 | 1,959,354 | 1,959,354 | ✅ |
|
||||
| 연금보험료 | 19,206,114 | 19,206,114 | ✅ |
|
||||
| 산업안전보건관리비 | 19,270,590 | 19,270,590 | ✅ |
|
||||
| 기타경비 | 40,833,361 | 40,833,361 | ✅ |
|
||||
| 환경보전비 | 6,250,267 | 6,250,267 | ✅ |
|
||||
| 건설기계대여금 지급보증 | 3,125,133 | 3,125,133 | ✅ |
|
||||
| 경비 소계 | 290,721,814 | 290,721,814 | ✅ |
|
||||
| 일반관리비 | 58,276,670 | 58,276,670 | ✅ |
|
||||
| **이윤 (끝전보정 후)** | **125,652,769** | **125,652,769** | ✅ |
|
||||
| 총원가 | 1,155,207,273 | 1,155,207,273 | ✅ |
|
||||
| 부가가치세 | 115,520,727 | 115,520,727 | ✅ |
|
||||
| 도급공사비 | 1,270,728,000 | 1,270,728,000 | ✅ |
|
||||
| 관급자재대 | 143,555,000 | 143,555,000 | ✅ |
|
||||
| **총공사비** | **1,414,283,000** | **1,414,283,000** | ✅ |
|
||||
|
||||
## 4. 이번에 새로 밝혀진 규칙 일곱
|
||||
|
||||
문서에 없던 것들이다. 이것이 없으면 위 표가 맞지 않는다.
|
||||
|
||||
### 4.1 말단 행 판별 — `BDCODE1` 접두가 구조를 말한다
|
||||
|
||||
`BOQ11`은 말단 공종과 계층 제목·계를 한 테이블에 섞어 담는다. 구분은 `BDCODE1`의 접두다.
|
||||
|
||||
```text
|
||||
T1~T5 계층 제목 행 (그 구역 소계를 담고 있음)
|
||||
E1~E5 계(End) 행
|
||||
C1~C5 제외금액 총계 행
|
||||
: 페이지 넘김
|
||||
그 외 실제 단가 코드 (D00021, M00053, B00012 …) → 말단 행
|
||||
```
|
||||
|
||||
제목 행과 계 행이 **소계를 그대로 담고 있으므로** 전체를 더하면 9배 가까이 부풀어 오른다.
|
||||
|
||||
### 4.2 `C*` 로 닫히는 구역은 제외금액 구역
|
||||
|
||||
`T4 관급자재대` 로 열리고 `C4 총계(제외금액)` 로 닫히는 구역 안의 행은 **직접비에 넣지 않는다.** 봉화의 경우 이 구역이 143,554,309원이고, 빼지 않으면 직접재료비가 정확히 그만큼씩 두 번 더해진다(구역 안 품목 행 + `C4` 총계 행).
|
||||
|
||||
구역 경계는 `C*` 행의 `ENDCODE`가 여는 `T*` 행의 `SORTCODE`를 가리키는 것으로 찾는다.
|
||||
|
||||
### 4.3 산업안전보건관리비의 관급재 밑수 = 관급자재대 − 조달수수료
|
||||
|
||||
화면 수식은 `(직노+직재+간재+관급재/1.1) × 율 + 기초액`인데, 여기서 **관급재는 조달수수료를 뺀 원자재대**다.
|
||||
|
||||
| 무엇을 관급재로 넣나 | 율 부분 | 판정 |
|
||||
|---|---:|---|
|
||||
| 관급자재대 143,554,309 그대로 | 13,934,628 | ✗ +13,038 |
|
||||
| **관급자재대 − 조달수수료 771,029 = 142,783,280** | **13,921,590** | **✅** |
|
||||
|
||||
### 4.4 끝전조정 — 차액을 세전으로 환산해 이윤에서 뺀다
|
||||
|
||||
봉화에서 보정 전후는 이렇다.
|
||||
|
||||
```text
|
||||
보정 전 이윤 125,653,108
|
||||
보정 전 총원가 1,155,207,612
|
||||
보정 전 부가세 115,520,761
|
||||
보정 전 도급공사비 1,270,728,373 ← 천원 단위가 아님
|
||||
|
||||
차액 = 1,270,728,373 − 1,270,728,000 = 373
|
||||
세전조정 = ROUND(373 / 1.1) = 339 ← 사사오입
|
||||
이윤 = 125,653,108 − 339 = 125,652,769 ✅
|
||||
총원가 = 1,155,207,273 ✅
|
||||
부가세 = ROUNDDOWN(총원가 × 10%) = 115,520,727 ✅
|
||||
도급 = 1,270,728,000 ✅
|
||||
```
|
||||
|
||||
**06번 §4가 세운 모델(`차액 → 세전 상당액 → 이윤·VAT 배분`)이 실데이터로 확인됐다.** 재구현은 `raw_amount / rounded_amount / adjustment_amount`를 반드시 분리해 저장해야 한다.
|
||||
|
||||
### 4.5 한 파일에 `BOQ11`·`BOQ12`가 같이 있으면 **`BOQ12`가 현재안**
|
||||
|
||||
소광 원설계 STC는 두 표를 함께 담는다.
|
||||
|
||||
| 표 | 행 | 말단 | 재료비 | 노무비 | 경비 | 출력과 |
|
||||
|---|---:|---:|---:|---:|---:|:--:|
|
||||
| `BOQ11` | 90 | 43 | 54,699,532 | 93,741,594 | 48,693,912 | ✗ |
|
||||
| `BOQ12` | 89 | 43 | 47,987,107 | 82,246,518 | 43,077,761 | **✅** |
|
||||
|
||||
**STmate가 뽑은 XLSX는 `BOQ12`와 맞는다.** `BOQ11`을 무조건 현재안으로 잡으면 이 프로젝트만 통째로 틀린다. 처음 회귀에서 소광 한 건이 어긋난 원인이 이것이었다.
|
||||
|
||||
### 4.6 차수 인계 — 앞 파일의 `BOQ12`가 다음 파일의 `차수1`이 된다
|
||||
|
||||
| 무엇 | 재료비 | 노무비 | 경비 |
|
||||
|---|---:|---:|---:|
|
||||
| 원설계 파일 `BOQ12` | 47,987,107 | 82,246,518 | 43,077,761 |
|
||||
| **변경1차 파일 `BOQ11` 차수1** | 47,987,107 | 82,246,518 | 43,077,761 |
|
||||
| 변경1차 파일 `BOQ11` 차수2 | 75,735,506 | 119,733,078 | 61,941,849 |
|
||||
|
||||
**정확히 같다.** 08번이 「접미사 n = 항상 `BOQ1n` 으로 기계적으로 가정하지 말라」고 경고한 것이 옳았고, 실제 규칙은 이렇다.
|
||||
|
||||
```text
|
||||
통합형 : 한 STC 안에 BOQ11(당초) + BOQ12(현재안). 출력은 BOQ12.
|
||||
분리형 : 변경 차수를 새 STC 로 저장.
|
||||
그 파일의 BOQ11 차수1 = 앞 파일의 BOQ12
|
||||
그 파일의 BOQ11 차수2 = 이번 변경분
|
||||
```
|
||||
|
||||
`PRJLST.TCOSTn = MCOSTn + LCOSTn + SCOSTn` 이며 **직접공사비 합계**다. 소광 변경1차는 당초 173,311,386 → 변경 257,410,433, 증감 **+84,099,047**이다.
|
||||
|
||||
### 4.7 관급자재대는 천원 **올림**
|
||||
|
||||
```text
|
||||
관급자재대 = ROUNDUP(원자재대 143,554,309, 천원) = 143,555,000 ✅
|
||||
총공사비 = 도급공사비 + 관급자재대 = 1,414,283,000 ✅
|
||||
```
|
||||
|
||||
## 5. 이로써 확정으로 올라간 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| 내역 행 금액 = `ROUNDDOWN(수량 × 단가)` | A (수식 관찰) | **A 실증** — 1,515건 무결 |
|
||||
| 간접비 항목별 밑수 | A (화면 라벨) | **A 실증** — 6건 프로젝트에서 원 단위 일치 |
|
||||
| 간접비 순서와 절사 전달 | B | **A 실증** — 앞 행의 절사액을 뒤 행이 받음 |
|
||||
| 끝전조정 모델 | B | **A 실증** |
|
||||
| 계층 집계 구조 | B | **A 실증** |
|
||||
| 제외금액 취급 | 서술만 | **A 실증** — `C*` 구역 규칙 |
|
||||
|
||||
## 6. 아직 이 시험으로 못 건드린 것
|
||||
|
||||
골든셋은 **내역 표의 단가를 주어진 것으로 놓고** 그 위를 검증한다. 따라서 아래는 여전히 열려 있다.
|
||||
|
||||
- 단가가 **어떻게 만들어지는가** — 일위대가·중기 시간당 단가 (`BDQTY` 암호화)
|
||||
- 자재 6후보 중 **무엇을 채택하는가**
|
||||
- 수량 **할증** 계산
|
||||
- 수공·실적 형식(`S`/`TA`/`SA`) — 표본이 전부 일반형식 `T`
|
||||
- 변경설계 **간접비**의 계산모드 — 차수 인계와 직접비 증감(+84,099,047)까지는 확인했으나, 변경분 XLSX 출력이 표본에 없어 간접비 쪽 모드(방법 A/B/직접입력)는 아직 못 가름
|
||||
|
||||
## 7. 다음
|
||||
|
||||
1. **일위대가표·중기사용료 시트로 단가 조립 검증** — XLSX에 두 시트가 있어 `BDQTY` 암호화와 무관하게 단가가 어떻게 쌓이는지 확인 가능. 지금 자료로 되는 다음 칸
|
||||
2. 변경설계 **간접비** 모드 — 변경분 출력 XLSX 한 건만 있으면 정적으로 갈림
|
||||
3. 수공·실적 형식 표본 확보 — 현재 표본이 전부 일반형식 `T`
|
||||
@@ -0,0 +1,169 @@
|
||||
# 단가 조립 검증 — 일위대가와 중기 시간당 사용료
|
||||
|
||||
실행일: 2026-09-13
|
||||
방법: STmate가 뽑은 XLSX의 `일위대가표`·`중기사용료` 시트를 호표 단위로 갈라, **구성행의 성분별 금액 합이 합계행과 맞는지** 전수 대조함.
|
||||
재현 스크립트: `40_스크립트/unit_assembly_check.py`, `machine_rate_extract.py`
|
||||
|
||||
## 1. 왜 이게 되는가 — 막힌 줄 알았던 길
|
||||
|
||||
15번은 단가 조립을 「`BDQTY.DBF` 암호화로 불가」로 적었다. **틀렸다.**
|
||||
|
||||
`BDQTY`는 구성 수량의 **입력 원장**이지만, STmate가 뽑는 **출력 XLSX의 `일위대가표`·`중기사용료` 시트에 구성 자원·수량·단가·성분별 금액이 전부 평문으로 들어 있다.** 입력 원장은 못 읽어도 **조립 결과와 조립 규칙은 읽힌다.**
|
||||
|
||||
## 2. 성분 소계는 **절사**다
|
||||
|
||||
호표마다 구성행의 노무·재료·경비 금액을 각각 더하고 합계행과 대조했다.
|
||||
|
||||
| 시트 | 성분 소계 건수 | 절사 | 사사오입 | 은행가 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| **중기사용료** | 398 | **398 (100%)** | 265 (66.6%) | 280 (70.4%) |
|
||||
| **일위대가표** | 345 | **325 (94.2%)** | 198 (57.4%) | 211 (61.2%) |
|
||||
|
||||
**절사가 유일하게 전건을 설명한다.** 사사오입·은행가는 소수부가 없는 건에서만 우연히 맞는다.
|
||||
|
||||
> 07번 §6의 「자원/일위대가/중기 **집계표** 행 금액 = `ROUND`」와 헷갈리면 안 된다. 그것은 **집계표**(자원집계표·일위대가수량금액집계표·중기시간금액집계표)의 행 금액이고, 여기서 말하는 것은 **호표 안 성분 소계**다. 둘은 다른 자리이고 **반올림 방식도 다르다.**
|
||||
|
||||
일위대가표의 나머지 5.8%는 호표 머리·부분계 서식이 달라 자동 구획이 어긋난 것으로, 반올림 방식이 다르다는 증거는 아니다.
|
||||
|
||||
## 3. 중기 시간당 사용료의 구조 — A등급
|
||||
|
||||
호표 한 벌이 그대로 공식이다.
|
||||
|
||||
```text
|
||||
손료 = 취득가(천원) × 시간당 손료율 → 경비
|
||||
운전원 = 1인 × 노임단가 → 노무비
|
||||
주연료 = 시간당 소요량(L) × 연료단가 → 재료비
|
||||
잡품 = 주연료비 × 잡품률(%) → 재료비
|
||||
────────────────────────────────────────
|
||||
시간당 사용료 = 노무비 + 재료비 + 경비 (성분별로 절사한 뒤 합)
|
||||
```
|
||||
|
||||
굴삭기(무한궤도) 0.7㎥ 실제 값으로 확인하면 이렇다.
|
||||
|
||||
```text
|
||||
손료 110,926천원 × 0.2085 = 23,128.07 → 경비 23,128
|
||||
운전원 1인 × 55,700 = 55,700 → 노무 55,700
|
||||
경유 11.6 L × 1,273 = 14,766.8 → 재료
|
||||
잡품 14,766.8 × 22% = 3,248.6 → 재료
|
||||
재료 소계 = 절사(14,766.8 + 3,248.6) = 18,015
|
||||
합계 = 55,700 + 18,015 + 23,128 = 96,843 ✅ 시트와 일치
|
||||
```
|
||||
|
||||
**`잡품`의 단가 칸에는 연료 금액이 그대로 들어간다.** 즉 잡품은 수량×단가가 아니라 **연료비에 비율을 곱하는 가산 행**이다. 07번 §4가 「재료 수량 할증과 잡재료 금액 가산은 다른 연산」이라 한 것이 이 자리다.
|
||||
|
||||
## 4. 시간당 손료율 실측 — 25개 장비 (봉화 2024)
|
||||
|
||||
15번이 「중기 손료 계수 실제 값이 어디에도 없음 ❌」이라 적은 것을 **정정한다. 자료에 있다.**
|
||||
|
||||
| 장비 | 규격 | 손료율 | 취득가(천원) | 손료(원/h) | 연료(L/h) | 잡품(%) |
|
||||
|---|---|---:|---:|---:|---:|---:|
|
||||
| 굴삭기(무한궤도) | 0.2㎥ | 0.2085 | 62,550 | 13,042 | 5 | 21 |
|
||||
| 굴삭기(무한궤도) | 0.4㎥ | 0.2085 | 78,556 | 16,379 | 9.9 | 22 |
|
||||
| 굴삭기(무한궤도) | 0.7㎥ | 0.2085 | 110,926 | 23,128 | 11.6 | 22 |
|
||||
| 굴삭기(무한궤도) | 0.4㎥ 암석 | 0.2405 | 78,556 | 18,893 | 9.9 | 22 |
|
||||
| 굴삭기(무한궤도) | 0.7㎥ 암석 | 0.2405 | 110,926 | 26,678 | 11.6 | 22 |
|
||||
| 굴삭기(무한궤도) | 1.0㎥ 암석 | 0.2405 | 133,819 | 32,184 | 19.5 | 22 |
|
||||
| 굴삭기(무한궤도) | 0.4㎥ 브레이커조합 | 0.6601 | 7,882 | 5,203 | 9.9 | 16 |
|
||||
| 굴삭기(무한궤도) | 0.7㎥ 브레이커조합 | 0.6601 | 16,315 | 10,770 | 11.6 | 16 |
|
||||
| 굴삭기(타이어) | 0.6㎥ | 0.2279 | 112,684 | 25,681 | 11.6 | 24 |
|
||||
| 굴삭기(타이어) | 0.6㎥ 부착용집게 | 0.7435 | 7,381 | 5,488 | 11.6 | 24 |
|
||||
| 유압식 진동콤팩터 | 굴삭기 0.7㎥ 부착용 | 0.3193 | 11,046 | 3,527 | — | — |
|
||||
| 불도저(무한궤도) | 19톤 | 0.1811 | 184,499 | 33,413 | 25 | 16 |
|
||||
| 덤프트럭 | 2.5톤 암석 | 0.3533 | 20,793 | 7,346 | 2.9 | 38 |
|
||||
| 덤프트럭 | 15톤 | 0.2279 | 86,142 | 19,632 | 15.9 | 38 |
|
||||
| 덤프트럭 | 15톤 암석 | 0.2679 | 86,142 | 23,077 | 15.9 | 38 |
|
||||
| 덤프트럭 | 24톤 | 0.2229 | 140,899 | 31,406 | 23 | 38 |
|
||||
| 덤프트럭 자동덮개시설 | 15톤용 | 0.2684 | 1,546 | 415 | — | — |
|
||||
| 트럭탑재형 크레인 | 5톤 | 0.2598 | 38,469 | 9,994 | 5.1 | 20 |
|
||||
| 트럭 트랙터 및 평판트레일러 | 20톤 | 0.2576 | 63,483 | 16,353 | 16.5 | 39 |
|
||||
| 콘크리트 믹서 | 0.45㎥ | 0.3039 | 5,409 | 1,644 | 3.9 | 2 |
|
||||
| 커터(콘크리트·아스팔트용) | 320~440mm | 0.6354 | 2,954 | 1,877 | 5.6 | 20 |
|
||||
| 물탱크(살수차) | 5,500ℓ | 0.2113 | 44,820 | 9,471 | 9.3 | 30 |
|
||||
| 동력분무기 | 4.85㎾ | 0.2799 | 875 | 245 | 1.3 | — |
|
||||
| 진동롤러(자주식) | 12톤 | 0.2825 | 97,000 | 27,403 | 15.8 | 30 |
|
||||
| 크롤러드릴(탑승유압식) | 110㎾ | 0.1548 | 150,666 | 23,323 | 18.6 | 23 |
|
||||
|
||||
읽는 법 몇 가지.
|
||||
|
||||
- **같은 장비라도 「암석」이 붙으면 손료율이 오른다** — 굴삭기 0.2085 → 0.2405. 작업 조건이 손료율을 바꾼다.
|
||||
- **부착 장비는 취득가가 부착물만의 값이다** — 굴삭기 0.7㎥ 브레이커조합은 취득가 16,315천원에 손료율 0.6601이다. 본체와 별도 행으로 쌓는다.
|
||||
- **손료율은 취득가 1천원당 시간 손료(원)** 이다. `손료 = 취득가(천원) × 손료율`.
|
||||
- 잡품률은 장비마다 다르다(2~39%). 일괄 적용하면 안 된다.
|
||||
|
||||
⚠ 이 값들은 **STmate가 담고 있는 값**이다. Aislo는 같은 수치를 **법령·품셈**(건설기계 경비 산정 기준)에서 확보해야 하며, 여기 표는 **대조용**으로만 쓴다.
|
||||
|
||||
## 5. 일위대가 조립
|
||||
|
||||
호표는 구성 자원을 나열하고 각 행이 `수량 × 단가`로 성분별 금액을 만든다. 구성 자원은 **다른 호표를 참조할 수 있다.**
|
||||
|
||||
```text
|
||||
제 6 호표 기슭막이(깬잡석,찰쌓기 L3=45) H=2.0m
|
||||
깬잡석채집 2.09 M2 × 30,302 = 63,331 ← 다른 호표
|
||||
깬잡석찰쌓기 2.09 M2 × 58,889 = 123,078 ← 제 1 호표
|
||||
고임돌채집 0.31 M3 × 17,416 = 5,399
|
||||
…
|
||||
```
|
||||
|
||||
`깬잡석찰쌓기 58,889`가 제 1 호표의 합계와 같다. **호표 사이에 참조가 걸리므로 위상순서로 계산해야 한다** — 09번 §3이 말한 의존 그래프가 실제로 필요하다.
|
||||
|
||||
## 5.5 자재 채택 규칙 — **최저가**, A등급 실증
|
||||
|
||||
출력 XLSX의 `자재단가대비표` 시트에 **후보 다섯 칸과 적용 단가·출처가 나란히** 있다.
|
||||
|
||||
```text
|
||||
호표 | 명칭 | 규격 | 단위 | 조달가격 | 물가자료 | 물가정보 | 거래가격 | 견적단가 | 적용단가 | 출처
|
||||
1 | 철못 | N 50 | KG | 0 | 1,665 | 1,935 | 1,680 | 0 | 1,665 | 물자
|
||||
6 | PVC파이프 | VG1 | M | 0 | 4,327 | 4,702 | 3,475 | 0 | 3,475 | 거래
|
||||
19 | 와이어메쉬 | #6 | M2 | 3,546 | 4,970 | 5,510 | 0 | 4,545 | 3,546 | 조달
|
||||
```
|
||||
|
||||
실무 프로젝트 6건 전수로 검정했다.
|
||||
|
||||
| 구분 | 건수 | 적용단가 = 후보 최저 |
|
||||
|---|---:|---:|
|
||||
| 전체 (후보 1개 이상) | 186 | **186 (100%)** |
|
||||
| **후보 값이 2종 이상** | 76 | **76 (100%)** |
|
||||
| **후보 값이 3종 이상** | 36 | **36 (100%)** |
|
||||
| 최저가가 아닌 후보를 택한 건 | — | **0** |
|
||||
| 어느 후보와도 다른 건 | — | **0** |
|
||||
|
||||
⇒ **자동채택은 「0이 아닌 후보 중 최저가」다.** 07번 §5가 「최저가·최신가·특정 슬롯 우선 중 어느 하나라고 단정할 근거가 없다」며 C등급으로 둔 항목이 **실증으로 확정**됐다.
|
||||
|
||||
곁들여 확인된 것.
|
||||
|
||||
- 후보 슬롯은 화면상 **다섯**(조달가격·물가자료·물가정보·거래가격·견적단가)이고, `COSTN`의 `AMT_11..AMT_61` 여섯 칸 중 **여섯 번째가 적용 단가 칸**으로 보인다. 07번이 「노무 L은 후보6과 채택가가 거의 일치」라 관찰한 것과 맞는다.
|
||||
- 후보가 하나도 없는데 적용 단가만 있는 행이 25건 있다(경유·휘발유 등 **유가 자료**, 별도 배포 단가).
|
||||
- 적용 단가가 0인 행 49건은 `별도계상`(이형철근·레미콘)·`조달수수료(재료비×0.54%)`처럼 **다른 자리에서 계산되는** 것이다.
|
||||
- **출처 표시는 슬롯 이름의 축약**이다(물자=물가자료, 물정=물가정보, 거래, 조달, 견적). 슬롯 번호에 기관명을 코드로 박으면 안 된다는 07번 경고는 그대로 유효하다 — `Start8x.Ini`가 표시명을 바꾼다.
|
||||
|
||||
⚠ **사용자 수동 고정이 있으면 이 규칙을 벗어난다.** 표본 6건에는 수동 고정 흔적이 없었을 뿐이며, 수동 우선순위는 여전히 통제시험(T10) 대상이다.
|
||||
|
||||
## 6. 이로써 바뀌는 판정
|
||||
|
||||
| 항목 | 15번의 판정 | 지금 |
|
||||
|---|---|---|
|
||||
| 중기 시간당 단가 재현 | ❌ 손료 계수가 어디에도 없음 | ✅ **호표에 손료율·취득가·연료·잡품률이 전부 있음** |
|
||||
| 단가 조립 규칙 | ❌ `BDQTY` 암호화로 불가 | ✅ **출력 시트로 조립 규칙·반올림 확인됨** |
|
||||
| 성분 소계 반올림 | 미확정 | ✅ **절사** (중기 398건 100%) |
|
||||
| 자재 6후보 채택 | ❌ C등급, 규칙 없음 | ✅ **최저가** — 복수후보 76/76 |
|
||||
|
||||
여전히 막힌 것은 **입력 쪽**이다.
|
||||
|
||||
- `BDQTY`의 수량 **할증** 필드(`X_QTY`·`X_HAL`) 실값 — 출력에는 할증 후 수량만 남는다
|
||||
- 품셈에서 구성 수량이 **어떻게 뽑혔는가**
|
||||
- 사용자가 **수동 고정**했을 때의 우선순위 (T10)
|
||||
|
||||
## 7. 다음
|
||||
|
||||
1. 일위대가 호표 사이 **참조 그래프**를 복원해 위상순서 계산을 재현 — 지금 자료로 됨
|
||||
2. `단가산출근거` 시트 구조 확인 — 시트는 있으나 이번 구획 규칙으로는 안 잡힘
|
||||
3. 09번 §8이 꼽은 **여섯 호환성 차단점** 가운데 남은 것 정리
|
||||
|
||||
| # | 차단점 | 상태 |
|
||||
|---:|---|---|
|
||||
| 1 | RATE 좌표 라벨·경계 | ⚠ 보험 계열 6개 확정(15번 §4), 규모·기간 좌표는 남음 |
|
||||
| 2 | `BDQTY` 세부 플래그 | ❌ 암호화 — 유일하게 손 못 댄 입력 |
|
||||
| 3 | 자재 자동채택 우선순위 | ✅ **최저가 확정** (§5.5) |
|
||||
| 4 | 중기 내부 절사 시점 | ✅ **성분별 절사 확정** (§2·§3) |
|
||||
| 5 | UI 명령별 재계산 범위 | ❌ 통제시험 T8 |
|
||||
| 6 | 일괄변경 수동값 우선순위 | ❌ 통제시험 T10 |
|
||||
@@ -0,0 +1,152 @@
|
||||
# 누락 계산로직 점검 — 사슬에 빠진 칸 찾기
|
||||
|
||||
점검일: 2026-09-13
|
||||
방법: STmate 출력 XLSX의 **21개 시트를 하나씩 세어**, 계산이 일어나는 시트 가운데 아직 확인하지 않은 것을 모두 열어 봄.
|
||||
재현 스크립트: `40_스크립트/wage_hourly_check.py`, `unit_derivation_check.py`
|
||||
|
||||
## 1. 점검 전 상태
|
||||
|
||||
21개 시트 중 **검증한 것은 4개뿐**이었다.
|
||||
|
||||
| 확인함 | 미확인 |
|
||||
|---|---|
|
||||
| 공사원가계산서 · 설계내역서 · 일위대가표 · 중기사용료 · 자재단가대비표 | **총괄설계내역서 · 일위대가목록표 · 단가산출근거(3,889행) · 단가산출근거목록표 · 환율및기초자료 · 중기목록표 · 집계표 4종 · 목록표 3종 · 〓 INITIAL 〓** |
|
||||
|
||||
가장 큰 시트인 **`단가산출근거` 3,889행이 통째로 미확인**이었다.
|
||||
|
||||
## 2. 빠져 있던 것 넷
|
||||
|
||||
### 2.1 노임 시간당 환산식 — 완전 미문서화였음
|
||||
|
||||
`환율및기초자료` 시트에 **일 노임을 시간당으로 바꾸는 식이 그대로** 있다.
|
||||
|
||||
```text
|
||||
건설기계운전사 267,360 × 1/8*16/12*25/20 → 55,700 (L00008)
|
||||
화물차운전사 226,709 × 1/8*16/12*25/20 → 47,231 (L00009)
|
||||
일반기계운전사 161,142 × 1/8*16/12*25/20 → 33,571 (L00010)
|
||||
```
|
||||
|
||||
식은 **프로젝트 설정에 문자열로 저장**된다 — `〓 INITIAL 〓` 의 `RXNM_ = 1/8*16/12*25/20`, 적용 직종은 `COND_ = L00008L00009L00010`(코드 11자리씩 이어 붙임).
|
||||
|
||||
**중기사용료의 「운전원 55,700」이 어디서 왔는지가 이것으로 풀린다.** 지금까지는 주어진 값으로만 놓고 있었다.
|
||||
|
||||
#### ⚠ 왼쪽부터 차례로 계산해야 한다 — 안 그러면 1원 틀림
|
||||
|
||||
| 직종 | 일 노임 | 계수를 먼저 곱함 | **왼쪽부터 차례로** | 출력 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| 건설기계운전사 | 267,360 | 55,699 ✗ | **55,700** ✅ | 55,700 |
|
||||
| 화물차운전사 | 226,709 | 47,231 | 47,231 | 47,231 |
|
||||
| 일반기계운전사 | 161,142 | 33,571 | 33,571 | 33,571 |
|
||||
|
||||
`1/8*16/12*25/20`을 배정밀도로 미리 곱하면 `0.20833333333333331`이 되어 `267,360 × 계수 = 55,699.9999…` → 절사 시 **55,699**가 된다. 왼쪽부터 차례로 하면 `267360/8=33420 → ×16=534720 → /12=44560 → ×25=1,114,000 → /20=55,700`으로 **모든 단계가 정수**라 정확히 맞는다.
|
||||
|
||||
프로젝트 6건 × 직종 3개 = 18건 전부, **왼쪽부터 계산했을 때만 18/18 일치**한다(계수 선계산은 14/18).
|
||||
|
||||
⇒ **재구현 규칙: 설정에 저장된 식 문자열을 그대로 좌→우 순차 평가할 것. 계수로 접어 두지 말 것.** 11번 §4.1의 `evaluate(template, …)` 계약에 이 조건을 명시해야 한다.
|
||||
|
||||
### 2.2 단가산출(`D` 코드) 공식 — 가장 큰 누락
|
||||
|
||||
`단가산출근거` 3,889행에 **품셈 근거와 기계 작업량 식이 전부** 있다.
|
||||
|
||||
```text
|
||||
제 2호표 구조물 터파기(토사) 굴삭기 0.2m3 / m3
|
||||
건설표준품셈 3-1-3(터파기), 11-3(굴삭기) 적용
|
||||
적용토질 : 자갈썩인 기계 및 인력 조합기준 : 기계100%
|
||||
|
||||
q = 0.2 , f = 1/1.175 = 0.85 , K = 0.7
|
||||
Cm = 15 sec (90°) , E = 0.60-0.05 = 0.55
|
||||
Q = 3600*q*K*f*E/Cm = 15.71 m3/hr
|
||||
|
||||
노 무 비 : 55,700 / Q = 3,545.5
|
||||
재 료 비 : 7,701 / Q = 490.1
|
||||
경 비 : 13,041 / Q = 830.1
|
||||
소계 4,865.7
|
||||
```
|
||||
|
||||
**구조**
|
||||
|
||||
```text
|
||||
Q(시간당 작업량) = 3600 × q × K × f × E / Cm
|
||||
단가 성분[k] = 절사( 중기 시간당 성분단가[k] / Q , 소수 1자리 )
|
||||
단가 소계 = 성분 셋의 합 (소수 1자리 유지)
|
||||
내역에 실릴 때 정수로 절사
|
||||
```
|
||||
|
||||
- `55,700 / 7,701 / 13,041`은 **중기사용료 제1호표(굴삭기 0.2㎥)의 노무·재료·경비 합계** 그대로다. 즉 `중기사용료 → 단가산출 → 일위대가 → 내역`으로 값이 이어진다.
|
||||
- **`Q`는 소수 2자리로 먼저 확정한 뒤 나눈다.** 원값으로 나누면 어긋난다 (`55,700/15.708 = 3,545.96` ≠ `55,700/15.71 = 3,545.5`).
|
||||
- 성분단가는 **소수 1자리 절사**다. 6개 프로젝트 211건 중 137건(64.9%)이 이 규칙으로 설명되고, 나머지는 한 호표에 `Q`가 둘 이상인 기계조합 호표라 자동 구획이 어긋난 것이다.
|
||||
|
||||
`Q`식은 73개 호표에서 발견됐다. 토질·조합기준·버킷계수 같은 **품셈 인자가 문자열로 남아 있어**, `BDQTY`가 암호화돼 있어도 **무엇을 근거로 그 수량이 나왔는지는 읽힌다.**
|
||||
|
||||
### 2.3 `〓 INITIAL 〓` — 계산 설정 스냅샷이 출력에 들어 있다
|
||||
|
||||
12번 §10은 「이 설정 스냅샷 없이는 같은 원본 내역으로도 원가계산 결과를 재현할 수 없다」고 적었다. **그 스냅샷이 출력 XLSX 안에 있다.**
|
||||
|
||||
```text
|
||||
[공사정보] 33개 M_VER 24.02 · PNAME · PCLAS · SCLAS · K_ORD · PRIPR · ACTYP 1 · ACTOP 2
|
||||
[초기값] 52개 P_ORD · EXNM_(환율 8칸) · EXRNM · RXNM_(노임 환산식) · COND_(적용 직종)
|
||||
KCDNM · JCDNM · ECDNM · ACDNM · BPNNM · ZCDNM
|
||||
CHGNM · CHBNM · CHNNM · CUTNM · OPTNM(SML) · S_JDA · SUBNM(발주청)
|
||||
[인쇄정보] 75개 BMSTR · BMKNM(TLMS) · JEMNM(123456)
|
||||
JMKN1..6 = 조달가격/조달 · 물가자료/물자 · 물가정보/물정 · 거래가격/거래
|
||||
· 견적단가/견적 · **적용 단가**
|
||||
CAP_* 열 제목 13개
|
||||
[PREPARE] PREP_VER · PREP_COP(작성기관) · PREP_DATE
|
||||
```
|
||||
|
||||
곁들여 확정된 것 둘.
|
||||
|
||||
- **`JMKN6 = 적용 단가`** — 후보 슬롯이 다섯이고 **여섯 번째가 채택가 칸**이라는 17번 §5.5의 추정이 확인됐다.
|
||||
- **슬롯 이름은 프로젝트 설정에 들어 있다** — 07번이 「슬롯 번호에 가격지 이름을 코드로 고정하지 말라」고 한 경고의 근거가 여기다.
|
||||
|
||||
### 2.4 집계표 반올림 = **사사오입**, 절사 아님
|
||||
|
||||
호표 성분 소계(절사)와 **다른 자리**임을 수치로 갈랐다.
|
||||
|
||||
| 시트 | 표본 | 사사오입 | 은행가 | 절사 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| 재료비수량금액집계표 | 169 | **169 (100%)** | 167 (99%) | 134 (79%) |
|
||||
| 노무비수량금액집계표 | 62 | **62 (100%)** | 62 (100%) | 32 (52%) |
|
||||
| 경비수량금액집계표 | 10 | **10 (100%)** | 10 (100%) | 6 (60%) |
|
||||
|
||||
**사사오입만 전건을 설명한다.** 재료비집계표 2건이 은행가와 갈려 둘을 구분해 준다. 07번 §6의 「집계표 = `ROUND`」가 실측으로 확인됐고, 17번 §2의 「호표 성분 소계 = 절사」와 **자리가 다르다**는 점이 분명해졌다.
|
||||
|
||||
## 3. 이로써 드러난 전체 계산 사슬
|
||||
|
||||
```text
|
||||
① 일 노임 ─ 설정 식(좌→우 순차) ─→ 시간당 노임
|
||||
② 취득가 · 손료율 · 연료 · 잡품률 ──────→ 중기 시간당 사용료 (성분별 절사)
|
||||
③ 품셈 인자 q·K·f·E·Cm ─ Q=3600qKfE/Cm ─→ 단가산출 D코드
|
||||
성분/Q, 소수1 절사
|
||||
④ 자원 · D코드 · 다른 호표 ────────────→ 일위대가 B코드 (성분별 절사)
|
||||
⑤ 설계수량 × 단가 ─ ROUNDDOWN(정수) ───→ 내역 행
|
||||
⑥ 내역 계층 합산 ─ C* 구역 제외 ───────→ 직접비
|
||||
⑦ 밑수 × 요율 ─ 행마다 절사, 앞 행 결과를 뒤 행이 참조 ─→ 간접비
|
||||
⑧ 일반관리비 → 이윤 → 끝전조정 → 부가세 → 관급(천원 올림) → 총공사비
|
||||
(집계표 출력만 사사오입, 나머지는 절사)
|
||||
```
|
||||
|
||||
**①~④가 이번에 채워진 칸이다.** 그전에는 ⑤부터만 검증돼 있었다.
|
||||
|
||||
## 4. 아직 안 본 시트
|
||||
|
||||
계산이 아니라 **목록·표지** 성격이라 우선순위가 낮다.
|
||||
|
||||
| 시트 | 성격 |
|
||||
|---|---|
|
||||
| 총괄설계내역서 | 내역서의 공종 상위 집계 — ⑥의 다른 표현 |
|
||||
| 일위대가목록표 · 단가산출근거목록표 · 중기목록표 | 호표 번호와 이름 색인 |
|
||||
| 재료비목록표 · 노무비목록표 · 경비목록표 | 자원 코드·단가 색인 |
|
||||
| 〓 목 차 〓 · ※※안내※※ | 표지 |
|
||||
|
||||
**중기시간금액집계표**는 표본에서 수량·단가·금액 세 쌍을 못 잡아 반올림 판정이 비었다. 다음에 열 위치를 맞춰 다시 볼 것.
|
||||
|
||||
## 5. 이번 점검이 바꾸는 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| 노임 시간당 환산 | **문서에 없음** | ✅ 식·저장 위치·평가 순서까지 확정 |
|
||||
| 식 평가 방식 | 명시 없음 | ✅ **좌→우 순차** (계수 선계산 금지) |
|
||||
| 단가산출 `D`코드 | **문서에 없음** | ✅ Q식·나눗셈·소수1 절사 |
|
||||
| 계산 설정 스냅샷 | 「화면 없이는 재현 불가」 | ✅ **출력 XLSX `〓 INITIAL 〓` 에 있음** |
|
||||
| 집계표 반올림 | 서술만 | ✅ **사사오입 100%** — 호표 절사와 다른 자리 |
|
||||
@@ -0,0 +1,173 @@
|
||||
# 진행률 장부 — 어디까지 봤고 무엇이 남았나
|
||||
|
||||
갱신일: 2026-09-13
|
||||
세는 법: 눈대중이 아니라 **파일·표·시트를 실제로 세어** 적음. 재현 스크립트 `40_스크립트/coverage_ledger.py`
|
||||
|
||||
## 1. 한눈에
|
||||
|
||||
| 원천 | 전체 | 본 것 | 남은 것 | 진행 |
|
||||
|---|---:|---:|---:|---|
|
||||
| 설치 폴더 `System` 파일 | 186 | 186 등록·형식·내부구조 | 암호화 13개는 **영구 불가** | ▓▓▓▓▓▓▓▓▓▓ 100% |
|
||||
| 기준 요율표 `JB_*` | 43 | 43 전수 파싱·블록·확장차이 | 셀 좌표 일부 | ▓▓▓▓▓▓▓▓░░ 85% |
|
||||
| 실무 프로젝트 `STC` | 7 | 7 전부 | — | ▓▓▓▓▓▓▓▓▓▓ 100% |
|
||||
| STC 안의 표 | 16종 | **10종 읽고 씀** · 4종 빈 표 | **2종 암호화** (`BDQTY`·`SYSINFO`) | ▓▓▓▓▓▓▓▓▓░ 88% |
|
||||
| STmate 출력 `XLSX` | 6 | 6 전부 | — | ▓▓▓▓▓▓▓▓▓▓ 100% |
|
||||
| 출력 시트 | **30종** | **30종 전부 훑음** (계산 있는 것 15종 규칙 확정) | — | ▓▓▓▓▓▓▓▓▓▓ 100% |
|
||||
| 계산 사슬 단계 | 8 | **8 전부** (④는 참조 그래프까지 실증) | — | ▓▓▓▓▓▓▓▓▓▓ 100% |
|
||||
| 변경설계 재현 | 직접비+간접비 | **전 항목 원 단위 일치** | — | ▓▓▓▓▓▓▓▓▓▓ 100% |
|
||||
| 09번이 꼽은 호환성 차단점 | 6 | **3 해결 · 1 부분** | 2 (실행 필요) | ▓▓▓▓▓▓░░░░ 58% |
|
||||
|
||||
## 2. 설치 폴더 186개
|
||||
|
||||
| 갈래 | 개수 | 상태 |
|
||||
|---|---:|---|
|
||||
| PE 실행모듈 (`*.CPM`·`*.BPL`·`*.EXE`) | 82 | 헤더·섹션·임포트·문자열 전수. 핵심 12개는 호출자리까지 |
|
||||
| ZIP 계열 (`*.STBSC` 등) | 28 | 컨테이너 확인, 내부는 암호 |
|
||||
| JB 요율표 | 43 | **전수 파싱** — 형태·블록·2020-05-27 확장 차이 |
|
||||
| 암호화 기준단가 (`*.BSC`·`*.CSTA`) | 13 | ⛔ **본문 암호화 — 영구 불가** |
|
||||
| 기타 | 20 | 형식 판정 완료 |
|
||||
|
||||
⛔ 13개 안에 **자재 57,580건·노임 460건·중기 833건·표준시장단가 4,921건**이 들어 있다. 이것이 「완제품 복제 불가」의 유일한 근거다.
|
||||
|
||||
## 3. 출력 시트 30종 — 가장 많이 남은 곳
|
||||
|
||||
### ✅ 계산 검증 완료 14종
|
||||
|
||||
```
|
||||
공사원가계산서 · 설계내역서 · 일위대가표 · 중기사용료 · 자재단가대비표
|
||||
단가산출근거 · 환율및기초자료 · 〓 INITIAL 〓
|
||||
재료비수량금액집계표 · 노무비수량금액집계표 · 경비수량금액집계표
|
||||
설계변경내역서 · 설계변경금액대비표 · 설계변경증감대비표
|
||||
```
|
||||
|
||||
### ✅ 나머지 16종도 훑음 (2026-09-13)
|
||||
|
||||
전부 열어 본 결과 **새 계산 규칙은 나오지 않았고**, 대신 두 가지가 밝혀졌다.
|
||||
|
||||
| 갈래 | 시트 | 결과 |
|
||||
|---|---|---|
|
||||
| 색인·목록 | 일위대가목록표 · 단가산출근거목록표 · 중기목록표 · 재료비목록표 · 노무비목록표 · 경비목록표 · 일식견적목록표 | **계산 쌍 없음** — 호표 번호·코드·단가 색인 |
|
||||
| 표지 | 〓 목 차 〓 · ※※안내※※ | 계산 없음 |
|
||||
| 내역서 변형 | **착공내역서** | 178건 **절사 100%** — 설계내역서와 같은 규칙 |
|
||||
| | 총괄설계내역서 · 총괄내역서 | 공종 상위 집계, 새 규칙 없음 |
|
||||
| | 설계변경내역서 | 당초·변경 두 벌이라 단순 쌍 검출로는 안 갈림. 금액 자체는 19번 §6에서 확정 |
|
||||
| 집계표 | 중기시간금액집계표 · 일위대가수량금액집계표 · 단가산출근거수량금액집계표 · 일식견적수량금액집계표 | **단가 열이 없음** — 아래 |
|
||||
|
||||
#### 집계표에는 단가 열이 없다
|
||||
|
||||
```text
|
||||
호표 | 명칭 | 규격 | 수량 | 단위 | 합계 | 노무비 | 재료비 | 경비
|
||||
2 | 굴삭기(무한궤도) | 0.7㎥ | 940.69 | 시간 | 91,099,241 | 52,396,433 | 16,946,530 | 21,756,278
|
||||
```
|
||||
|
||||
금액은 **`절사(수량 × 해당 호표의 시간당 성분단가)`** 다. 굴삭기 0.7㎥의 시간당 성분(노무 55,700 / 재료 18,015 / 경비 23,128)으로 검산하면 맞는다.
|
||||
|
||||
| 장비 | 시간 | 노무 | 재료 | 경비 |
|
||||
|---|---:|---|---|---|
|
||||
| 굴삭기 0.2㎥ | 17.47 | ✅ | ✅ | ✅ |
|
||||
| 굴삭기 0.7㎥ | 940.69 | ✅ | ✅ | ✅ |
|
||||
| 덤프 15톤 | 379.54 | ✅ | ✅ | −1 |
|
||||
| 덤프 2.5톤 | 150.52 | ✅ | −1 | −1 |
|
||||
|
||||
#### ⚠ 표시 수량은 반올림된 값이다
|
||||
|
||||
어긋나는 세 칸은 전부 **−1**이다. 경비로 역산하면 굴삭기 0.7㎥의 실제 시간은 `21,756,278 / 23,128 = 940.68998…`인데 시트에는 `940.69`로 찍힌다.
|
||||
|
||||
⇒ **집계표의 수량을 그대로 역산하면 1원씩 어긋난다.** 집계는 표시 수량이 아니라 **원수량 또는 행별 금액의 합**에서 나온다. 09번 §4의 「입력 원값과 표시 반올림값을 분리한다」가 바로 이 자리다.
|
||||
|
||||
## 4. 계산 사슬 8단계 — 전부 확인
|
||||
|
||||
| # | 단계 | 근거 문서 |
|
||||
|---:|---|---|
|
||||
| ① | 일 노임 → 시간당 (설정식, 좌→우 순차) | 18번 §2.1 |
|
||||
| ② | 중기 시간당 사용료 (손료·운전원·연료·잡품, 성분별 절사) | 17번 §3 |
|
||||
| ③ | 단가산출 `D`코드 (`Q=3600qKfE/Cm`, 성분/Q 소수1 절사) | 18번 §2.2 |
|
||||
| ④ | 일위대가 `B`코드 (구성 조립, 성분별 절사, 호표 간 참조) | 17번 §2·§5 |
|
||||
| ⑤ | 내역 행 (`ROUNDDOWN(수량×단가)`) | 16번 |
|
||||
| ⑥ | 계층 합산·제외금액 구역 | 16번 §4.1·§4.2 |
|
||||
| ⑦ | 간접비 (밑수×요율, 행마다 절사, 앞 행 참조) | 16번 §3, 06번 §3 |
|
||||
| ⑧ | 일반관리비 → 이윤 → 끝전조정 → 부가세 → 관급 → 총공사비 | 16번 §4.4 |
|
||||
|
||||
## 5. 호환성 차단점 여섯 — 현황
|
||||
|
||||
| # | 차단점 | 상태 | 근거 |
|
||||
|---:|---|---|---|
|
||||
| 1 | RATE 좌표 라벨·경계 | ◐ **부분** — 고정 칸(보험·산안비·이윤) 확정, 규모·기간 의존 칸은 남음 | 15번 §4 |
|
||||
| 2 | `BDQTY` 세부 플래그 | ⛔ **암호화 — 불가** | 15번 §2 |
|
||||
| 3 | 자재 자동채택 우선순위 | ✅ **최저가** | 17번 §5.5 |
|
||||
| 4 | 중기 내부 절사 시점 | ✅ **성분별 절사** | 17번 §2 |
|
||||
| 5 | UI 명령별 재계산 범위 | ⛔ 실행 필요 | 13번 T8 |
|
||||
| 6 | 일괄변경 수동값 우선순위 | ⛔ 실행 필요 | 13번 T10 |
|
||||
|
||||
**추가로 09번 목록 밖에서 푼 것** — 변경설계 계산모드(아래 6장), 노임 환산식, 단가산출 Q식, 계산 설정 스냅샷, 집계표 반올림.
|
||||
|
||||
## 6. 변경설계 계산모드 — 이번에 확정 (T9)
|
||||
|
||||
계류보전 2025(변경) 프로젝트의 `공사원가계산서`가 **당초·변경·증감을 나란히** 낸다. 직접노무비가 `32,976,276 → 41,778,438`(+8,802,162)로 크게 늘었는데도 **세 항목은 증감이 0이다.**
|
||||
|
||||
| 항목 | 당초 | 변경 | 증감 | 재계산했다면 | 판정 |
|
||||
|---|---:|---:|---:|---:|---|
|
||||
| 간접노무비 | 4,715,607 | 5,974,316 | +1,258,709 | 5,974,316 ✅ | **재계산** |
|
||||
| 산재보험료 | 1,341,831 | 1,699,998 | +358,167 | 1,699,998 ✅ | **재계산** |
|
||||
| 고용보험료 | 380,688 | 482,302 | +101,614 | 482,302 ✅ | **재계산** |
|
||||
| **건강보험료** | 1,328,500 | 1,328,500 | **0** | 1,683,108 (+354,608) | **당초액 유지** |
|
||||
| **노인장기요양보험료** | 172,040 | 172,040 | **0** | 217,961 (+45,921) | **당초액 유지** |
|
||||
| **연금보험료** | 1,686,389 | 1,686,389 | **0** | 2,136,526 (+450,137) | **당초액 유지** |
|
||||
|
||||
요율은 당초·변경이 같다(간접노무비 14.300%, 산재 3.560%, 고용 1.010%). 즉 요율을 바꾼 것이 아니라 **항목마다 모드가 다르다.**
|
||||
|
||||
⇒ **건강보험료·노인장기요양보험료·연금보험료는 사후정산 대상이라 설계 단계에서 당초액을 그대로 가져간다.** 11번 §10의 「보험료 사후정산 항목은 당초금액과 동일하게 직접 입력하는 옵션」이 실데이터로 확인됐다.
|
||||
|
||||
**재구현 필수** — 간접비 항목마다 `calc_mode ∈ {recalculated, carry_original, proportional_from_original, delta_times_rate, manual}` 을 저장할 것. 전부 재계산으로 두면 이 세 항목에서 **85만 원**이 어긋난다.
|
||||
|
||||
## 6.5 이번에 마저 끝낸 둘
|
||||
|
||||
### 일위대가 호표 참조 그래프 — ④단계 실증
|
||||
|
||||
봉화 2024의 `일위대가표` 45개 호표를 그래프로 세웠다.
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 구성행이 다른 호표를 가리키고 **단가가 일치** | **22건** |
|
||||
| 이름은 같은데 단가가 다름 | **0건** |
|
||||
| 호표 간 참조를 가진 호표 | 18 / 45 |
|
||||
| **순환 참조** | **없음 → 위상순서 계산 가능** |
|
||||
| 참조 깊이 | 최대 **2단** (잎 27 · 1단 14 · 2단 4) |
|
||||
|
||||
⇒ 09번 §3의 「의존 그래프를 만들고 위상순서로 계산, 순환은 오류」가 **실데이터에서 성립**한다. 깊이가 얕아 재귀 깊이 걱정은 없고, 이름+규격 짝으로 참조를 찾을 수 있다(불일치 0건).
|
||||
|
||||
### 설계변경 직접비 — 당초·변경 양쪽 원 단위 재현
|
||||
|
||||
계류보전 2025(변경)의 `BOQ11`은 한 표에 **차수1(당초)·차수2(변경)를 나란히** 담는다. 양쪽을 각각 다시 세웠다.
|
||||
|
||||
| | 직접재료비 | 직접노무비 | 산출경비 | 행금액 절사 |
|
||||
|---|---:|---:|---:|---|
|
||||
| 차수1 (당초) | 40,003,539 ✅ | 32,976,276 ✅ | 20,047,669 ✅ | 294건 무결 |
|
||||
| 차수2 (변경) | 26,448,347 ✅ | 41,778,438 ✅ | 16,915,256 ✅ | 294건 무결 |
|
||||
| **증감** | −13,555,192 | **+8,802,162** | −3,132,413 | |
|
||||
|
||||
**전 항목 1원도 안 틀림.** 같은 말단 행 집합(98행)에 차수별 수량·단가를 걸어 계산하면 되고, **제외금액 구역(`C*`) 판정도 차수와 무관하게 같다.**
|
||||
|
||||
이로써 6장의 간접비 모드와 합쳐 **변경설계가 직접비부터 간접비까지 전부 재현**된다.
|
||||
|
||||
## 7. 남은 일 — 할 수 있는 것과 없는 것
|
||||
|
||||
### 지금 자료로 할 수 있는 것
|
||||
|
||||
| 순위 | 할 일 | 왜 |
|
||||
|---:|---|---|
|
||||
| ~~1~~ | ~~남은 시트 훑기~~ | ✅ **완료** — 30종 전부. 새 규칙 없음, 집계표 ±1 원인 밝힘 |
|
||||
| ~~2~~ | ~~중기시간금액집계표~~ | ✅ **완료** — 절사(수량 × 시간당 성분단가) |
|
||||
| ~~1~~ | ~~일위대가 참조 그래프~~ | ✅ **완료** — 순환 없음, 깊이 2단, 불일치 0 (§6.5) |
|
||||
| ~~2~~ | ~~설계변경 직접비 증감~~ | ✅ **완료** — 당초·변경 전 항목 원 단위 일치 (§6.5) |
|
||||
| 1 | **표본 늘리기** | 지금 자료로 할 수 있는 것은 사실상 다 함. 규모·기간 의존 요율 좌표와 수공·실적 형식은 **새 표본이 있어야** 열림 |
|
||||
|
||||
### 끝내 못 하는 것
|
||||
|
||||
| 무엇 | 왜 |
|
||||
|---|---|
|
||||
| 기초단가 원천 (자재 5.7만 건 등) | 본문 암호화 |
|
||||
| `BDQTY` 수량 할증 | 본문 암호화 |
|
||||
| UI 재계산 범위·일괄변경 우선순위 | **STmate 실행 필요 — 라이선스 없음** |
|
||||
| 규모·기간 의존 요율 좌표 | 같음. 표본을 늘리면 좁혀지기는 함 |
|
||||
| 수공 `S`·실적 `TA`/`SA` 형식 | **표본이 하나도 없음.** 그 형식 설계서가 한 건 생기면 바로 가능 |
|
||||
@@ -0,0 +1,156 @@
|
||||
# 설정파일과 보고서 카탈로그 — 마지막으로 남아 있던 자리
|
||||
|
||||
점검일: 2026-09-13
|
||||
계기: 「추가로 더 있는지」 확인. **`System` 폴더만 보고 `STmate 자료`·`STmate 압축자료` 폴더를 통째로 안 봤다.**
|
||||
재현 스크립트: `40_스크립트/ini_and_report_catalog.py`
|
||||
|
||||
## 1. 안 보고 있던 자리
|
||||
|
||||
`C:\경영정보(주)` 아래에는 폴더가 넷이다.
|
||||
|
||||
| 폴더 | 내용 | 그동안 |
|
||||
|---|---|---|
|
||||
| `System` | 설치 파일 186개 | ✅ 전수 |
|
||||
| `STmate 자료\설계자료\실습(맛보기)` | 맛보기 프로젝트를 **푼 것** 23개 | ⚠ **STC와 같다고 넘겼음** |
|
||||
| `STmate 압축자료` | `실습(맛보기).STC` | ✅ |
|
||||
| `(TMP)` | 비어 있음 | — |
|
||||
|
||||
DBF는 STC와 같지만 **STC에 없는 파일 셋이 있었다** — `Start8x.Ini` · `STini.MEM` · `QCODE.SMT`.
|
||||
|
||||
## 2. `Start8x.Ini` — 설치본 전역 설정
|
||||
|
||||
### 2.1 ⭐ 간접비 계산 순서가 코드 목록으로 저장돼 있다
|
||||
|
||||
```ini
|
||||
KAN_ORD_1=;301;302;303;310;304;305;306;312;313;321;307;308;309;311;361;362;363;364;365;366;367;368;369;370;
|
||||
KAN_ORD_2=;351;301;302;303;310;304;305;306;312;313;331;332;333;334;335;336;337;338;307;308;309;311;361;…;370;
|
||||
KAN_TYP=0
|
||||
```
|
||||
|
||||
**두 목록의 차이가 12번 §7의 「기타경비: 일반=묶음 1개 / 수공=세부 8개」와 정확히 맞는다.**
|
||||
|
||||
- `KAN_ORD_1` (24개) — `313` 다음에 **`321` 하나**
|
||||
- `KAN_ORD_2` (32개) — 같은 자리에 **`331`~`338` 여덟 개**, 그리고 맨 앞에 `351` 추가
|
||||
|
||||
⇒ **`KAN_ORD_1` = 일반형식(`T`) 순서, `KAN_ORD_2` = 수공형식(`S`) 순서**이고 `KAN_TYP`가 고른다.
|
||||
|
||||
### 2.2 코드 → 항목 사전 (봉화 2024 출력과 대조해 확정)
|
||||
|
||||
`KAN_ORD_1` 순서대로 놓고, 값이 0인 항목은 출력에서 빠진다는 규칙으로 봉화 `공사원가계산서`와 맞추면 하나도 어긋나지 않는다.
|
||||
|
||||
| 순서 | 코드 | 항목 | 봉화에 출력됨 |
|
||||
|---:|---|---|:--:|
|
||||
| 1 | `301` | 간접노무비 | ✅ |
|
||||
| 2 | `302` | 산재보험료 | ✅ |
|
||||
| 3 | `303` | 고용보험료 | ✅ |
|
||||
| 4 | `310` | 건강보험료 | ✅ |
|
||||
| 5 | `304` | 노인장기요양보험료 | ✅ |
|
||||
| 6 | `305` | 연금보험료 | ✅ |
|
||||
| 7 | `306` | 퇴직공제부금비 | ✗ (0) |
|
||||
| 8 | `312` | 산업안전보건관리비 | ✅ |
|
||||
| 9 | `313` | 안전·품질관리비 (별도내역) | ✗ (0) |
|
||||
| 10 | `321` | **기타경비** (일반형식 묶음) | ✅ |
|
||||
| 11 | `307` | 공사이행보증수수료 | ✗ (0) |
|
||||
| 12 | `308` | 하도급대금지급보증수수료 | ✗ (0) |
|
||||
| 13 | `309` | 환경보전비 | ✅ |
|
||||
| 14 | `311` | 건설기계대여금 지급보증 | ✅ |
|
||||
| 15~24 | `361`~`370` | 후단 공통 (일반관리비·이윤·부가세·도급·관급 계열) | — |
|
||||
| — | `331`~`338` | **수공형식 세부 경비 8개** | 표본 없음 |
|
||||
| — | `351` | 수공형식 선두 항목 | 표본 없음 |
|
||||
|
||||
출력 순서가 **정확히 이 순서**다. 등급 **A**.
|
||||
|
||||
⇒ **재구현은 항목을 이름이 아니라 코드로 다루고, 순서를 목록 데이터로 둘 것.** 형식을 바꾸면 목록을 갈아 끼우면 된다. `if 항목명 == "기타경비"` 같은 분기를 심으면 수공형식에서 깨진다.
|
||||
|
||||
### 2.3 ⚠ 후보 슬롯 이름이 설치본마다 다르다
|
||||
|
||||
| | 슬롯1 | 슬롯2 | 슬롯3 | 슬롯4 | **슬롯5** | 슬롯6 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 설치 기본 (`Start8x.Ini`) | 가격 정보 | 물가 자료 | 물가 정보 | 거래 가격 | **유통 물가** | 적용 단가 |
|
||||
| 실무 프로젝트 (`INITIAL`) | 조달가격 | 물가자료 | 물가정보 | 거래가격 | **견적단가** | 적용 단가 |
|
||||
|
||||
**다섯 번째가 통째로 다르다.** 07번의 「슬롯 번호에 가격지 이름을 코드로 고정하지 말라」가 이보다 분명할 수 없다. 축약도 다르다(`가.정/물.자/물.정/거.래/유.통` ↔ `조달/물자/물정/거래/견적`).
|
||||
|
||||
`JEMNM=123456`이 표시 순서다.
|
||||
|
||||
### 2.4 코드 종류 사전
|
||||
|
||||
```ini
|
||||
COD_FMT_1=노무 COD_FMT_2=자재 COD_FMT_3=경비 COD_FMT_4=대가
|
||||
COD_FMT_5=중기 COD_FMT_6=산근 COD_FMT_7=기타 COD_FMT_8=실적
|
||||
```
|
||||
|
||||
설계변경증감대비표 비고 칸의 `산근 4호표` · `대가 1호표` · `M00760`이 이 사전을 쓴다. **`대가`=일위대가(`B`), `산근`=단가산출근거(`D`)**다.
|
||||
|
||||
`BCDNM=10 · DCDNM=14 · LCDNM=9 · MCDNM=13 · PCDNM=11 · SCDNM=15 · XCDNM=14`는 코드 종류별 기본값이며 의미는 아직 미확정(B등급).
|
||||
|
||||
### 2.5 `[EQUIPMENT]` — 노임 환산식과 환율의 설치 기본값
|
||||
|
||||
```ini
|
||||
RXXNM=1/8 * 16/12 * 25/20 ← 18번 §2.1의 그 식
|
||||
RXTNM=1/8*16/12*25/20
|
||||
CONLD=L00007L00008L00009 ← 적용 직종 (설치 기본)
|
||||
EXLNM1~4 / EXHNM1~4 = 1088 · 1289 · 1134.5 · 1134.5 ← 달러 구간별 환율
|
||||
EXEUR1/2=1000 EXYEN1/2=1000
|
||||
```
|
||||
|
||||
프로젝트의 `COND_`는 `L00008L00009L00010`으로 **설치 기본과 다르다** — 프로젝트마다 적용 직종을 바꿀 수 있다.
|
||||
|
||||
### 2.6 그 밖
|
||||
|
||||
| 키 | 값 | 뜻 |
|
||||
|---|---|---|
|
||||
| `DISP_EX` | `제외금액` | 16번 §4.2의 `C*` 구역 이름이 여기서 온다 |
|
||||
| `DISP_TOT` | `총 계` | |
|
||||
| `UPDNCAP` | `상단:당초 , 하단:변경` | 변경설계 표시 |
|
||||
| `BMKNM` | `TLMS` | 비목 코드 순서 (총계·노무·재료·경비) |
|
||||
| `[NAG_HAL]` | `ST_MHAL` · `EQ_LHAL` · `EQ_MHAL` · `EQ_SHAL` = 0 | **할증률 설정 자리** — 표본은 전부 0 |
|
||||
| `LMGNM=5.0` · `MKRNM=4.0` · `TMGNM=0.0` | | 미확정 (B) |
|
||||
|
||||
## 3. `SET_REP.DBF` — 보고서 서식 75종 카탈로그
|
||||
|
||||
우리 표본의 XLSX에는 **30종**이 나왔는데, 카탈로그에는 **75종**이 있다. 45종은 표본에 안 나온 것뿐이다.
|
||||
|
||||
| 갈래 | 서식 |
|
||||
|---|---|
|
||||
| 기성 | 기성부분총괄내역서 · 기성내역서 · 기성내역서(사정) · 기성내역서(기성율) · 기성검사조서(을/병) · 기성부분수량조서 |
|
||||
| 견적·계약 | 견적내역서 · 견적단가대비표 · 계약,실행내역서 |
|
||||
| 준공 | 준공조서(을/병) |
|
||||
| 변경 | 변경내역서 · 변경내역서(변경율) · 변경총괄내역서(구.시도/시도/구도) |
|
||||
| 계획 | 장비투입계획 · 인력투입계획 · 자재사용계획 |
|
||||
| 실행예산 | 실행예산서Ⅰ · Ⅱ |
|
||||
| 양식 변형 | 종양식 · 도공양식 · 굴착 · 구.시도/시도/구도 · **수공양식** |
|
||||
| 기타 | 간이공사비 산출명세서 · 총괄집계표 · 표준시장/시장시공 목록표·집계표 |
|
||||
|
||||
각 행은 `INDX`(서식번호) · `NAME` · `HEADTITLE` · 열 이름 11개와 폭을 가진다. **보고서 서식이 코드가 아니라 데이터**라는 00번의 관찰이 여기서 확인된다.
|
||||
|
||||
⚠ **`일위대가표(수공양식)`(`0303`)과 `표준시장/시장시공` 계열이 카탈로그에 있다** — 수공·실적 형식이 실제로 지원되며 서식도 따로 있다. 우리 표본에 그 형식 프로젝트가 없을 뿐이다.
|
||||
|
||||
새 **계산** 규칙은 없다. 같은 값을 다른 묶음·다른 열로 내는 출력 변형이다.
|
||||
|
||||
## 4. `COSTN.SMT` · `QCODE.SMT` · `STini.MEM`
|
||||
|
||||
> ⛔ **이 절의 첫 판정은 틀렸다. 21번 §1에서 정정했다.** 아래는 바로잡은 내용이다.
|
||||
|
||||
| 파일 | 크기 | 상태 |
|
||||
|---|---:|---|
|
||||
| `COSTN.SMT` | 10,177 B | ✅ **평문 — 본문 문자열 276개.** 단가산출 전개 텍스트 |
|
||||
| `PRJLST.SMT` | 2,759 B | ✅ **평문 — 153개.** 프로젝트별 원가계산 설정 원장 |
|
||||
| `QCODE.SMT` | 512 B | 머리만, 내용 없음 |
|
||||
| `STini.MEM` | 512 B | 미해독 |
|
||||
|
||||
처음 훑을 때 **파일 앞 4KB만** 보고 필터를 세게 걸어 놓쳤다. **본문은 `0x200`부터** 시작한다. 07번이 기대한 `&M00034M&` 참조 토큰이 **평문으로 그대로 있다.**
|
||||
|
||||
암호화된 것은 `BDQTY.DBF`·`SYSINFO.DBF` 둘뿐이라는 판정은 그대로 맞다. 상세는 **21번 §1·§3**과 기존 `original/원가계산/STmate/SIxMemo_구조.md`.
|
||||
|
||||
## 5. 이번 점검이 바꾸는 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| 간접비 항목 순서 | 화면 텍스트로 복원 | ✅ **코드 목록으로 확정**, 형식별 두 벌 |
|
||||
| 간접비 항목 코드 | 없었음 | ✅ **301~370 사전**, 봉화 출력과 무결 대조 |
|
||||
| 후보 슬롯 이름 | 「설정에 있을 것」 | ✅ **설치본마다 다름을 실물로 확인** |
|
||||
| 코드 종류 사전 | 추정 | ✅ `COD_FMT_1~8` |
|
||||
| 보고서 서식 | 30종 봄 | ✅ **카탈로그 75종** 전체 |
|
||||
| `*.SMT` 메모 | 「토큰이 있을 것」 | ✅ **평문 — 토큰·설정 원장이 그대로** (21번 §1·§3) |
|
||||
| 할증률 설정 자리 | 없었음 | ◐ `[NAG_HAL]` 발견, 표본은 전부 0 |
|
||||
@@ -0,0 +1,156 @@
|
||||
# 정정과 트랙 통합 — 마지막 확인에서 드러난 것
|
||||
|
||||
작성일: 2026-09-13
|
||||
계기: 「분석이 대부분 끝났나」 최종 확인. **끝나지 않았다.** 오히려 이 확인에서 **내 오류 하나와 큰 구멍 하나**가 나왔다.
|
||||
|
||||
## 1. ⛔ 정정 — `*.SMT` 는 암호화가 아니다. 읽힌다.
|
||||
|
||||
20번 §4에 「`COSTN.SMT`·`QCODE.SMT`·`PRJLST.SMT` 본문에 읽히는 문자열 없음 — 암호화/압축」이라 적었다. **틀렸다.**
|
||||
|
||||
앞선 훑기가 **파일 앞 4KB만** 본 데다 필터가 셌다. 본문은 **`0x200`부터** 시작한다.
|
||||
|
||||
| 파일 | 크기 | 읽히는 문자열 |
|
||||
|---|---:|---:|
|
||||
| `COSTN.SMT` | 10,177 B | **276개** |
|
||||
| `PRJLST.SMT` | 2,759 B | **153개** |
|
||||
| `QCODE.SMT` | 512 B | 1개 (비어 있음) |
|
||||
|
||||
암호화된 것은 **`BDQTY.DBF`와 `SYSINFO.DBF` 둘뿐**이라는 앞선 판정은 그대로 맞다.
|
||||
|
||||
## 2. 큰 구멍 — 지식DB에 STmate 트랙이 **셋**이고 서로 몰랐다
|
||||
|
||||
| 트랙 | 자리 | 담당 | 만든 때 |
|
||||
|---|---|---|---|
|
||||
| ① **계산 로직** | `technical_info/01_임도/05_원가정보/` (문서 10개) | 밑수·단수처리·제비율·기계경비·노임·자재 적용 | 2026-08 |
|
||||
| ② **STC 포맷** | `original/원가계산/STmate/` (문서 8개) | 컨테이너·필드사전·난독화·SIxMemo·INITIAL | 2026-08-15 |
|
||||
| ③ **프로그램 분석** | `original/경쟁사 프로그램/STmate 분석/` (이 폴더) | 실행모듈·폼·계산사슬·골든셋 | 2026-09-12~13 |
|
||||
|
||||
②의 `_meta.md`가 **「계산 로직은 `technical_info/01_임도/05_원가정보/`가 담당, 이 폴더는 포맷만」** 이라고 트랙 분리를 명시해 놓았다. ③을 만들 때 이것을 못 보고 시작했다.
|
||||
|
||||
### 그 결과 겹치거나 어긋난 것
|
||||
|
||||
| 내가 「새로 찾았다」한 것 | 이미 있던 곳 | 판정 |
|
||||
|---|---|---|
|
||||
| 내역 행 절사 · 집계표 사사오입 · 관급 천원 올림 | ①`단수처리_규칙.md` — **5공사지 교차로 이미 확정**. 봉화 216건까지 같은 숫자 | **중복** |
|
||||
| `〓 INITIAL 〓` 시트 | ②`XLSX_INITIAL_구조.md` — 존재·성격 이미 규명. 「셀 전수 추출」만 미착수 | **부분 중복** (전수 추출은 18번이 처음) |
|
||||
| `COSTN.SMT` 토큰 | ②`SIxMemo_구조.md` — **헤더·문법 이미 해독**(`&M00034M&`, `:=A01`, `Q=…`) | **중복이자 내 오판** |
|
||||
| `BDQTY`·`SYSINFO` 암호화 | ②`STC_난독화_상태.md` — **XOR 키스트림 부분 검증까지** 완료 | **중복** |
|
||||
|
||||
**정직하게 말해 ③의 상당 부분이 ①②를 다시 한 것이다.** 다만 ③에서만 나온 것도 분명하다 — 실행모듈 정적분석(은행가 반올림), 폼 복원(밑수 공식·설정 83축·간접비 코드), 골든셋 회귀(원 단위 재현), 변경설계 모드, 단가산출 Q식 전수.
|
||||
|
||||
## 3. ⭐ `PRJLST.SMT` — 요율이 **값으로** 저장돼 있다
|
||||
|
||||
가장 중요한 발견. 프로젝트마다 원가계산 설정이 **INI 형식 평문**으로 들어 있다.
|
||||
|
||||
```ini
|
||||
[KANJUB] JAB_TYPE=0 CAL_ACTL=0 VIS_TOTL=0
|
||||
[1_KANJUB] ← 차수 1
|
||||
[2_KANJUB] ← 차수 2
|
||||
```
|
||||
|
||||
### 3.1 요율 `R_*` — 전부 값으로
|
||||
|
||||
```ini
|
||||
1_R_KANNO=10.8 간접노무비 1_R_SANBO=3.8 산재보험료
|
||||
1_R_KOBO=0.67 고용보험료 1_R_KUNBO=1.4 건강보험료
|
||||
1_R_YUNBO=2.41 연금보험료 1_R_ANJUN=2.48 산업안전보건관리비
|
||||
1_R_T_KITA=6.1 기타경비 1_R_HADOSU=0.018 하도급보증수수료
|
||||
1_R_ILBAN=4.7 일반관리비 1_R_IYUN=15 이윤
|
||||
1_R_KONGSA=0.7 공사보험료 1_R_TBUGUM=0 퇴직공제부금
|
||||
```
|
||||
|
||||
### 3.2 ⭐ 수공형식 세부 경비 8개가 여기 있다
|
||||
|
||||
```ini
|
||||
1_R_S_SUDO 수도광열비 1_R_S_BOKRI 복리후생비
|
||||
1_R_S_SOMO 소모품·사무용품비 1_R_S_TONGSIN 여비·교통비·통신비
|
||||
1_R_S_SEGUM 세금·공과금 1_R_S_PEGI 폐기물처리
|
||||
1_R_S_DOSEO 도서·시공도작성비 1_R_S_JIGUP 지급수수료
|
||||
```
|
||||
|
||||
**`KAN_ORD_2`의 `331`~`338` 여덟 개와 정확히 맞는다**(20번 §2.1). 수공형식 표본이 없어도 **항목 구성은 확정**됐다.
|
||||
|
||||
### 3.3 직접입력 금액 `C_*` — 변경설계 「당초액 유지」의 실제 자리
|
||||
|
||||
```ini
|
||||
1_C_KUNBO=649160 건강보험료 직접입력 금액
|
||||
1_C_YUNBO=1117483 연금보험료 직접입력 금액
|
||||
1_C_ANJUN=1994098 안전관리비
|
||||
1_C_IYUN_M=37182 이윤 보정액 ← 16번 §4.4 끝전조정의 저장 자리
|
||||
1_C_KONGSA_J=119300000
|
||||
```
|
||||
|
||||
**19번 §6에서 「건강·장기요양·연금은 당초액 유지」라 실증한 것의 저장 자리가 이것이다.** 요율이 아니라 **금액을 직접 써 넣는다.**
|
||||
|
||||
### 3.4 선택 인덱스 `I_*` — 폼의 83축과 직결
|
||||
|
||||
```ini
|
||||
1_I_KONG_TYPE=0 공사종류 1_I_KONG_KIKAN=2 공사기간
|
||||
1_I_ANJUN_TYPE=0 안전비 공종 1_I_KUN_TYPE=6 건물종류
|
||||
1_I_JEOLSA=5 공급가액 절사 1_I_TBUGUM=3 퇴직공제 구분
|
||||
1_I_BUGA=0 부가세 방식 1_I_KOBO_TYPE=0 고용보험 등급
|
||||
1_I_MTYP · 1_I_LTYP · 1_I_MXA · 1_I_MXK ← 실적형식 전용 (12번 §10.1-2)
|
||||
```
|
||||
|
||||
12번이 폼에서 뽑은 축 이름(`cmBx_Kong_Type`·`cmBx_AnJun_Type`·`cmBx_JeolSa`…)과 **일대일로 맞는다.**
|
||||
|
||||
### 3.5 ⇒ 요율 좌표(T1)는 재구현 필수가 아니다
|
||||
|
||||
**프로젝트를 읽을 때는 `PRJLST.SMT`의 `R_*` 값을 그대로 쓰면 된다.** 기준표 좌표는 「새 프로젝트를 만들며 기준표에서 고르는」 경로에만 필요하다.
|
||||
|
||||
| 쓰임 | 좌표 필요? |
|
||||
|---|---|
|
||||
| STmate 프로젝트 **읽기·검산** | ❌ — `PRJLST.SMT`에 값이 있음 |
|
||||
| Aislo에서 **새로 요율을 고르기** | ⭕ — 다만 Aislo는 **현행 법령**에서 고르면 됨 |
|
||||
| STmate와 **같은 기준표 선택 재현** | ⭕ — 하지만 목표가 아님 |
|
||||
|
||||
⇒ 13번 T1의 우선순위를 **1위에서 내린다.** 19번 §7의 「규모·기간 의존 요율 좌표」도 같은 이유로 격하한다.
|
||||
|
||||
## 4. 암호화의 성격 — 프로그램이 아니라 **사온 데이터**를 지킨다
|
||||
|
||||
| | 암호화 41개 | 평문 43개 |
|
||||
|---|---|---|
|
||||
| 무엇 | `*.BSC` · `*.CSTA` · `*.STBSC` 단가 자료 | `JB_*` 제비율 요율표 |
|
||||
| 성격 | **유료 구독 데이터** | **법정 공개 자료** |
|
||||
|
||||
암호화된 파일은 **전부 파일명에 출처 기관과 반기가 박혀 있다.**
|
||||
|
||||
```
|
||||
2025(상,국교,해수부,조달청)_표준시장.CSTA 국토부·해수부·조달청 표준시장단가
|
||||
2026(상_노임,기계경비).STBSC 대한건설협회 노임 + 건설기계 경비
|
||||
2026(상_표준일위대가_토목).STBSC 표준품셈 일위대가
|
||||
2026-상-시설공통자재.STBSC 나라장터 시설공통자재
|
||||
JDCODE_M.BSC 19 MB 자재 코드·단가 57,580건
|
||||
```
|
||||
|
||||
**결정적 근거는 후보 슬롯 이름이 국내 물가지 이름 그대로**라는 점이다.
|
||||
|
||||
| 슬롯 | 이름 | 실체 |
|
||||
|---|---|---|
|
||||
| 1 | 조달가격 / 가격 정보 | 조달청 가격정보 |
|
||||
| 2 | **물가자료** | 한국물가협회 |
|
||||
| 3 | **물가정보** | 한국물가정보 |
|
||||
| 4 | 거래가격 | 거래가격지 |
|
||||
| 5 | 유통 물가 / 견적단가 | 유통물가 또는 업체 견적 |
|
||||
|
||||
즉 STmate는 **물가지 네댓 곳에서 사온 단가를 자기 포맷으로 담아 반기마다 배포**하며, 암호화는 **그 데이터의 저작권 보호**다. 프로그램 로직을 숨기려는 것이 아니다 — 계산 모듈(`*.CPM`)은 암호화돼 있지 않고 프로젝트 파일도 대부분 평문이다.
|
||||
|
||||
### Aislo 에 뜻하는 것 — 뚫을 일이 아니라 살 일
|
||||
|
||||
| 무엇 | STmate | Aislo 현재 |
|
||||
|---|---|---|
|
||||
| 계산 규칙 | 암호화 안 됨 | ✅ 다 읽었음 |
|
||||
| 법정 요율(제비율) | 평문 | ✅ 현행법에서 직접 |
|
||||
| 품셈 `q·K·f·E·Cm` | 출력에 평문 | ✅ `건설공사_표준품셈` 보유 |
|
||||
| 노임 | 암호화 배포 | ✅ `노임단가_건설업_대한건설협회` |
|
||||
| 유가·환율 | 암호화 배포 | ✅ `유가_오피넷` · `환율_한국은행ECOS` |
|
||||
| 시설공통자재 | 암호화 배포 | ✅ `자재단가/나라장터_시설공통자재` |
|
||||
| **물가지 자재단가** | 암호화 배포 | ⛔ **구독 필요 — 유일한 실질 격차** |
|
||||
| 표준시장단가 | 암호화 배포 | ◐ 조달청 공표 자료로 대체 가능 |
|
||||
|
||||
## 5. 이 문서 이후 해야 할 것
|
||||
|
||||
1. **①②③ 트랙을 잇는 색인** — 세 폴더 머리에 서로를 가리키는 줄을 넣어 다시 갈라지지 않게 할 것
|
||||
2. ③의 계산 규칙을 ①`technical_info/05_원가정보/`로 **승격** — ①이 Aislo 구현의 정본이므로
|
||||
3. ②의 미해명(`SMT` 10바이트 인덱스, 레코드 경계)과 ③의 성과를 합칠 것
|
||||
4. `COSTN.SMT` 전개 텍스트 **전수 파싱** — ②가 문법을 해독해 뒀으므로 바로 가능
|
||||
@@ -0,0 +1,156 @@
|
||||
# UI 폼 분석 — 화면에 남아 있던 계산 규칙
|
||||
|
||||
점검일: 2026-09-13
|
||||
계기: 「추가 UI 정보도 분석했는지」 확인. **안 했다.** 폼 233개 중 **3개만** 뽑아 썼다.
|
||||
원자료: `30_원자료/ui_form_catalog.txt` · 재현 스크립트: `40_스크립트/ui_form_sweep.py`, `form_extract.py`, `form_catalog.py`
|
||||
|
||||
## 1. 그동안 얼마나 봤나
|
||||
|
||||
| | 개수 |
|
||||
|---|---:|
|
||||
| 폼을 가진 PE 모듈 | 46 |
|
||||
| 폼 자원 (중복 포함) | **233** |
|
||||
| 서로 다른 폼 이름 | **190** |
|
||||
| **그동안 뽑아 쓴 것** | **3** — `TWM_KANJUB`·`TWM_KAN_RATE`·`TPM_KAN_RATE` |
|
||||
|
||||
계산 분석에 필요한 제비율·계산설정 화면만 뽑고 나머지는 손대지 않았다.
|
||||
|
||||
## 2. 이번에 전수로 훑음
|
||||
|
||||
파싱에 성공한 STmate 고유 폼에서 캡션과 선택지를 뽑았다.
|
||||
|
||||
| | 개수 |
|
||||
|---|---:|
|
||||
| 선택지를 가진 폼 | **40** |
|
||||
| **선택축** | **272** |
|
||||
|
||||
12번 §10.1-2가 계산설정 폼 하나에서 뽑은 **83축의 세 배가 넘는다.**
|
||||
|
||||
### 폼 이름 규칙
|
||||
|
||||
```
|
||||
PM_* 인쇄 폼 — SET_REP 의 보고서 서식 75종과 대응
|
||||
w* / wM_* 편집 창·모달
|
||||
frm* 보조 창
|
||||
```
|
||||
|
||||
`ST_P_Nae`(22) · `STmate.Cpm`(30) · `ST_Boq`(13)에 폼이 몰려 있다.
|
||||
|
||||
## 3. 화면에서 새로 나온 계산 규칙
|
||||
|
||||
### 3.1 `wM_Edt_Hal` — 기준단가 수정 (단가 일괄보정)
|
||||
|
||||
```text
|
||||
적용단가 = 기준단가 × [ ] % , 소수 [ ] 자리 미만 [절사 / 반올림]
|
||||
|
||||
※ 단【 적용단가 】가 "0" 원인것과 보호표시된 것은 수정안됨
|
||||
```
|
||||
|
||||
⇒ **일괄변경에서 「보호표시(수동 고정)된 항목과 적용단가 0원인 항목은 건너뛴다.**」 13번 **T10(일괄변경 수동값 우선순위)**의 핵심이 화면 안내문에 적혀 있다. 등급 **A**(화면 문구 기준).
|
||||
|
||||
### 3.2 `wM_Rate` — 비목별 일괄 보정, 절사/반올림 × 소수 0~4위
|
||||
|
||||
`ST_Boq.CPM`의 노무비·재료비·경비·중기·일식 보정 창에 **비목마다 같은 짝**이 있다.
|
||||
|
||||
```text
|
||||
wcBx_Pnt1_M / _L / _X / _W → 0 절사하여 · 1 반올림 하여
|
||||
wcBx_Pnt2_M / _L / _X / _W → 0~4 소수 n 위값을 적용
|
||||
rd_Qty1 / rd_Amt1 / rd_Amt2 → 내역서 전체 · 선택된 항목만
|
||||
```
|
||||
|
||||
⇒ **단수처리가 전역 설정 하나가 아니라 비목(M·L·X·W)마다 따로**다. 09번 §4의 「출력별 정책을 데이터로」가 여기서도 확인된다.
|
||||
|
||||
### 3.3 `wM_Sel_Prn` — `ACTTYP`·`ACTOPT` 의 뜻이 풀렸다
|
||||
|
||||
`PRJLST.ACTTYP`·`ACTOPT`와 `INITIAL`의 `ACTYP`·`ACTOP`가 무엇인지 몰랐다. 화면이 답한다.
|
||||
|
||||
```text
|
||||
cb_ACTTYP 노무비외 비목 적용안함 / 노무비외 재료비로 적용 / 노무비외 경비로 적용
|
||||
cb_ACTOPT 노무비 1원미만 버림 / 소수 비교후 노무비 우선보정 / 소수 비교후 타금액 우선보정
|
||||
```
|
||||
|
||||
**표준시장단가(실적) 적용 방식**이다. 설치 폴더의 파일명과 정확히 대응한다.
|
||||
|
||||
```
|
||||
2025(상_표준시장[노임외 경비적용],국교,해수부).STBSC
|
||||
2025(상_표준시장[노임외 재료비적용],국교,해수부).STBSC
|
||||
```
|
||||
|
||||
봉화 2024는 `ACTYP=1`·`ACTOP=2` → **「노임외 재료비로 적용」 + 「소수 비교후 타금액 우선보정」**이다.
|
||||
|
||||
`cb_PCLASS`(당초/변경/분할설계)와 `cb_CHASU`(1~8차변경)도 여기 있다 — **변경 차수가 최대 8차**까지다. `COSTN`의 접미사 1~4(최대 3차변경)와 다르므로, **차수 저장 구조가 4벌 슬롯만이 아니다.**
|
||||
|
||||
### 3.4 `wM_Set_Sys` — `Start8x.Ini` 의 미확정 키가 풀렸다
|
||||
|
||||
20번 §2.4에서 「`KCDNM`·`ACDNM`·`ZCDNM`·`JCDNM`·`ECDNM`·`BPNNM`은 의미 미확정(B)」이라 적었다. 화면이 답한다.
|
||||
|
||||
```text
|
||||
cmBx_KCD / ACD / JCD / ECD / BPN → 0 자리 · 1 자리 · 2 자리
|
||||
cmBx_ZCD → 2 자리 · 3 자리
|
||||
```
|
||||
|
||||
⇒ **전부 소수 자리수 설정**이다. `Start8x.Ini`의 `KCDNM=1 ACDNM=1 ZCDNM=2 JCDNM=0 ECDNM=0 BPNNM=1`이 그대로 자리수다. 등급 **A**.
|
||||
|
||||
같은 화면에서 두 가지가 더 풀린다.
|
||||
|
||||
```text
|
||||
cmBX_EXR 환율 적용 범위 — 시간당중기사용료 단독적용 / 중기 / 대가 / 산근 / 내역 전체
|
||||
cmBx_Sel_RX 노임 환산 계수 갈래 — 계수 ; 터널구간 / 계수-1 ; 장대터널
|
||||
/ 계수-2 ; 펌프준설선 / 계수-3 ; GRAB준설선 / 계수-4 ; 버켓준설선
|
||||
```
|
||||
|
||||
⇒ `[EQUIPMENT]`의 `RX1NM`~`RX4NM`은 **작업 조건별 노임 환산식 네 벌**이다(18번 §2.1의 `RXTNM`이 기본). `EXRNM`은 환율을 어디까지 적용할지다.
|
||||
|
||||
### 3.5 `wM_KanJub_K` — 부가세 방식이 하나 더 있다
|
||||
|
||||
기성 제잡비 계산서 화면의 부가세 선택지가 **12번 §10.1과 다르다.**
|
||||
|
||||
```text
|
||||
(0) 직접입력 (1) 공급가액의 10% (2) 재료비의 10% (3) 재료비+산출경비의 10%
|
||||
```
|
||||
|
||||
12번이 본 설계용 화면은 `공급가액의 10% / 재료비의 10% / 없음 / 산림조합 형식 / 산림조합-면세품`이었다. **기성 계산은 선택지 집합이 다르다.** 계류보전 프로젝트의 부가세가 10%가 아니었던 것(19번 §6 주석)과 이어질 수 있다.
|
||||
|
||||
`rd_Filter`는 집계 수준을 고르는 축이다 — `소계(+=) 이상 / 계(+==) / 합계(+===) / 총계(+====) / 총합계(+=====)`. **`BOQ11`의 `T*`·`E*` 계층 깊이와 대응**한다(16번 §4.1).
|
||||
|
||||
### 3.6 `wM_Mrg_BSC` — 기준자료 갱신 매칭 규칙
|
||||
|
||||
```text
|
||||
매칭 기준(5단계) 호환규정코드 / 시설코드 / 명칭·규격·단위 / 호환+명+규+단 / 명칭·단위 / 설정안함
|
||||
명칭·단가·조달코드 업데이트 안함(복사처적용) / 업데이트(복사원적용)
|
||||
+ 조달코드는 「복사처 우선」·「복사원 우선」까지 4택
|
||||
```
|
||||
|
||||
⇒ 반기마다 새 기준자료가 오면 **무엇을 키로 잡아 기존 프로젝트 단가를 갱신할지**가 여기서 정해진다. 재구현 시 같은 축이 필요하다.
|
||||
|
||||
### 3.7 `wM_JabPum` · `wQtyList` — 코드 종류 전체 목록
|
||||
|
||||
```text
|
||||
모든단가 · 노무비 · 재료비 · 경비 · 일위대가 · 중기비 · 단가산출 · 견적단가 · 대가,산근 · 그림코드
|
||||
```
|
||||
|
||||
`COD_FMT_1~8`(20번 §2.4)보다 넓다. **`그림코드`**가 따로 있다 — 도면 참조 코드가 단가 체계 안에 있다.
|
||||
|
||||
## 4. 이번 점검이 바꾸는 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| UI 분석 범위 | 폼 3 / 190 | **선택축 272개 전수 카탈로그** |
|
||||
| 일괄변경 수동값 우선순위 (T10) | ⛔ 실행 필요 | ✅ **화면 안내문에 있음** — 보호표시·0원은 제외 |
|
||||
| 비목별 단수처리 | 전역 설정으로 봄 | ✅ **M·L·X·W 각각 절사/반올림 × 소수 0~4위** |
|
||||
| `ACTTYP`·`ACTOPT` | 뜻 모름 | ✅ **표준시장단가 적용 방식·끝전 보정 우선순위** |
|
||||
| `KCDNM` 등 6개 키 | 미확정 (B) | ✅ **소수 자리수 설정** |
|
||||
| `RX1NM`~`RX4NM` | 미상 | ✅ **작업조건별 노임 환산식 4벌** (터널·준설선 계열) |
|
||||
| 변경 차수 | 최대 3차(접미사 1~4)로 봄 | ⚠ **화면은 8차까지** — 저장 구조 재확인 필요 |
|
||||
| 부가세 방식 | 설계용 5택 | ⚠ **기성용은 다른 4택** |
|
||||
|
||||
## 5. 남은 UI 작업
|
||||
|
||||
| 순위 | 할 일 | 왜 |
|
||||
|---:|---|---|
|
||||
| 1 | `PM_*` 인쇄 폼 ↔ `SET_REP` 75종 **대응표** | 어느 서식이 어느 계산을 쓰는지 |
|
||||
| 2 | `wBoq`(398 KB)·`wM_Edit_San`(81 KB) 깊이 파싱 | 내역·단가산출 **편집 규칙** |
|
||||
| 3 | 변경 차수 **8차** 저장 구조 확인 | `COSTN` 접미사 4벌과 어긋남 |
|
||||
| 4 | 272개 선택축 ↔ `PRJLST.SMT` 키 대응 | 설정 저장 자리 완성 |
|
||||
|
||||
**UI 는 「화면 모양」이 아니라 계산 옵션의 원장이다.** 아직 캘 것이 남았다.
|
||||
@@ -0,0 +1,117 @@
|
||||
# `COSTN.SMT` 전수 파싱 — 단가산출 원장
|
||||
|
||||
파싱일: 2026-09-13
|
||||
문법 출처: **`original/원가계산/STmate/SIxMemo_구조.md`** (②트랙, 2026-08-15) — 그쪽이 해독해 둔 문법을 그대로 씀.
|
||||
대상: 맛보기 1건 + 실무 STC 7건의 `COSTN.SMT`
|
||||
재현 스크립트: `40_스크립트/smt_expansion_parse.py`
|
||||
|
||||
## 1. 무엇이 들어 있나
|
||||
|
||||
`COSTN.SMT`는 **단가 하나하나의 산출 전개 텍스트**다. `BDQTY.DBF`가 암호화돼 막힌 자리를 **이 파일이 평문으로 대신 채운다.**
|
||||
|
||||
```text
|
||||
'1. 포설(그레이다 3.6 m)'
|
||||
L=2.9'm', H=0.2'm', D=50 , f=1.0
|
||||
E=0.50 , N=4'회'
|
||||
Cm=0.06*(50/4+50/4.0)+2*0.5=
|
||||
Q=60*L*D*H*E*f/(N*Cm)='㎥/Hr'
|
||||
'노무비' &X00006L& / Q =
|
||||
'재료비' &X00006M& / Q =
|
||||
'경 비' &X00006S& / Q =
|
||||
```
|
||||
|
||||
## 2. 전수 집계
|
||||
|
||||
| 요소 | 검출 |
|
||||
|---|---:|
|
||||
| **자원 참조** `&코드&` | **1,570** |
|
||||
| **품** (수량 + 단위) | **1,460** |
|
||||
| **`Q` 식** | **187** |
|
||||
| **중간 라벨** `:=A01` | **162** |
|
||||
| **할증률** `n'%'` | **92** |
|
||||
|
||||
### 참조되는 자원 종류 — 중기가 압도적
|
||||
|
||||
| 앞글자 | 뜻 | 건수 |
|
||||
|---|---|---:|
|
||||
| **`X`** | **중기** | **1,504** |
|
||||
| `L` | 노무 | 58 |
|
||||
| `M` | 재료 | 7 |
|
||||
| `S` | 경비 | 1 |
|
||||
|
||||
⇒ 단가산출(`D`코드)은 거의 전부 **중기 시간당 사용료를 작업량으로 나누는 계산**이다. 17번 §3(중기 호표)과 18번 §2.2(Q식)가 이 구조의 양 끝이다.
|
||||
|
||||
### 가져오는 성분 — 세 성분을 고루
|
||||
|
||||
| 끝글자 | 뜻 | 건수 |
|
||||
|---|---|---:|
|
||||
| `L` | 노무비 성분 | 548 |
|
||||
| `S` | 경비 성분 | 524 |
|
||||
| `M` | 재료비 성분 | 498 |
|
||||
|
||||
⇒ **중기 한 대를 참조하면 노무·재료·경비 세 성분을 각각 따로 끌어온다.** `&X00006L&` / `&X00006M&` / `&X00006S&`가 한 벌로 나온다. 07번 §1의 「총액 하나로만 저장하면 간접비 산정기초가 달라진다」가 여기서 확인된다.
|
||||
|
||||
## 3. `Q` 식은 한 꼴이 아니다 — 187개, 최소 10가지 꼴
|
||||
|
||||
18번 §2.2는 `Q = 3600·q·K·f·E/Cm` 한 꼴만 봤다. **실제로는 작업 종류마다 다르다.**
|
||||
|
||||
| 꼴 | 횟수 | 쓰임 |
|
||||
|---|---:|---|
|
||||
| `3600*q*K*f*E/Cm` | 56 | 굴삭 계열 (버킷) |
|
||||
| `3600*q*k*E*f/Cm` | 26 | 같은 식, 인자 순서·대소문자만 다름 |
|
||||
| `60*q1*F*E/Cm` | 16 | 분 단위 계열 |
|
||||
| `60*P*f*E/Cm` | 12 | 출력(P) 기반 |
|
||||
| `60*q1*f*E/Cm` | 10 | |
|
||||
| `3600*q*K*f*E1/Cm1` | 4 | 인자에 번호가 붙은 변형 |
|
||||
| `3600*q*K*f*E/Cms` | 4 | |
|
||||
| `60*q*F*E/Cm` · `60*q*f*E1/Cm` | 각 4 | |
|
||||
| `60*L*D*H*E*f/(N*Cm)` | — | 포설 계열 (길이·폭·두께) |
|
||||
|
||||
⇒ **재구현은 `Q` 식을 하드코딩하면 안 된다.** 텍스트에 적힌 식을 그대로 평가하는 경로가 필요하다 — 18번 §2.1의 「좌→우 순차 평가」와 같은 요구다.
|
||||
|
||||
`3600`(초→시간)과 `60`(분→시간) 두 계열이고, `Cm`(사이클 시간)의 단위가 각각 초·분이다.
|
||||
|
||||
## 4. 단위 분포 — 운반 계산 인자가 드러난다
|
||||
|
||||
| 단위 | 횟수 | | 단위 | 횟수 |
|
||||
|---|---:|---|---|---:|
|
||||
| 분 | 216 | | `m/min` | 44 |
|
||||
| 인 | 115 | | `(적재시간)` | 35 |
|
||||
| `km/hr` | 85 | | `(왕복시간)` | 35 |
|
||||
| `km` | 69 | | `hr` | 34 |
|
||||
| `(적하시간)` | 64 | | `sec(135)` | 31 |
|
||||
| `m` | 56 | | `Km` | 29 |
|
||||
|
||||
⇒ **운반(덤프·도자) 계산이 `적재시간 + 왕복시간 + 적하시간`으로 사이클을 쌓는다.** `km/hr`(주행속도)·`km`(운반거리)가 함께 나오므로 `Cm = 적재 + 거리/속도 왕복 + 적하 + 대기` 꼴이다. 임도 설계에서 토공 운반이 큰 몫이라 Aislo에 직접 쓸모가 있다.
|
||||
|
||||
`sec(135)`처럼 **괄호 안에 조건(선회각 등)이 붙는다** — 18번 §2.2의 `Cm = 15 sec (90°)`와 같은 표기다.
|
||||
|
||||
## 5. 할증률 — 92건, 값이 다양
|
||||
|
||||
| 값 | 횟수 | | 값 | 횟수 |
|
||||
|---|---:|---|---|---:|
|
||||
| 2% | 33 | | 24% | 3 |
|
||||
| 3% | 22 | | 8% | 3 |
|
||||
| 5% | 9 | | 52% | 3 |
|
||||
| 1% | 6 | | 11% | 2 |
|
||||
| 9% | 6 | | 0.443% | 2 |
|
||||
|
||||
⇒ **할증은 품목·작업마다 제각각**이며 일괄 적용값이 아니다. `52%`·`24%` 같은 큰 값은 잡재료·공구손료 계열로 보인다(07번 §4의 「금액 가산」). `0.443%` 같은 값은 개별 산정 결과다.
|
||||
|
||||
⚠ `BDQTY`의 `X_HAL` 실값은 여전히 못 보지만, **적용된 할증률 자체는 이 텍스트에 남는다.**
|
||||
|
||||
## 6. 이번 파싱이 바꾸는 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| `COSTN.SMT` | 21번에서 「평문」까지만 | ✅ **전수 파싱** — 참조 1,570·품 1,460·Q식 187 |
|
||||
| 단가산출 참조 구조 | Q식 한 꼴만 봄 | ✅ **중기 참조 1,504건, 세 성분 각각** |
|
||||
| `Q` 식 | 한 꼴로 알았음 | ⚠ **최소 10가지 꼴** — 하드코딩 금지 |
|
||||
| 운반 사이클 | 안 봤음 | ✅ **적재+왕복+적하** 구조와 인자 |
|
||||
| 할증률 | `BDQTY` 암호화로 막힘 | ◐ **적용값 92건은 읽힘** (입력 원장은 여전히 막힘) |
|
||||
|
||||
## 7. 남은 것
|
||||
|
||||
1. 호표 단위로 갈라 **`D`코드 ↔ 전개 텍스트 ↔ 참조 중기 호표**를 잇기 — 지금은 요소별 집계까지만
|
||||
2. `Cm` 식 전수 수집 — 운반 사이클 공식을 꼴별로 정리
|
||||
3. ②트랙의 미해명 **10바이트 메모 인덱스** — 이것이 풀리면 `COSTN.DBF` 행과 메모를 정확히 이을 수 있음
|
||||
@@ -0,0 +1,121 @@
|
||||
# 모듈 구조 전수 — 46개 모듈의 export 와 플러그인 규약
|
||||
|
||||
점검일: 2026-09-13
|
||||
계기: 11번은 **핵심 CPM 12개**만 다뤘다. 나머지 **32개 모듈**이 손대지 않은 채였다.
|
||||
재현 스크립트: `40_스크립트/module_export_sweep.py`
|
||||
|
||||
## 1. 플러그인 규약 — `Calc_Kanjub`
|
||||
|
||||
**46개 모듈 중 31개가 `Calc_Kanjub` 를 내보낸다.** 편집·인쇄·변환 무엇이든 **제비율 재계산을 스스로 부를 수 있는 공통 진입점**이다.
|
||||
|
||||
```text
|
||||
Calc_Kanjub 31개 모듈이 공유
|
||||
```
|
||||
|
||||
⇒ 「어느 화면에서 무엇을 고쳐도 제비율이 다시 돌 수 있다」는 뜻이다. 13번 **T8(UI 명령별 재계산 범위)**이 왜 어려운지가 여기서 보인다 — 재계산 진입점이 한 곳이 아니라 **모듈마다** 있다.
|
||||
|
||||
## 2. ⭐ `DP_Common.CPM` — 계산 공용 라이브러리 73함수
|
||||
|
||||
11번 §4.1은 `CALC_STR`만 언급했다. **실제 함수 집합 전체**는 이렇다.
|
||||
|
||||
### 2.1 식 평가
|
||||
|
||||
```text
|
||||
CALC_STR 식 문자열 평가
|
||||
CALC_STR_PART 부분 평가
|
||||
CALC_STR_URC URC(자원참조) 포함 평가 ← &M00034M& 계열을 푸는 자리
|
||||
GET_URC_TYP 참조 종류 판정
|
||||
GET_VARI_VAL 변수 읽기 SET_VARI_VAL 변수 쓰기 ← :=A01 중간라벨
|
||||
```
|
||||
|
||||
### 2.2 반올림·수치 — 11번 §4.2의 원 함수가 전부 여기 있다
|
||||
|
||||
```text
|
||||
MROUNDUD 반올림 계열 MROUNDUP 올림 MTRUNC 절사
|
||||
MINT 정수화 MFRAC 소수부
|
||||
GET_ROUND_CUT 설정에 따라 위를 고르는 디스패처
|
||||
MSTRTODBL · MSTRTOINT · ISNUM · ISINT · CH_FNTOVAL · FLOATTOSTR_ · EXTTOSTR
|
||||
```
|
||||
|
||||
⇒ **`MFRAC`(소수부)가 새로 나왔다.** 18번 §2.2의 「`Q`를 소수 2자리로 먼저 확정」·17번 §2의 「성분 소계 절사」처럼 **소수부를 따로 다루는 자리**가 실제로 함수로 있다.
|
||||
|
||||
### 2.3 조건·논리
|
||||
|
||||
```text
|
||||
IIF IFI IFO CHK_TRUE_CLSTR COUNT_LOGICAL
|
||||
```
|
||||
|
||||
⇒ 식 문자열에 **조건 분기**가 들어간다. 11번 §4.1의 「선택 토큰(`[+LAB]`·`[+ANJ_B]`)을 옵션에 따라 포함/제거」가 이 함수들로 처리된다.
|
||||
|
||||
### 2.4 문자열·기타
|
||||
|
||||
`ADDBJVAL` · `ADDBSL` · `ADDSPSTR` · `CHANGE_STR` · `COPY_LEFT/RIGHT` · `LEFT_STR` · `RIGHT_STR` · `SEP_STR` · `REPLICA_STR` · `COUNT_STR` · `MPOS` · `NPOS` · `BPOS` · `DIS_FMT` · **`AMTTOHAN`**(금액 한글 변환 — 「금 일억삼천사백이십만원정」) · 파일 조작 20여 개.
|
||||
|
||||
**재구현 지침** — 식 평가기는 `+ - * /` 와 `ROUND` 계열만으로 부족하다. **`IIF`·`IFI`·`IFO` 조건 함수와 `GET_VARI_VAL`/`SET_VARI_VAL` 중간변수**까지 갖춰야 한다.
|
||||
|
||||
## 3. `ST_P_XLS.CPM` — XLSX 입출력 72함수
|
||||
|
||||
| 함수 | 뜻 |
|
||||
|---|---|
|
||||
| `MAKE_XLS` · `SHOW_XLS` · `FREE_XLS` | 엑셀 생성·표시·해제 |
|
||||
| **`CONV_XLS_2_ST`** | **엑셀 → STmate 역등록** |
|
||||
| **`XLS_2_BDREC`** | 엑셀 → `BDQTY` 레코드 |
|
||||
| **`Prn_FMLA_INFO`** | **수식 정보 출력** — 출력 XLSX에 수식이 남는 까닭 |
|
||||
| `MK_BSCBOQ_EXL` · `MK_EXL_BSCBOQ` | 기준 BOQ 양방향 |
|
||||
| `Prn_*_EXL` 약 55개 | `SET_REP` 서식마다 엑셀판 |
|
||||
|
||||
⇒ **엑셀이 단순 출력이 아니라 양방향 교환 경로**다. `〓 INITIAL 〓` 시트(18번 §2.3)가 재등록 메타인 까닭이 `CONV_XLS_2_ST`에 있다.
|
||||
|
||||
⚠ **`XLS_2_BDREC`** — 암호화된 `BDQTY`를 **엑셀에서 만들 수 있다.** 뒤집으면 `BDQTY` 내용이 엑셀 수량산출 양식과 대응한다는 뜻이다.
|
||||
|
||||
## 4. `SX32W.CPM` — 라이선스는 하드웨어 동글
|
||||
|
||||
```text
|
||||
RNBOsproInitialize · RNBOsproFindFirstUnit · RNBOsproQuery · RNBOsproRead
|
||||
RNBOsproGetKeyInfo · RNBOsproGetSubLicense · RNBOsproSetDeveloperCode
|
||||
RNBOsproCheckTerminalService · RNBOsproSetHeartBeat · RNBOsproReleaseLicense …29개
|
||||
```
|
||||
|
||||
**Rainbow(현 Thales) Sentinel SuperPro** API다. `ST_BdSN.CPM`의 `Chk_BD_SN`·`Chk_REG_SN`(시리얼 검사)이 함께 있다.
|
||||
|
||||
⇒ **라이선스가 소프트 키가 아니라 USB 동글**이다. 13번의 통제시험 T1~T11이 왜 「영구 불가」인지가 확정됐다 — 동글이 없으면 실행 자체가 안 된다.
|
||||
|
||||
## 5. 그 밖에 드러난 모듈
|
||||
|
||||
| 모듈 | export | 뜻 |
|
||||
|---|---:|---|
|
||||
| `ST_U_Archive.CPM` | 16 | **STC 압축·해제** — `Chk_STC_Ver`(버전) · `ComP_Data` · `UnPack`/`UnPack1`/`UnPack_No` |
|
||||
| `ST_P_Excav.CPM` | 15 | **굴착 전용 내역 양식** — `Prn_Nae_Ex1_*` · `Ex2_*` 계열. 도로공사 양식 |
|
||||
| `ST_P_Nae.CPM` | 30 | 내역·원가 인쇄 본진 |
|
||||
| `ST_P_List.CPM` | 11 | 목록표 계열 |
|
||||
| **`ST_Jabpum.CPM`** | 6 | **`Edt_Hal_Amt` vs `Edt_Hal_Rate`** — 할증을 **금액과 율 두 갈래**로 편집. 07번 §4의 「수량 할증 ≠ 금액 가산」이 export 이름에 그대로 |
|
||||
| `ST_U_ADL.CPM` | 6 | 온라인 갱신 — `ST_Down_File` · `ST_UP_INFO_R` · `ST_SND_Mail` |
|
||||
| `ST_Com_V.CPM` | 10 | `GET_*_R` 계열 — 모듈 간 자원 조회 규약 |
|
||||
| `ST_Func_C.CPM` | 7 | `SUM_TOT` · `CON_TOT` · `ZERO_TOT` · `PCN_TO_T` · `OPTIMIZ` — **계층 합산** |
|
||||
| `CPMARC`/`CPMUNA` | 4+4 | ZIP 압축·해제 (Info-ZIP) |
|
||||
| `STmexe.Cpm` | 2 | `Chk_Exe` · `Chk_New_Exe` — 자기 갱신 |
|
||||
|
||||
## 6. 이번 점검이 바꾸는 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| 모듈 분석 범위 | 핵심 12개 | **46개 전수 export** |
|
||||
| 식 평가기 함수 집합 | `CALC_STR`만 알았음 | ✅ **73함수** — 조건(`IIF`)·중간변수·`MFRAC`까지 |
|
||||
| 재계산 진입점 | 한 곳으로 봄 | ⚠ **31개 모듈이 `Calc_Kanjub` 보유** — T8이 어려운 까닭 |
|
||||
| 라이선스 | 「없음」까지만 | ✅ **Rainbow Sentinel SuperPro 하드웨어 동글** |
|
||||
| 엑셀 | 단방향 출력으로 봄 | ✅ **양방향** — `CONV_XLS_2_ST` · `XLS_2_BDREC` |
|
||||
| 할증 | 한 갈래로 봄 | ✅ **금액·율 두 갈래** (`Edt_Hal_Amt`/`Edt_Hal_Rate`) |
|
||||
| 변경 차수 8차 의문 | 미해결 | ✅ **해소** — 아래 |
|
||||
|
||||
## 7. 변경 차수 8차 의문 해소
|
||||
|
||||
22번 §3.3에서 「화면은 8차인데 `COSTN` 접미사는 4벌」이라 남겨 둔 것을 확인했다.
|
||||
|
||||
| 근거 | 결과 |
|
||||
|---|---|
|
||||
| `PRJLST.SMT` 차수 섹션 | 표본 7건 모두 **최대 `[1_KANJUB]`·`[2_KANJUB]`** |
|
||||
| DBF 접미사 | `PRJLST`·`BOQ11`·`COSTN` 모두 **1~4 네 벌** |
|
||||
| 바이너리 문자열 | **`1차변경`~`3차변경`만** 존재. 4~8차 없음 |
|
||||
| `BOQ1*` / `BOQ2*` | `BOQ1` 15개 모듈, **`BOQ2`는 0개** |
|
||||
|
||||
⇒ **한 파일이 담는 것은 4벌(원설계 + 3차변경)이 맞다.** 화면의 1~8차는 **분리형으로 파일을 이어 갔을 때 그 파일들을 고르는 인쇄 선택**이다(16번 §4.6의 차수 인계). 저장 구조 의문은 없다.
|
||||
@@ -0,0 +1,149 @@
|
||||
# 설정키 전수와 간접비 식 템플릿 — 설정 사슬 닫기
|
||||
|
||||
점검일: 2026-09-13
|
||||
재현 스크립트: `40_스크립트/settings_key_map.py` · 원자료 `30_원자료/prjlst_smt_keys.txt`
|
||||
|
||||
## 1. `PRJLST.SMT` 저장키 **241종**
|
||||
|
||||
21번 §3에서 일부만 봤던 것을 전수로 세었다. 접두어가 뜻을 가른다.
|
||||
|
||||
| 접두 | 뜻 | 개수 |
|
||||
|---|---|---:|
|
||||
| `C_` | **금액 직접입력** | 62 |
|
||||
| `R_` | **요율** | 39 |
|
||||
| `I_` | **선택 인덱스** | 41 |
|
||||
| `B_` | 켬/끔 | 12 |
|
||||
| `D_` | **밑수** | 26 |
|
||||
| `S_` | **산출액** | 30 |
|
||||
| `ADD_*` | **추가금액** | 22 |
|
||||
| 그 밖 | `JAB_TYPE` · `CAL_ACTL` · `VIS_TOTL` · `Y_KONGSU` | 9 |
|
||||
|
||||
### 1.1 ⭐ `D_` 와 `S_` 가 짝을 이룬다 — 밑수와 결과를 함께 저장
|
||||
|
||||
```text
|
||||
D_KANNO / S_KANNO 간접노무비 D_SANBO / S_SANBO 산재보험료
|
||||
D_KUNBO / S_KUNBO 건강보험료 D_YUNBO / S_YUNBO 연금보험료
|
||||
D_ANJUN / S_ANJUN 안전관리비 D_T_KITA / S_T_KITA 기타경비
|
||||
D_HWAN / S_HWAN 환경보전비 D_ILBAN / S_ILBAN 일반관리비
|
||||
D_HADOSU / S_HADOSU 하도급보증 D_EQIPSU / S_EQIPSU 건설기계보증
|
||||
D_S_SUDO / S_S_SUDO … 수공 세부경비 8종도 같은 짝
|
||||
```
|
||||
|
||||
⇒ **항목마다 「어떤 밑수에 곱했고 얼마가 나왔는지」를 프로젝트에 남긴다.** 09번 §2의 `indirect_result(base_amount, rate, raw_amount, rounded_amount)` 계약이 실제 저장 구조와 맞는다.
|
||||
|
||||
`S_DAMT_L` · `S_DAMT_M` · `S_DAMT_S` · `S_KAMT_M` 은 직접비 성분 합계다.
|
||||
|
||||
### 1.2 고용개선지원비가 4종으로 나뉜다
|
||||
|
||||
```text
|
||||
C_EIS_HOLLAB C_EIS_HOLPAY C_EIS_INCENT C_EIS_SOCINS C_EIS_SUPPAY
|
||||
R_EIS_HOLIR1 R_EIS_HOLIR2 R_EIS_INCENT R_EIS_SOCIR2
|
||||
D_EIS_* / S_EIS_*
|
||||
```
|
||||
|
||||
12번 §3.2가 「일반형식에 별도 묶음이 있다」고만 적은 **고용개선지원비**가 실제로 **휴업수당·장려금·사회보험·지원금 네 갈래**다.
|
||||
|
||||
### 1.3 ⭐ 추가금액 저장 구조
|
||||
|
||||
```text
|
||||
ADD_CNT_1 ~ ADD_CNT_4 단계별 행 수
|
||||
ADD_NAME_2_01 · ADD_UNIT_2_01 · ADD_BIGO_2_01 이름·단위·비고
|
||||
ADD_AMT_2_01_1 ~ ADD_AMT_2_01_5 금액 (단계별 5칸)
|
||||
ADD_AMT_3_01_1 ~ ADD_AMT_3_01_5
|
||||
ADD_BUGA_2_01 부가세 포함 여부
|
||||
```
|
||||
|
||||
`_2_`·`_3_` = **삽입 단계**, `_01` = 행 번호, `_1~5` = **단계별 포함 칸**. 11번 §9가 화면 문자열로 본 「단계 3~6 삽입」이 저장 구조로 확인된다.
|
||||
|
||||
### 1.4 형식 전용 키
|
||||
|
||||
```text
|
||||
실적형식만 I_MTYP · I_LTYP · I_MXA · I_MXK · R_R_DM · R_R_DL · R_R_L · R_R_DMDL · R_R_DML
|
||||
수공형식만 C_S_* / R_S_* / D_S_* / S_S_* (수도광열·복리후생·소모품·통신·세금·폐기물·도서·지급수수료)
|
||||
```
|
||||
|
||||
`R_R_DM`·`R_R_DL`·`R_R_L`·`R_R_DMDL`·`R_R_DML` — **`rGrd_RateB`의 열 이름(`재`·`직노`·`노`·`재+직노`·`재+노`) 그대로**다(02번 §RATE_B). 수공형식 밑수 배분 계수가 여기 저장된다.
|
||||
|
||||
## 2. 간접비 식 템플릿 — 바이너리 전수 **26개**
|
||||
|
||||
11번 §6이 일부만 보여 준 것을 모두 뽑았다. **대괄호는 런타임 치환 변수**다.
|
||||
|
||||
### 2.1 밑수가 다른 기본 꼴
|
||||
|
||||
```text
|
||||
ROUNDDOWN( [T_AMT] * [RAT_1] ) 총액 기반
|
||||
ROUNDDOWN( [LAMT] * [RAT_1] ) 노무 기반
|
||||
ROUNDDOWN( [M_AMT] * [RAT_1] ) 재료 기반
|
||||
ROUNDDOWN( ([L_AMT][+LAB]) * [RAT_1] ) 노무 + 선택토큰
|
||||
ROUNDDOWN( ([T_AMT]-[M_AMT]) * [RAT_1] ) 총액 − 재료 ← 이윤
|
||||
ROUNDDOWN( ([M_AMT]+[L_AMT]+[S_AMT]) * … ) 세 성분 합
|
||||
```
|
||||
|
||||
`[+LAB]`는 **옵션에 따라 켜고 끄는 항**이다 — `DP_Common`의 `CHK_TRUE_CLSTR`가 판정한다(24번 §2.3).
|
||||
|
||||
### 2.2 ⭐ 조건 분기가 식 안에 있다
|
||||
|
||||
```text
|
||||
IF( ([LAMT]+[M_AMT]) … )
|
||||
IF( ([L_AMT]+[M_AMT]) … )
|
||||
```
|
||||
|
||||
⇒ **식 평가기에 `IF`가 필요하다.** 24번 §2.3의 `IIF`·`IFI`·`IFO` 함수가 이것을 처리한다. 사칙연산과 `ROUND` 계열만으로는 재현이 안 된다.
|
||||
|
||||
### 2.3 앞 결과를 밑수로 쓰는 꼴
|
||||
|
||||
```text
|
||||
ROUNDDOWN( [C_KUNBO] * [RAT_1] )
|
||||
```
|
||||
|
||||
**건강보험료의 「직접입력 금액」(`C_KUNBO`)에 요율을 곱한다** — 노인장기요양보험료다. 16번 §3에서 `장기요양 = 건강보험료 × 12.95%`로 실증한 것의 식 원문이며, **밑수가 요율 계산 결과가 아니라 `C_` 직접입력 칸**이라는 점이 19번 §6의 「당초액 유지」와 이어진다.
|
||||
|
||||
### 2.4 복합 보험식 — 세 항을 각각 절사 후 다시 절사
|
||||
|
||||
```text
|
||||
ROUNDDOWN( ROUNDDOWN([LAMT]*[RAT_1]*[RAT_2]/100*1.4, 0)
|
||||
+ ROUNDDOWN([LAMT]*[RAT_KY]*[RAT_3]/100, 0)
|
||||
+ ROUNDDOWN([LAMT]*[RAT_1]*[RAT_2]/100*[RAT_4]/100, 0), 자리 )
|
||||
```
|
||||
|
||||
### 2.5 변경설계 두 꼴
|
||||
|
||||
```text
|
||||
[C_AMT_B] + ROUNDDOWN( [C_AMT_B] * (([C_TAMT_C]-[C_TAMT_B])/[C_TAMT_B]) ) 방법 A
|
||||
[C_AMT_B] + ROUNDDOWN( ([C_TAMT_C]-[C_TAMT_B]) * [RAT_1] ) 방법 B
|
||||
```
|
||||
|
||||
### 2.6 사급자재 VAT 제외
|
||||
|
||||
```text
|
||||
+ [C_SAGUP_C] / 1.1
|
||||
```
|
||||
|
||||
`cmBx_Kwan_Sa`의 `사급자재대/1.1` 선택(12번 §10.1)이 식으로 들어간다.
|
||||
|
||||
## 3. 설정 사슬이 닫혔다
|
||||
|
||||
```text
|
||||
화면 선택축 272개 (22번)
|
||||
↓ 고르면
|
||||
PRJLST.SMT 저장키 241종 (이 문서 §1)
|
||||
↓ C_/R_/I_/B_ 값이
|
||||
간접비 식 템플릿 26개 (§2)의 [RAT_n]·[C_*] 자리에 치환
|
||||
↓ DP_Common 73함수로 평가 (24번 §2)
|
||||
↓ 항목마다 D_(밑수)·S_(결과) 로 되돌아 저장
|
||||
공사원가계산서 출력 (16번에서 원 단위 재현 완료)
|
||||
```
|
||||
|
||||
**입력(화면) → 저장(SMT) → 식(템플릿) → 평가기(함수) → 결과(출력)** 다섯 칸이 모두 이어졌다.
|
||||
|
||||
## 4. 이번 점검이 바꾸는 것
|
||||
|
||||
| 항목 | 이전 | 지금 |
|
||||
|---|---|---|
|
||||
| 프로젝트 저장키 | 일부만 봄 | ✅ **241종 전수**, 접두어 6갈래 |
|
||||
| 밑수 저장 | 안 남는 줄 알았음 | ✅ **`D_`(밑수)·`S_`(결과) 짝으로 저장** |
|
||||
| 추가금액 | 화면 문자열로만 | ✅ **저장 구조** `ADD_AMT_단계_행_칸` |
|
||||
| 고용개선지원비 | 「별도 묶음」 | ✅ **4갈래** (휴업수당·장려금·사회보험·지원금) |
|
||||
| 수공 밑수 배분 | `RATE_B` 표만 | ✅ **`R_R_DM`·`DL`·`L`·`DMDL`·`DML`** 저장키 |
|
||||
| 간접비 식 | 일부 | ✅ **26개 전수** |
|
||||
| 식에 조건 분기 | 몰랐음 | ⚠ **`IF(...)` 존재** — 평가기 필수 요건 |
|
||||
@@ -0,0 +1,61 @@
|
||||
### CPM/EXE export 전수 — 모듈 44개, export 408개(중복포함)
|
||||
11번이 다룬 핵심 12개 말고 **32개 모듈**이 남아 있었음
|
||||
|
||||
CPMARC.CPM 4개 GetZipDllPrivVersion, GetZipDllVersion, ZipDllExec, ___CPPdebugHook
|
||||
CPMUNA.CPM 4개 GetUnzDllPrivVersion, GetUnzDllVersion, UnzDllExec, ___CPPdebugHook
|
||||
DP_Common.CPM 73개 ADDBJVAL, ADDBSL, ADDSPSTR, AMTTOHAN, BPOS, CALC_STR, CALC_STR_PART, CALC_STR_URC, CHANGE_STR, C
|
||||
DP_MinMsg.CPM 3개 GETNEWCODE_NAME, GETSPCHAR, MINIMSG
|
||||
ST_BdSN.CPM 3개 Chk_BD_SN, Chk_BD_USE, Chk_REG_SN
|
||||
✅ ST_Boq.CPM 4개 Add_From_MRG, Calc_Kanjub, Edit_Boq, Edit_Bqm
|
||||
ST_Com_V.CPM 10개 GET_CAL_R, GET_COM_R, GET_IMS_R, GET_MRG_R, GET_PBL_R, GET_PRN_R, GET_SFC_R, GET_STM_R, GET_SYS_
|
||||
ST_EdSan.CPM 3개 Calc_Kanjub, Edit_EQ_SYNC, Edit_SanKun
|
||||
✅ ST_EdiLWi.CPM 2개 Calc_Kanjub, Edit_iLW
|
||||
ST_FindUse.CPM 3개 Calc_Kanjub, Find_BDQ, Find_BOQ
|
||||
ST_Func_C.CPM 7개 CON_TOT, OPTIMIZ, PCN_TO_T, SUM_TOT, VALTOPOS, ZERO_TO, ZERO_TOT
|
||||
ST_Jabpum.CPM 6개 Calc_Kanjub, Edit_Dan, Edit_Strg, Edt_Hal_Amt, Edt_Hal_Rate, View_JabPum
|
||||
✅ ST_KanJub.CPM 5개 Calc_Kanjub, Edit_KanJub, Edit_KanJub_K, Edit_Kan_Rate, Prn_Kan_Rate
|
||||
ST_Merge_Code.CPM 1개 Calc_Kanjub
|
||||
ST_PList.CPM 3개 Calc_Kanjub, Call_MRG, Call_PLIST
|
||||
✅ ST_P_Collect.CPM 5개 Calc_Kanjub, Prn_Col_Dan, Prn_Col_Dan_L, Prn_Col_X, Prn_Col_X_L
|
||||
✅ ST_P_ConEst.CPM 9개 Calc_Kanjub, Prn_Jab_K, Prn_Nae_C1, Prn_Nae_C2, Prn_Nae_C3, Prn_Nae_Est, Prn_Nae_K1, Prn_Nae_K2,
|
||||
ST_P_Excav.CPM 15개 Calc_Kanjub, Prn_Nae_Ex1_0, Prn_Nae_Ex1_1, Prn_Nae_Ex1_2, Prn_Nae_Ex1_C1, Prn_Nae_Ex1_C2, Prn_Na
|
||||
✅ ST_P_Joong.CPM 7개 Calc_Kanjub, Prn_Joong, Prn_Joong2, Prn_Joong2_L, Prn_Joong_C, Prn_Joong_C_L, Prn_Joong_L
|
||||
ST_P_List.CPM 11개 Calc_Kanjub, Prn_AList, Prn_Comp_M, Prn_Comp_M1, Prn_Comp_M1_L, Prn_Dan, Prn_Dan_L, Prn_SanMok,
|
||||
ST_P_Nae.CPM 30개 Calc_Kanjub, Prn_Boq_Not, Prn_Bqm, Prn_Jab, Prn_Jab_P, Prn_Nae, Prn_NaeW, Prn_NaeW_P, Prn_Nae_B,
|
||||
ST_P_San.CPM 3개 Calc_Kanjub, Prn_San_L, Prn_San_P
|
||||
ST_P_XLS.CPM 72개 CONV_XLS_2_ST, Calc_Kanjub, Chk_Xls_File, FIND_PRT_INDX, FREE_XLS, MAKE_XLS, MK_BSCBOQ_EXL, MK_E
|
||||
ST_P_iLWi.CPM 3개 Calc_Kanjub, Prn_iLWi, Prn_iLWi_S
|
||||
ST_Prt.CPM 4개 Calc_Kanjub, GS_Prn_Rec, Prn_Prt, Rep_Print
|
||||
✅ ST_SanCal.CPM 2개 Calc_Kanjub, Calq_San
|
||||
ST_Sys.CPM 2개 Calc_Kanjub, Set_Sys_1
|
||||
ST_U_ADL.CPM 6개 Get_Real_Time, ST_Down_File, ST_NEW_INFO, ST_SND_Mail, ST_UP_DNINFO, ST_UP_INFO_R
|
||||
ST_U_Archive.CPM 16개 Calc_Kanjub, Chk_ArcName, Chk_Data, Chk_STC_Ver, Chk_STDir, ComP_CurBOQ, ComP_Data, ComP_FOLD, C
|
||||
✅ ST_U_ChgAmt.CPM 2개 CHANGE_AMT, Calc_Kanjub
|
||||
✅ ST_U_ChgCode.CPM 10개 Calc_Kanjub, Chg_Code_All, Chg_Code_Plus, Chg_Code_Sel, Chk_Chg_Code_1, Chk_True_Rep_Code, Exec_
|
||||
✅ ST_U_ConBD.CPM 4개 CONBD_USD_QTY, CON_QTY, CON_QTY9, Calc_Kanjub
|
||||
✅ ST_U_Convert.CPM 20개 Appn_Tag_REP, CHK_K_FLD, Calc_Kanjub, Conv_BOQ, Conv_Equip, Conv_Jodal_ST8, Conv_ST8_Jodal, Conv
|
||||
ST_U_DelCode.CPM 6개 Calc_Kanjub, Chk_True_Del_1, Del_Code_A_T, Del_Code_A_X, Del_Code_Sel, Exec_Delete_Code
|
||||
ST_U_DelPrj.CPM 3개 Calc_Kanjub, Delete_Prj, Merge_Prj
|
||||
ST_U_Indx.CPM 2개 Calc_Kanjub, INDX_ALL
|
||||
ST_U_Merge.CPM 2개 Calc_Kanjub, ST_MERGE
|
||||
ST_VSetup.EXE 2개 _MainWndProc@16, _StubFileWrite@12
|
||||
STmate.Cpm 1개 Calc_Kanjub
|
||||
✅ STmateQty8.CPM 4개 Calc_Kanjub, FIND_QCODE, Free_Qty, ST_QTY
|
||||
STmexe.Cpm 2개 Chk_Exe, Chk_New_Exe
|
||||
SX32W.CPM 29개 RNBOsproActivate, RNBOsproCheckTerminalService, RNBOsproCleanup, RNBOsproDecrement, RNBOsproEnum
|
||||
UNWISE.EXE 4개 _ItemDlg@16, _MainWndProc@16, _PromptDlg@16, _SharedDlg@16
|
||||
View_Old_San.CPM 1개 Show_Old_San
|
||||
|
||||
### 여러 모듈이 공유하는 export (플러그인 공통 규약)
|
||||
Calc_Kanjub 31개 모듈
|
||||
___CPPdebugHook 2개 모듈
|
||||
Prn_Boq_Not 2개 모듈
|
||||
Prn_Nae_ColL 2개 모듈
|
||||
Prn_Nae_ColM 2개 모듈
|
||||
Prn_Nae_ColX 2개 모듈
|
||||
Prn_Nae_Cont1 2개 모듈
|
||||
Prn_Nae_Cont2 2개 모듈
|
||||
_MainWndProc@16 2개 모듈
|
||||
GetZipDllPrivVersion 1개 모듈
|
||||
GetZipDllVersion 1개 모듈
|
||||
ZipDllExec 1개 모듈
|
||||
@@ -0,0 +1,95 @@
|
||||
### PRJLST.SMT 저장키 241종 (접두 차수 제거)
|
||||
|
||||
■ C_ 금액 직접입력 62개
|
||||
C_ANJUN C_ANJUN1_G C_ANJUN2_G C_ANJUNP C_ANJUN_B
|
||||
C_ANJUN_G C_BUGA_M C_BUSAN C_BUSAN_B C_DOSEOH
|
||||
C_EIS_HOLLAB C_EIS_HOLPAY C_EIS_INCENT C_EIS_SOCINS C_EIS_SUPPAY
|
||||
C_EQIPSU C_EXCEPT_B C_HADOSU C_HWAN C_ILBAN
|
||||
C_IYUN_M C_KANNO C_KAN_M C_KN_SA C_KOBO
|
||||
C_KONGSA C_KONGSA_J C_KONGSU C_KONGSU_A C_KONGSU_MA
|
||||
C_KUNBO C_KWANGUP C_KWANGUP_D C_KWANGUP_K C_NBUGAM
|
||||
C_PSAMT_B C_PUMJIL_B C_PURCHAS C_SAGUP C_SANBO
|
||||
C_SANGSI C_SLVBO C_S_ANJUN_B C_S_BOKRI C_S_DOSEO
|
||||
C_S_JIGUP C_S_PEGI C_S_PUMJIL C_S_SEGUM C_S_SIGONG
|
||||
C_S_SOMO C_S_SUDO C_S_TONGSIN C_TBUGUM C_T_ANJUN_N
|
||||
C_T_JODAL C_T_KITA C_T_KOLJAE C_T_K_MAT C_T_PEGI
|
||||
C_UPDNKWN C_YUNBO
|
||||
|
||||
■ R_ 요율 39개
|
||||
R_ANJUN R_ANJUN1 R_ANJUN2 R_ANJUNP R_DOSEOH
|
||||
R_EIS_HOLIR1 R_EIS_HOLIR2 R_EIS_INCENT R_EIS_SOCIR2 R_EQIPSU
|
||||
R_HADOSU R_HWAN R_ILBAN R_IYUN R_KANNO
|
||||
R_KOBO R_KONGSA R_KONGSU R_KUNBO R_R_DL
|
||||
R_R_DM R_R_DMDL R_R_DML R_R_L R_SANBO
|
||||
R_SLVBO R_S_BOKRI R_S_DOSEO R_S_JIGUP R_S_PEGI
|
||||
R_S_SEGUM R_S_SIGONG R_S_SIGONG_1 R_S_SOMO R_S_SUDO
|
||||
R_S_TONGSIN R_TBUGUM R_T_KITA R_YUNBO
|
||||
|
||||
■ I_ 선택 인덱스 41개
|
||||
I_ANJUNAUTO I_ANJUN_B I_ANJUN_K I_ANJUN_TYPE I_ANPUM
|
||||
I_AN_DN_TYPE I_BF_CH_BO I_BID_TYPE I_BUGA I_COST_BO
|
||||
I_DOGUP I_DOG_JEOL I_EQIP I_HWAN I_ILBAN
|
||||
I_IYUN I_JEOLSA I_KAN_JEOLSA I_KN_SA_TYPE I_KOBO_TYPE
|
||||
I_KONG_KIKAN I_KONG_TYPE I_KOSU_TYPE I_KUNBO I_KUN_TYPE
|
||||
I_KWN_COMB I_KWN_JEOLSA I_KWN_K_JEOLSA I_KWN_K_UPDN I_KWN_UPDN
|
||||
I_LTYP I_MTYP I_MXA I_MXK I_NAG_RATE
|
||||
I_POS_SA I_SET_BASE I_TBUGUM I_TOT_ADD I_TOT_JEOL
|
||||
I_YUNBO
|
||||
|
||||
■ B_ 켬/끔 12개
|
||||
B_ANJUN_B B_BUGA_UP B_BUSAN_B B_CHANGE B_EXCEPT_B
|
||||
B_KONGSA B_KWANGUP_D B_KWANGUP_K B_NBUGAM B_PSAMT_B
|
||||
B_PUMJIL_B B_T_JODAL
|
||||
|
||||
■ Y_ 연도·기타 1개
|
||||
Y_KONGSU
|
||||
|
||||
■ 기타_ 섹션·기타 86개
|
||||
ADD_AMT_2_01_1 ADD_AMT_2_01_2 ADD_AMT_2_01_3 ADD_AMT_2_01_4 ADD_AMT_2_01_5
|
||||
ADD_AMT_3_01_1 ADD_AMT_3_01_2 ADD_AMT_3_01_3 ADD_AMT_3_01_4 ADD_AMT_3_01_5
|
||||
ADD_BIGO_2_01 ADD_BIGO_3_01 ADD_BUGA_2_01 ADD_CNT_1 ADD_CNT_2
|
||||
ADD_CNT_3 ADD_CNT_4 ADD_NAME_2_01 ADD_NAME_3_01 ADD_UNIT_2_01
|
||||
ADD_UNIT_3_01 CAL_ACTL D_ANJUN D_ANJUNP D_EIS_HOLLAB
|
||||
D_EIS_INCENT D_EIS_SOCINS D_EIS_SUPPAY D_EQIPSU D_HADOSU
|
||||
D_HWAN D_ILBAN D_KANNO D_KOBO D_KONGSA
|
||||
D_KONGSU D_KUNBO D_SANBO D_SLVBO D_S_BOKRI
|
||||
D_S_DOSEO D_S_JIGUP D_S_PEGI D_S_SEGUM D_S_SIGONG
|
||||
D_S_SOMO D_S_SUDO D_S_TONGSIN D_TBUGUM D_T_KITA
|
||||
D_YUNBO JAB_TYPE S_ANJUN S_ANJUNP S_DAMT_L
|
||||
S_DAMT_M S_DAMT_S S_EIS_HOLLAB S_EIS_INCENT S_EIS_SOCINS
|
||||
S_EIS_SUPPAY S_EQIPSU S_HADOSU S_HWAN S_ILBAN
|
||||
S_KAMT_M S_KANNO S_KOBO S_KONGSA S_KONGSU
|
||||
S_KUNBO S_SANBO S_SLVBO S_S_BOKRI S_S_DOSEO
|
||||
S_S_JIGUP S_S_PEGI S_S_SEGUM S_S_SIGONG S_S_SOMO
|
||||
S_S_SUDO S_S_TONGSIN S_TBUGUM S_T_KITA S_YUNBO
|
||||
VIS_TOTL
|
||||
|
||||
|
||||
### 간접비 식 템플릿 — 바이너리에서 전수
|
||||
서로 다른 템플릿 26개 (상위 30)
|
||||
ROUNDDOWN( [T_AMT] * [RAT_1] 62
|
||||
ROUNDDOWN( [LAMT] * [RAT_1] 31
|
||||
ROUNDDOWN( ([L_AMT][+LAB]) * [RAT_1] 31
|
||||
[C_AMT_B]+ROUNDDOWN( [C_AMT_B] * (([C_TAMT_C]-[C_TAMT_B])/[C_TAMT_B]) 31
|
||||
(([LAMT][+LAB])- 31
|
||||
[C_AMT_B]+ROUNDDOWN( ([C_TAMT_C]-[C_TAMT_B]) * [RAT_1] 31
|
||||
([LAMT][+LAB]) 31
|
||||
ROUNDDOWN( [C_KUNBO] * [RAT_1] 31
|
||||
ROUNDDOWN( ([LAMT][+LAB]) * [RAT_1] 31
|
||||
[LAMT][+LAB] 31
|
||||
([LAMT]+[M_AMT] 31
|
||||
IF(([LAMT]+[M_AMT]) 31
|
||||
([LAMT]+[M_AMT])) 31
|
||||
([L_AMT]+[M_AMT]) 31
|
||||
IF(([L_AMT]+[M_AMT]) 31
|
||||
([L_AMT]+[M_AMT])) 31
|
||||
ROUNDDOWN( ([T_AMT]-[M_AMT]) * [RAT_1] 31
|
||||
ROUNDDOWN( ROUNDDOWN([LAMT]*[RAT_1]*[RAT_2]/100*1.4 31
|
||||
0) + ROUNDDOWN([LAMT]*[RAT_KY]*[RAT_3]/100 31
|
||||
0) + ROUNDDOWN([LAMT]*[RAT_1]*[RAT_2]/100*[RAT_4]/100 31
|
||||
ROUNDDOWN( ([M_AMT]+[L_AMT]+[S_AMT] 31
|
||||
ROUNDDOWN([T_AMT] * [RAT_1] 31
|
||||
ROUNDDOWN([M_AMT] * [RAT_1] 31
|
||||
+[C_SAGUP_C]/1.1 31
|
||||
[C_SAGUP_C]/1.1 31
|
||||
ROUNDDOWN([TAMT] * [RATE] 3
|
||||
@@ -0,0 +1,335 @@
|
||||
### 파싱 성공한 STmate 고유 폼 150개
|
||||
|
||||
■ PM_Prn_Ord ST_Prt.CPM «보고서 파일의 인쇄순서를 지정합니다.»
|
||||
cBx_BTyp 당초 설계, 변경 설계, 분할 설계, 실적 공사, 할증 내역, 견적 내역, 계약 내역, 실행 예산, 기성 내역, 설계 변경, 굴착/복구, 굴착 변경
|
||||
■ PM_Prt ST_Prt.CPM «설계예산시스템 인쇄메뉴»
|
||||
cmBx_FntTyp 굴림체, 궁서체, 바탕체, 돋움체
|
||||
cmBx_BigoFld 코드번호 인쇄, 번호(호표) 인쇄, 분류번호 인쇄, 시설코드 인쇄, 호환규정코드, 적용기준일, 수공분류코드
|
||||
cmBx_CodeFld 코드번호 인쇄, 번호(호표) 인쇄, 분류번호 인쇄, 시설코드 인쇄, 호환규정코드
|
||||
cmBx_PosQty 수량 / 단위, 단위 / 수량, 표 준
|
||||
cmBx_BigoFld2 코드번호 인쇄, 번호(호표) 인쇄, 분류번호 인쇄, 시설코드 인쇄, 호환규정코드, 적용기준일, 수공분류코드
|
||||
cmBx_SHTyp 일반양식 인쇄, 토개공양식 인쇄, 수자원공사양식 인쇄
|
||||
cmBx_SanLP 기본방향(세로) 인쇄, 횡 방향(가로) 인쇄
|
||||
cmBx_SanMOK 일반양식 인쇄, 산근양식 인쇄
|
||||
■ PM_Sel_St ST_Prt.CPM «인쇄할 공종을 선택하세요»
|
||||
rd_Filter 소 계(+=) 이상, 계(+==) 이상, 합 계(+===) 이상, 총 계(+====) 이상, 총합계(+=====
|
||||
Rg_Bk 아래로, 위로
|
||||
■ mMinMsg_ DP_MinMsg.CPM «종류»
|
||||
ListBox1 기호1, 기호2, 영어/숫자, 한글자모, 그리스어, 선문자, 단위기호, 원문자, 괄호문자, 히라가다, 가다가나, 러시아어, 특수외국어, 분수/첨자
|
||||
■ uM_ConstType ST_U_Convert.CPM «건축공사 사업내용《 건축공사 중 신축 또는 신축에 준하»
|
||||
cn_Cn_Code1 서울특별시; 11, 부산광역시; 26, 대구광역시; 27, 인천광역시; 28, 광주광역시; 29, 대전광역시; 30, 울산광역시; 31, 세종특별자치시; 36
|
||||
cn_Cn_Code2 서울특별시; 11, 부산광역시; 26, 대구광역시; 27, 인천광역시; 28, 광주광역시; 29, 대전광역시; 30, 울산광역시; 31, 세종특별자치시; 36
|
||||
cn_Bd_Type 일반청사; 연면적 10,000㎡ 미만 공용청사(경찰서, 우체국 포함), 대형청사; 연면적 10,000㎡ 이상 공용청사(경찰서, 우체국 포함), 일반형초중고;
|
||||
cn_Dm_Const 철거공사 포함; 기존 부지에 있는 시설물을 철거하고 공사, 철거공사 미포함; 기존/신축 부지에 시설물만 공사
|
||||
cn_Cl_Area 비혼잡지역(일반); 공사와 관련된 현장주변의 장애요인이 적고 공사수행환경이 복잡하지 않은 지역, 혼잡지역(특수); 공사에 따른 민원발생 소지가 높고, 특수지반
|
||||
cn_Gr_Shape 일반지반; 터파기 등 기초공사 수행에 어려움이 없는 지반, 특수지반;
|
||||
cn_Sh_Const 흙막이공사가 없는 경우, 흙막이공사가 있는 경우
|
||||
cn_St_Type 철골조; 주요 구조부가 철골조로 구성된 경우, 철골철근콘크리트조; 주요 구조부가 철골철근콘크리트로 구성된 경우, 철근콘크리트조; 주요 구조부가 철근콘크리로 구
|
||||
■ uM_Merge ST_U_Merge.CPM «데이타 병합 【 복사처 <= 복사원 】»
|
||||
Rdo_BD 각 기준에 따라 설정, 일식단가(W코드)로 생성
|
||||
Rdo_X 일위대가 기준대로 처리, 일식단가(W코드)로 생성
|
||||
RdoL 복사처적용 (동일항목 코드치환), 복사원적용 (동일항목 내용복사), 신규등록 (단가다른코드 신규생성)
|
||||
RdoM 복사처적용 (동일항목 코드치환), 복사원적용 (동일항목 내용복사), 신규등록 (단가다른코드 신규생성)
|
||||
RdoS 복사처적용 (동일항목 코드치환), 복사원적용 (동일항목 내용복사), 신규등록 (단가다른코드 신규생성)
|
||||
RdoB 복사처적용 (동일항목 코드치환), 복사원적용 (동일항목 내용복사), 신규등록 (단가다른코드 신규생성)
|
||||
RdoD 복사처적용 (동일항목 코드치환), 복사원적용 (동일항목 내용복사), 신규등록 (단가다른코드 신규생성)
|
||||
RdoW 복사처적용 (동일항목 코드치환), 복사원적용 (동일항목 내용복사), 신규등록 (단가다른코드 신규생성)
|
||||
■ uM_XML_Info ST_U_Convert.CPM «공사원가호환규정 포맷(XML) 변환»
|
||||
ed_PrjTyp 토목공사, 건축공사, 설비공사, 전기공사, 통신공사, 조경공사, 소방공사, 국가유산수리공사
|
||||
■ wBoq ST_Boq.CPM «공종목록표»
|
||||
cBox01 = 이상, == 이상, === 이상, ==== 이상 , ===== 이상, ====== 이상
|
||||
RGrpTo 내역서, 열린 보관함
|
||||
cBox02 = 이상, == 이상, === 이상, ==== 이상 , ===== 이상, ====== 이상
|
||||
■ wBoqRate ST_Boq.CPM «할증율 적용:»
|
||||
Rdo 전체(재,노,경 같은 비율), 비목별(재,노,경 각각)
|
||||
■ wExpnd ST_Boq.CPM «일위대가표 풀기»
|
||||
RGrpToT 소계(=), 계(==), 합계(===)
|
||||
CMBoxUpDn 절삭하여, 반올림하여
|
||||
CMBoxDot 소수 4위값을 적용, 소수 3위값을 적용, 소수 2위값을 적용, 소수 1위값을 적용, 소수 0위값을 적용
|
||||
■ wITNum ST_Boq.CPM «내역서의 공종번호 자동입력»
|
||||
RdoFirstChar 공란처리, . 입력, ( 입력, - 입력, 추가입력
|
||||
RdoLastChar 공란처리, . 입력, ) 입력
|
||||
CBoxType 가..하,거..허 ~ 그..흐,기..히 (14x10=140개 까지만 처리), a .. z,a1..z1 ~ a8..z8,a9..z9 (26x10=260개 까
|
||||
CBoxLeft 여백 0 Byte, 여백 1 Byte, 여백 2 Byte, 여백 3 Byte, 여백 4 Byte, 여백 5 Byte, 여백 6 Byte, 여백 7 Byte,
|
||||
Fmt_Typ 0, 00, 000
|
||||
cBx_itNum_B 『직전공종(계표시)의 공종번호』, 『직전공종길이 여백으로 채움』
|
||||
cBx_OnlyIT 공종(계표시) / 단가코드 모두적용., 공종(계표시)에만 구분적용., 단가코드에만 구분적용.
|
||||
cBx_Bl_Lev 하위 전체공종 반영, 9레벨(=========) 미만에서 초기화, == 레벨 미만에서 초기화, === 레벨 미만에서 초기화,
|
||||
■ wKongJong ST_Boq.CPM «공종 목록표»
|
||||
RdoLevel 소계 (+=), 계 (+==), 합계 (+===), 총계 (+====), 총합계(+=====), 총대계(+======)
|
||||
RdoFirstChar - 입력, 없슴
|
||||
RdoIndex 코드번호 순, 명 칭 순, 분류번호 순
|
||||
RdoFilt 일위대가/산근전체, 대공종 해당목록만
|
||||
■ wMBoq ST_Boq.CPM «주공종 집계표(계표시 확인)»
|
||||
CBox01 1레벨( 소 계 ) 이상, 2레벨( 계 ) 이상, 3레벨( 합 계 ) 이상, 4레벨( 총 계 ) 이상, 5레벨(총 합 계) 이상, 6레벨(총 대
|
||||
■ wM_Boq_ExecX ST_Boq.CPM «시간/단위당 중기실행단가 계산 및 입력»
|
||||
wCm_Jeol 1원, 10원, 100원, 1,000원, 10,000원, 100,000원
|
||||
wCm_OPT 기본보정, 노 무 비, 재 료 비, 경 비
|
||||
■ wM_Box_H STmate.Cpm «불러간 목록»
|
||||
RG_MComp 변동없음, 1 단가, 2 단가, 3 단가, 4 단가, 5 단가, 최소단가
|
||||
■ wM_Boxa STmate.Cpm «단가목록표»
|
||||
RG_MComp 변동없음, 1 단가, 2 단가, 3 단가, 4 단가, 5 단가, 최소단가
|
||||
rg_FLT_M_PP 모 두 보 임, 10 : 새들분수전, 11 : K.P 자재, 12 : 엘보, 13 : 직관, 14 : 소켓, 15 : S.T.S 자재, 16 : 니플, 17
|
||||
rg_FLT_B_PP 모 두 보 임, 30 : 토공, 31 : 포장공, 32 : 포장깨기/절단, 30~32 : 토공사, 40 : 분기공, 41 : 관접합, 42 : 관연결, 43
|
||||
■ wM_Conv_A2W STmate.Cpm «실적단가를 일식단가로 변환등록»
|
||||
wcBx_SelJOD 비목율( 단가 => 노,재,경 ), 조달청 발표단가 변환적용
|
||||
cBx_Stndard 공종(실적)코드 ↔ 분류번호로 연결, 공종(실적)코드 ↔ 시설코드로 연결, 명칭, 규격, 단위 로 연결
|
||||
■ wM_Conv_Mode ST_PList.CPM «설계자료 → 계약자료로 변환등록»
|
||||
cBx_Sel_QTY 당초 수량/단가 적용, 변경 수량/단가 적용
|
||||
cBx_Lev 소계 이상, 계 이상, 합계 이상, 총계 이상, 총합계 이상, 총대계 이상, 광역계 이상, 광역총계이상, 광역대계이상
|
||||
■ wM_Edit_EQ ST_EdSan.CPM «표준중기공식 편집.»
|
||||
cBx_INS_X 공 식, 공식+코드
|
||||
■ wM_Edit_San ST_EdSan.CPM «단가산출근거 수정[ D00001: 무근콘크리트 깨기 ]»
|
||||
RdosmMode 열블럭, 행블럭, 일 반
|
||||
cBoxFunc 각종 지원함수, ∏, INT(0), SIN(0), COS(0), TAN(0), SQRT(1) , √(1), ROUND(1,0), ROUNDDOWN(1,0),
|
||||
cBox_Trans 코드 * 수량(0) =, 명칭 코드 * 수량(0) =, 명칭 규격 코드 * 수량(0) =, 명칭 규격 코드 * 수량(0) 단위 =, 코드번호
|
||||
ComOp /, *
|
||||
ComQty Q, A, T, N
|
||||
cmBx_IFORCE XML비호환 메세지 숨김, XML비호환시 보이기
|
||||
cBx_SBMOK 0 각 해당비목 적용, 1 재료비로 적용, 2 노무비로 적용, 3 경 비로 적용
|
||||
■ wM_Edit_XRate ST_EdiLWi.CPM «시간당 손료계수 수정»
|
||||
cBx_Sel 계 입력, 각계수 입력
|
||||
■ wM_Edit_iLWi ST_EdiLWi.CPM «일위대가표 수정»
|
||||
wcBx_RXX 초기값설정 개별적용, 기 본 노임산출계수, 기 타 노임산출계수, 기타-1노임산출계수, 기타-2노임산출계수, 기타-3노임산출계수, 기타-4노임산출계수
|
||||
cBx_SBMOK 0 각 해당비목 적용, 1 재료비로 적용, 2 노무비로 적용, 3 경 비로 적용
|
||||
■ wM_Edt_Hal ST_Jabpum.CPM «기준단가 수정»
|
||||
wcBx_Up 절사, 반올림
|
||||
■ wM_Expnd ST_Boq.CPM «일위대가표 및 단가산출근거 풀어넣기»
|
||||
wcBx_Pnt1_M 0 절사하여, 1 반올림 하여
|
||||
wcBx_Pnt2_M 0 소수 0위값을 적용, 1 소수 1위값을 적용, 2 소수 2위값을 적용, 3 소수 3위값을 적용, 4 소수 4위값을 적용
|
||||
wcBx_Pnt1_L 0 절사하여, 1 반올림 하여
|
||||
wcBx_Pnt2_L 0 소수 0위값을 적용, 1 소수 1위값을 적용, 2 소수 2위값을 적용, 3 소수 3위값을 적용, 4 소수 4위값을 적용
|
||||
wcBx_Pnt1_X 0 절사하여, 1 반올림 하여
|
||||
wcBx_Pnt2_X 0 소수 0위값을 적용, 1 소수 1위값을 적용, 2 소수 2위값을 적용, 3 소수 3위값을 적용, 4 소수 4위값을 적용
|
||||
wcBx_Pnt1_W 0 절사하여, 1 반올림 하여
|
||||
wcBx_Pnt2_W 0 소수 0위값을 적용, 1 소수 1위값을 적용, 2 소수 2위값을 적용, 3 소수 3위값을 적용, 4 소수 4위값을 적용
|
||||
■ wM_Expnd10 ST_Boq.CPM «일위대가표 풀기»
|
||||
RGrpToT 입력안함, 소계(=), 계(==), 합계(===)
|
||||
Add1_PRIN 재료비의, 노무비의, 경비의, 전체의
|
||||
Add1_PROUT 재료비로 계산, 노무비로 계산, 경비로 계산, 해당비목 계산
|
||||
Add2_PRIN 재료비의, 노무비의, 경비의, 전체의
|
||||
Add2_PROUT 재료비로 계산, 노무비로 계산, 경비로 계산, 해당비목 계산
|
||||
■ wM_GetAMT ST_KanJub.CPM «잡비 금액 불러오기»
|
||||
rd_Filter 모두보기, 소 계(+=), 계(+==), 합 계(+===), 총 계(+====), 총합계(+=====)
|
||||
■ wM_Host STmate8.Exe «서버의 IP주소를 입력하고 IP접속( 또는 Enter »
|
||||
rgPrgTyp 일반, LAN, EDU, OnLine, 북측
|
||||
■ wM_JDCode STmate.Cpm «공사원가 호환규정코드 찾기 / 입력»
|
||||
wCb_Base 명칭, 명칭/규격, 분류번호, 시설코드, 선택안함
|
||||
wCb_Srch 명칭/규격/단위, 호환규정코드, 군급목록번호, 중기품셈코드, 시설코드번호
|
||||
wCb_Other 기본, 자재, 경비, 중기, 실적/기타
|
||||
wCb_Method 분류별 조회, 코드..명칭 조회
|
||||
■ wM_JabPum ST_Jabpum.CPM «닫기 (&Q)»
|
||||
cBoxCode 모든단가, 노무비, 재료비, 경비, 일위대가, 중기비, 단가산출, 견적단가, 대가,산근, 그림코드
|
||||
cBox_IFORM 명칭,규격 으로검색, 산근내용 으로검색
|
||||
■ wM_KanJub ST_KanJub.CPM «제잡비 계산»
|
||||
Cmbx_PORD1_H 당초 / 변경 대비인쇄, 변 경 분 만 인 쇄
|
||||
cBx_K_JeolSa 간접비 절사없음, 간접비 10원미만 절사, 간접비 100원미만 절사, 간접비 1,000원미만 절사, 간접비 10,000원미
|
||||
cBx_Chasu 원설계, 1차변경, 2차변경, 3차변경
|
||||
cBx_W_Type 제잡비 원가계산방식, 제잡비 표준시장방식
|
||||
cBx_Jeol_T 총공사비에서 조정, 10원 미만, 100원 미만, 1,000원 미만, 10,000원 미만, 100,
|
||||
Ed_C_AnJun_K_T 없음, 관급/사급 내용, 사급자재대
|
||||
Ed_C_AnJun_B_T , 의 1.2배
|
||||
Ed_C_KUNBO_T ㄴ.1)직노, 당해 상시근로자노무비
|
||||
■ wM_KanJub_K ST_KanJub.CPM «< 기성 제잡비 계산서 >»
|
||||
rd_Filter 소 계(+=) 이상, 계(+==) 이상, 합 계(+===) 이상, 총 계(+====) 이상, 총합계(+=====) 이상
|
||||
cmBx_Buga (0) 직접입력, (1) 공급가액의 10%, (2) 재료비의 10%, (3) 재료비+산출경비의 10%
|
||||
cmBx_JeolSa 총공사비에서 조정, 10원 미만, 100원 미만, 1,000원 미만, 10,000원 미만, 100,
|
||||
cmBx_Tot_Jeol 이윤금액 직접입력, 10원 미만, 100원 미만, 1,000원 미만, 10,000원 미만, 100,
|
||||
■ wM_Mk_Cont STmate.Cpm «당초설계 → 계약내역 변환등록»
|
||||
cBx_Lev 소계 이상, 계 이상, 합계 이상, 총계 이상, 총합계 이상, 총대계 이상, 광역계 이상, 광역총계이상, 광역대계이상
|
||||
■ wM_Mrg_BSC STmate.Cpm «자료파일 업데이트»
|
||||
cBx_Sel1 호환규정코드, 시설코드, 명칭/규격/단위, 호환+명+규+단, 명칭/단위, 설정안함
|
||||
cBx_Sel2 호환규정코드, 시설코드, 명칭/규격/단위, 호환+명+규+단, 명칭/단위, 설정안함
|
||||
cBx_Sel3 호환규정코드, 시설코드, 명칭/규격/단위, 호환+명+규+단, 명칭/단위, 설정안함
|
||||
cBx_Sel4 호환규정코드, 시설코드, 명칭/규격/단위, 호환+명+규+단, 명칭/단위, 설정안함
|
||||
cBx_Sel5 호환규정코드, 시설코드, 명칭/규격/단위, 호환+명+규+단, 명칭/단위, 설정안함
|
||||
rg_Name 업데이트 안함(복사처적용), 업데이트 (복사원적용)
|
||||
rg_Amt 업데이트 안함(복사처적용), 업데이트 (복사원적용)
|
||||
rg_JDCOD 업데이트 안함(복사처적용), 업데이트 (복사원적용), 복사처 우선적용 업데이트, 복사원 우선적용 업데이트
|
||||
■ wM_Rate ST_Boq.CPM «노무비»
|
||||
rd_Qty1 내역서 전체, 선택된 항목만
|
||||
rd_Amt1 내역서 전체, 선택된 항목만
|
||||
rd_Amt2 내역서 전체, 선택된 항목만
|
||||
wcBx_Pnt1 0 절사하여, 1 반올림 하여
|
||||
wcBx_Pnt2 0 소수 0위값을 적용, 1 소수 1위값을 적용, 2 소수 2위값을 적용, 3 소수 3위값을 적용, 4 소수 4위값을 적용
|
||||
wcBx_Pnt1_M 0 절사하여, 1 반올림 하여
|
||||
wcBx_Pnt2_M 0 소수 0위값을 적용, 1 소수 1위값을 적용, 2 소수 2위값을 적용, 3 소수 3위값을 적용, 4 소수 4위값을 적용
|
||||
wcBx_Pnt1_L 0 절사하여, 1 반올림 하여
|
||||
■ wM_Req_ON STmate.Cpm «유지보수기간 연장신청»
|
||||
wEd_Sub 12개월, 6개월, 3개월
|
||||
■ wM_Sel_Prn STmate.Cpm «인쇄할 영역을 선택하세요»
|
||||
cb_PCLASS 당초설계, 변경설계, 분할설계
|
||||
cb_PRTYPE 해당코드 상위의 7레벨공종이하 내역전체, 해당코드 상위의 해당레벨(하위레벨제외)
|
||||
cb_ACTTYP 노무비외 비목 적용안함, 노무비외 재료비로 적용, 노무비외 경비로 적용
|
||||
cb_ACTOPT 노무비 1원미만 버림, 소수 비교후 노무비 우선보정, 소수 비교후 타금액 우선보정
|
||||
cb_CHASU 1차변경, 2차변경, 3차변경, 4차변경, 5차변경, 6차변경, 7차변경, 8차변경
|
||||
■ wM_Set_Sys ST_Sys.CPM «초기값 입력및 수정»
|
||||
cmBx_Sel_RX 계수 ;터널구간, 계수-1;장대터널, 계수-2;펌프준설선, 계수-3;GRAB준설선, 계수-4;버켓준설선
|
||||
cmBX_EXR 시간당중기사용료 단독적용, 중기, 대가, 산근, 내역 전체
|
||||
cmBx_KCD 0 자리, 1 자리, 2 자리
|
||||
cmBx_ACD 0 자리, 1 자리, 2 자리
|
||||
cmBx_ZCD 2 자리, 3 자리
|
||||
cmBx_JCD 0 자리, 1 자리, 2 자리
|
||||
cmBx_ECD 0 자리, 1 자리, 2 자리
|
||||
cmBx_BPN 0 자리, 1 자리, 2 자리
|
||||
■ wM_Stmate_Merge_Code ST_Merge_Code.CPM «설계예산시스템 코드병합»
|
||||
rg_Set_Sys 선택코드만 병합, 선택코드+하위기초단가코드 병합, 모든코드(하위 산근,대가포함) 병합, 코드병합 취소
|
||||
■ wQtyList STmateQty8.CPM «수량산출서»
|
||||
cBoxCode 모든단가, 노무비, 재료비, 경비, 일위대가, 중기비, 단가산출, 견적단가, 대가,산근
|
||||
■ wSrch_PL ST_PList.CPM «프로젝트 DATA검색(명칭, 규격)»
|
||||
wcBx_Type 노무비, 재료비, 경비, 중기비, 일위대가, 단가산출, 일식/표준시장, 내역서
|
||||
|
||||
선택지를 가진 폼 40개, 선택축 272개
|
||||
### STmate 고유 폼 — 모듈별
|
||||
|
||||
■ STmate.Cpm 30개
|
||||
M_Chk_Pw PM_Req_AS frmAddTip frmEditor
|
||||
frmTips wM_About wM_Box_H wM_Boxa
|
||||
wM_Conv_A2W wM_DM_Com wM_D_Restore wM_Dm_1
|
||||
wM_Dm_2 wM_Down_Center wM_Get_Info wM_Get_Value
|
||||
wM_JDCode wM_JajeXLS wM_Mk_Cont wM_Mrg_BSC
|
||||
wM_Req_AS wM_Req_ON wM_Rw_On wM_Sel_Prn
|
||||
wM_Show_Info wM_Status wM_Stmate_Start_8x wM_Xls_Comp
|
||||
wM_iLWi_San wPrjList
|
||||
|
||||
■ ST_P_Nae.CPM 22개
|
||||
PM_Boq_Not PM_Bqm PM_Jab PM_Jab_P
|
||||
PM_Nae PM_NaeW PM_NaeW_P PM_Nae_B
|
||||
PM_Nae_Col PM_Nae_Col_ML PM_Nae_Cont PM_Nae_K0
|
||||
PM_Nae_P PM_Nae_P_K0 PM_Nae_Plan_A PM_Nae_Plan_K
|
||||
PM_Won_A PM_Won_A_C PM_Won_A_L PM_Wonga
|
||||
PM_Wonga_C PM_Wonga_L
|
||||
|
||||
■ ST_Boq.CPM 13개
|
||||
wBoq wBoqRate wExpnd wITNum
|
||||
wKongJong wMBoq wM_Boq_ExecX wM_Expnd
|
||||
wM_Expnd10 wM_From_XLS wM_Rate wM_Ref_Lst
|
||||
wSameCode
|
||||
|
||||
■ ST_P_ConEst.CPM 8개
|
||||
PM_JAB_K PM_NAE_C1 PM_NAE_C2 PM_NAE_C3
|
||||
PM_NAE_K1 PM_NAE_K2 PM_NAE_K3 PM_Nae_Est
|
||||
|
||||
■ ST_PList.CPM 8개
|
||||
wM_Add_Prj wM_Conv_Mode wM_Get_Node wM_Get_Value
|
||||
wM_ST_Restore wPList wST_Dir wSrch_PL
|
||||
|
||||
■ ST_P_List.CPM 7개
|
||||
PM_Comp_M PM_Dan PM_Dan_L PM_SanMok
|
||||
PM_SanMok_L PM_SanMok_P PM_iLMok
|
||||
|
||||
■ ST_P_Excav.CPM 6개
|
||||
PM_NAE_Ex2 PM_NAE_Ex2T PM_Nae_Ex1 PM_Nae_Ex1T
|
||||
PM_Nae_ExC PM_Nae_ExCT
|
||||
|
||||
■ ST_P_Joong.CPM 6개
|
||||
PM_JOONG PM_JOONG2 PM_JOONG2_L PM_JOONG_L
|
||||
PM_Joong_C PM_Joong_C_L
|
||||
|
||||
■ ST_KanJub.CPM 5개
|
||||
PM_KAN_RATE wM_GetAMT wM_KAN_RATE wM_KanJub
|
||||
wM_KanJub_K
|
||||
|
||||
■ ST_Jabpum.CPM 4개
|
||||
wM_Edit_Strorage wM_Edt_Hal wM_Inst_CD wM_JabPum
|
||||
|
||||
■ ST_P_Collect.CPM 4개
|
||||
PM_Col_Dan PM_Col_Dan_L PM_Col_X PM_Col_X_L
|
||||
|
||||
■ ST_Prt.CPM 4개
|
||||
PM_Jab_Ord PM_Prn_Ord PM_Prt PM_Sel_St
|
||||
|
||||
■ ST_Viewer.exe 4개
|
||||
wM_About wM_Extr wM_Main__Viewer wM_Sub
|
||||
|
||||
■ DP_MinMsg.CPM 3개
|
||||
mMinMsg_ wInDlgImg wInDlgMemo
|
||||
|
||||
■ ST_EdSan.CPM 3개
|
||||
wM_Edit_EQ wM_Edit_San wM_Edit_USER
|
||||
|
||||
■ ST_Sys.CPM 3개
|
||||
wM_Set_Init wM_Set_JLab wM_Set_Sys
|
||||
|
||||
■ ST_U_ChgCode.CPM 3개
|
||||
uM_ChgCode uM_Chg_Plus uM_ReplCode
|
||||
|
||||
■ ST_U_ConBD.CPM 3개
|
||||
uM_ConBD uM_ConQTY uM_ConQTY9
|
||||
|
||||
■ ST_U_Convert.CPM 3개
|
||||
uM_BSC_M uM_ConstType uM_XML_Info
|
||||
|
||||
■ ST_Merge_Code.CPM 3개
|
||||
wM_Stmate_Merge_Code wM_Sub wSrch_PL
|
||||
|
||||
■ JDLCHK.Size.dll 2개
|
||||
frm_CheckProgress frm_JDLAnno
|
||||
|
||||
■ ST_Com_V.CPM 2개
|
||||
wInDlgImg wInDlgMemo
|
||||
|
||||
■ ST_EdiLWi.CPM 2개
|
||||
wM_Edit_XRate wM_Edit_iLWi
|
||||
|
||||
■ ST_FindUse.CPM 2개
|
||||
wM_FindBoq wM_FindUse
|
||||
|
||||
■ ST_P_iLWi.CPM 2개
|
||||
PM_iLWi PM_iLWi_S
|
||||
|
||||
■ ST_P_San.CPM 2개
|
||||
PM_San_L PM_San_P
|
||||
|
||||
■ ST_U_ADL.CPM 2개
|
||||
NMShow wM_ADL
|
||||
|
||||
■ ST_Func_C.CPM 2개
|
||||
wInDlgImg wInDlgMemo
|
||||
|
||||
■ ST_Wal.CPM 2개
|
||||
wM_Get_GJ wM_Stmate_Get_Wal
|
||||
|
||||
■ JDLCHK.Tab.dll 1개
|
||||
frm_CheckProgress
|
||||
|
||||
■ JDLCHK.Tab_33.dll 1개
|
||||
frm_CheckProgress
|
||||
|
||||
■ ST_U_Archive.CPM 1개
|
||||
uM_Archive
|
||||
|
||||
■ ST_U_ChgAmt.CPM 1개
|
||||
uM_Chg_Amt
|
||||
|
||||
■ ST_U_DelCode.CPM 1개
|
||||
uM_DelCode
|
||||
|
||||
■ ST_U_DelPrj.CPM 1개
|
||||
uM_DelPrj
|
||||
|
||||
■ ST_U_Indx.CPM 1개
|
||||
uM_Indx
|
||||
|
||||
■ ST_U_Merge.CPM 1개
|
||||
uM_Merge
|
||||
|
||||
■ STmateQty8.CPM 1개
|
||||
wQtyList
|
||||
|
||||
■ ST_U_APatch.CPM 1개
|
||||
wM_Stmate_APatch
|
||||
|
||||
■ STmate8.Exe 1개
|
||||
wM_Host
|
||||
|
||||
■ View_Old_San.CPM 1개
|
||||
M_Hrp_San
|
||||
|
||||
|
||||
### 갈래별 (총 172개)
|
||||
모달 편집 66개
|
||||
인쇄 63개
|
||||
편집 창 19개
|
||||
기타 17개
|
||||
보조 창 7개
|
||||
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""기준자료를 실제로 읽을 수 있는지 판정. 복호화 시도는 하지 않음 — 읽히나만 봄."""
|
||||
import pathlib, struct, sys, io, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
ROOT = pathlib.Path(r"C:\경영정보(주)\System")
|
||||
|
||||
def probe(p):
|
||||
try: b = p.read_bytes()
|
||||
except Exception as e: return "열기실패", 0, 0
|
||||
if b[:2] == b"PK": return "ZIP", 0, 0
|
||||
if b[:1] not in (b"\x03", b"\x04", b"\x05", b"\x30", b"\xE5", b"\x06", b"\xF5", b"\x8B"):
|
||||
return "DBF아님", 0, 0
|
||||
try:
|
||||
nrec, hlen, rlen = struct.unpack_from("<IHH", b, 4)
|
||||
n = 0; o = 32
|
||||
while o < len(b) and b[o] != 0x0D: n += 1; o += 32
|
||||
if not (1 <= n <= 300 and hlen and rlen): return "머리깨짐", 0, 0
|
||||
# 첫 레코드가 사람이 읽을 수 있는 문자인가
|
||||
rec = b[hlen:hlen+rlen]
|
||||
printable = sum(1 for c in rec if 32 <= c < 127 or c >= 0xA1)
|
||||
ratio = printable / max(len(rec), 1)
|
||||
return ("평문" if ratio > 0.85 else "본문암호화"), nrec, n
|
||||
except Exception:
|
||||
return "해석실패", 0, 0
|
||||
|
||||
groups = collections.defaultdict(lambda: collections.Counter())
|
||||
detail = collections.defaultdict(list)
|
||||
for p in sorted(ROOT.rglob("*")):
|
||||
if not p.is_file(): continue
|
||||
ext = p.suffix.upper() or "(없음)"
|
||||
if ext not in (".DBF",".BSC",".STBSC",".CSTA",".JB_R",".JB_RA",".JB_1",".JB_M",".SMT",".MEM"): continue
|
||||
st, nrec, nfld = probe(p)
|
||||
groups[ext][st] += 1
|
||||
detail[ext].append((p.name, st, nrec, nfld))
|
||||
|
||||
print("### 기준자료·프로젝트 테이블의 읽힘 상태 (복호화 시도 없음)")
|
||||
print("%-8s %-5s %s" % ("확장자","개수","판정"))
|
||||
for ext in sorted(groups):
|
||||
tot = sum(groups[ext].values())
|
||||
print("%-8s %-5d %s" % (ext, tot, dict(groups[ext])))
|
||||
|
||||
print("\n### 단가 원천으로 쓰이는 대형 기준파일")
|
||||
for ext in (".BSC", ".CSTA", ".STBSC"):
|
||||
for nm, st, nrec, nfld in detail.get(ext, [])[:8]:
|
||||
print(" %-42s %-10s rec=%-7s fld=%s" % (nm[:42], st, nrec or "-", nfld or "-"))
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pathlib, zipfile, struct, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
def rd(zf, name):
|
||||
b = zf.read(name)
|
||||
nrec, hlen, rlen = struct.unpack_from("<IHH", b, 4)
|
||||
f=[]; o=32
|
||||
while b[o] != 0x0D:
|
||||
f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"), b[o+16])); o += 32
|
||||
rows=[]
|
||||
for i in range(nrec):
|
||||
r = b[hlen+i*rlen:hlen+(i+1)*rlen]; v=[]; c=1
|
||||
for nm,ln in f: v.append(r[c:c+ln].decode("cp949","replace").strip()); c+=ln
|
||||
rows.append(v)
|
||||
return [x[0] for x in f], rows
|
||||
|
||||
Z = zipfile.ZipFile(pathlib.Path(r"C:\경영정보(주)\STmate 압축자료\실습(맛보기).STC"))
|
||||
fn, rows = rd(Z, "COSTN.DBF")
|
||||
ix = {n:i for i,n in enumerate(fn)}
|
||||
def g(r,k):
|
||||
try: return float(r[ix[k]] or 0)
|
||||
except: return 0.0
|
||||
|
||||
print("### TAMT ≠ MAMT+LAMT+SAMT 인 행")
|
||||
bad=0
|
||||
for r in rows:
|
||||
T,M,L,S = g(r,"TAMT1"), g(r,"MAMT1"), g(r,"LAMT1"), g(r,"SAMT1")
|
||||
if not (T or M or L or S): continue
|
||||
if abs(T-(M+L+S)) >= 0.005:
|
||||
bad+=1
|
||||
print(" %-8s %-14s %-12s T=%10.0f M+L+S=%10.0f 차=%+.0f" % (
|
||||
r[ix["CODE"]], r[ix["NAME"]][:14], r[ix["SIZE"]][:12], T, M+L+S, T-(M+L+S)))
|
||||
print(" 불일치 %d행" % bad)
|
||||
|
||||
print("\n### 프로젝트 RATE 표 전수 (맛보기)")
|
||||
import re
|
||||
for nm in sorted(n for n in Z.namelist() if re.match(r"RATE", n, re.I)):
|
||||
f2, r2 = rd(Z, nm)
|
||||
nz = sum(1 for r in r2 if any((x and x not in ("0.0000","0.00","0")) for x in r))
|
||||
print(" %-14s %2d행 × %2d열 값이 든 행 %d" % (nm, len(r2), len(f2), nz))
|
||||
|
||||
print("\n### RATE_A 와 RATE_M 이 같은 표인가")
|
||||
fa, ra = rd(Z,"RATE_A.DBF"); fm, rm = rd(Z,"RATE_M.DBF")
|
||||
same = sum(1 for a,b in zip(ra,rm) if a==b)
|
||||
print(" 행 %d개 중 완전히 같은 행 %d개 → %s" % (len(ra), same, "같은 표 아님" if same<len(ra) else "같은 표"))
|
||||
print(" RATE_A 첫행: %s" % " ".join(x or "·" for x in ra[0]))
|
||||
print(" RATE_M 첫행: %s" % " ".join(x or "·" for x in rm[0]))
|
||||
|
||||
print("\n### 다른 프로젝트에도 RATE_A/RATE_B 가 있나")
|
||||
for p in sorted(pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서").rglob("*.STC")):
|
||||
try:
|
||||
zz = zipfile.ZipFile(p)
|
||||
rs = sorted(n for n in zz.namelist() if re.match(r"RATE", n, re.I))
|
||||
print(" %-46s %s" % (p.name[:46], ", ".join(rs)))
|
||||
except Exception as e: print(" %s 열기실패" % p.name)
|
||||
@@ -0,0 +1,61 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""진행률 장부 — 원천별로 무엇을 어디까지 봤나 실제로 센다."""
|
||||
import sys, io, pathlib, zipfile, struct, re, warnings, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
SYS = pathlib.Path(r"C:\경영정보(주)\System")
|
||||
PRAC = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
|
||||
# ── ① 설치 폴더 186개 ──
|
||||
files = [p for p in SYS.rglob("*") if p.is_file()]
|
||||
kinds = collections.Counter()
|
||||
for p in files:
|
||||
try: head = p.open("rb").read(2)
|
||||
except Exception: head = b""
|
||||
ext = p.suffix.upper()
|
||||
if head == b"MZ": kinds["PE 실행모듈"] += 1
|
||||
elif head == b"PK": kinds["ZIP 계열"] += 1
|
||||
elif ext in (".JB_1",".JB_M",".JB_R",".JB_RA"): kinds["JB 요율표"] += 1
|
||||
elif ext in (".BSC",".CSTA"): kinds["암호화 기준단가"] += 1
|
||||
else: kinds["기타"] += 1
|
||||
print("### ① 설치 폴더 `C:\경영정보(주)\System` 전체 %d개" % len(files))
|
||||
for k,v in kinds.most_common(): print(" %-16s %3d" % (k,v))
|
||||
|
||||
# ── ② 실무 STC ──
|
||||
stcs = sorted(PRAC.rglob("*.STC"))
|
||||
tabset = collections.Counter()
|
||||
for s in stcs:
|
||||
try: z = zipfile.ZipFile(s)
|
||||
except Exception: continue
|
||||
for n in z.namelist():
|
||||
if n.upper().endswith(".DBF"): tabset[n.upper()] += 1
|
||||
print("\n### ② 실무 프로젝트 STC %d건, 담긴 표 %d종" % (len(stcs), len(tabset)))
|
||||
|
||||
# ── ③ STmate 생성 XLSX 시트 ──
|
||||
sheets = collections.Counter(); nx = 0
|
||||
for p in PRAC.rglob("*.xlsx"):
|
||||
if p.name.startswith("~$"): continue
|
||||
try: wb = openpyxl.load_workbook(p, data_only=True, read_only=True)
|
||||
except Exception: continue
|
||||
if "공사원가계산서" not in wb.sheetnames: continue
|
||||
nx += 1
|
||||
for s in wb.sheetnames: sheets[s] += 1
|
||||
print("\n### ③ STmate 생성 XLSX %d건, 시트 %d종" % (nx, len(sheets)))
|
||||
|
||||
DONE_SHEET = {"공사원가계산서","설계내역서","일위대가표","중기사용료","자재단가대비표",
|
||||
"환율및기초자료","단가산출근거","〓 INITIAL 〓",
|
||||
"재료비수량금액집계표","노무비수량금액집계표","경비수량금액집계표"}
|
||||
PART_SHEET = {"중기시간금액집계표"}
|
||||
print(" %-24s %s" % ("시트","상태"))
|
||||
for s,_ in sheets.most_common():
|
||||
st = "✅ 계산 검증" if s in DONE_SHEET else ("◐ 부분" if s in PART_SHEET else "· 미확인(목록·표지)")
|
||||
print(" %-24s %s" % (s[:24], st))
|
||||
d=len(DONE_SHEET & set(sheets)); pt=len(PART_SHEET & set(sheets))
|
||||
print(" → 계산 검증 %d / %d 시트 (부분 %d)" % (d, len(sheets), pt))
|
||||
|
||||
DONE_TAB = {"BOQ11.DBF","BOQ12.DBF","COSTN.DBF","PRJLST.DBF","RATE.DBF","RATE_M.DBF",
|
||||
"RATE_R.DBF","RATE_RA.DBF","RATE_A.DBF","RATE_B.DBF","SET_REP.DBF","KONGJONG.DBF"}
|
||||
print("\n STC 표별 상태")
|
||||
for t,c in sorted(tabset.items()):
|
||||
st = "✅ 읽고 씀" if t in DONE_TAB else ("⛔ 암호화" if t in ("BDQTY.DBF","SYSINFO.DBF") else "· 빈 표/미사용")
|
||||
print(" %-16s %d건 %s" % (t, c, st))
|
||||
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""STmate 고유 폼 목록을 모듈·갈래별로 정리."""
|
||||
import sys, io, pathlib, re, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
ROOT = pathlib.Path(r"C:\경영정보(주)\System")
|
||||
THIRD = re.compile(r"^(Dclhrp60|designide60|RTL60|VCL60|DBRTL60|BDERTL60|RealGrid|Apollo|dsnide)", re.I)
|
||||
HRLIB = {"HReportUf","HRPageRf","HRPreVUf","HRSaveUf","LoginDialog","PasswordDialog","HRGridCUf",
|
||||
"HRGridFUf","HRGroupUf","HRLinkFf","HRPrintUf","HRExprUf","HRFieldUf","HRSortUf"}
|
||||
def forms(b):
|
||||
out=[]
|
||||
for m in re.finditer(rb"TPF0", b):
|
||||
s=m.end()
|
||||
if s>=len(b): continue
|
||||
n=b[s]
|
||||
if not (3<=n<=40): continue
|
||||
cls=b[s+1:s+1+n]
|
||||
if not re.fullmatch(rb"T[A-Za-z0-9_]+", cls): continue
|
||||
s2=s+1+n
|
||||
if s2>=len(b): continue
|
||||
n2=b[s2]
|
||||
if not (1<=n2<=40): continue
|
||||
nm=b[s2+1:s2+1+n2]
|
||||
if not re.fullmatch(rb"[A-Za-z_][A-Za-z0-9_]*", nm): continue
|
||||
out.append(nm.decode("latin1"))
|
||||
return out
|
||||
|
||||
per={}
|
||||
for p in sorted(ROOT.rglob("*")):
|
||||
if not p.is_file() or THIRD.match(p.stem): continue
|
||||
try: b=p.read_bytes()
|
||||
except Exception: continue
|
||||
if b[:2]!=b"MZ": continue
|
||||
f=[x for x in forms(b) if x not in HRLIB]
|
||||
if f: per[p.name]=sorted(set(f))
|
||||
|
||||
def kind(n):
|
||||
if n.startswith("PM_"): return "인쇄"
|
||||
if n.startswith(("wM_","M_")): return "모달 편집"
|
||||
if n.startswith("w"): return "편집 창"
|
||||
if n.startswith("frm"): return "보조 창"
|
||||
return "기타"
|
||||
cnt=collections.Counter(); tot=0
|
||||
print("### STmate 고유 폼 — 모듈별")
|
||||
for nm,f in sorted(per.items(), key=lambda x:-len(x[1])):
|
||||
if not f: continue
|
||||
tot+=len(f)
|
||||
for x in f: cnt[kind(x)]+=1
|
||||
print("\n■ %-24s %2d개" % (nm, len(f)))
|
||||
for i in range(0,len(f),4):
|
||||
print(" " + " ".join("%-22s"%x for x in f[i:i+4]))
|
||||
print("\n\n### 갈래별 (총 %d개)"%tot)
|
||||
for k,v in cnt.most_common(): print(" %-10s %3d개" % (k,v))
|
||||
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""PE 안의 이름 붙은 Delphi 폼(TPF0)을 뽑아 .dfm 으로 저장. 원본은 읽기만."""
|
||||
import sys, io, re, pathlib
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
ROOT = pathlib.Path(r"C:\경영정보(주)\System")
|
||||
OUT = pathlib.Path(r"C:\Users\umsan\AppData\Local\Temp\claude\D--aislo-wt-sub\df75230a-b05b-4088-ad34-bb0c9da0a312\scratchpad\forms")
|
||||
OUT.mkdir(exist_ok=True)
|
||||
|
||||
want = sys.argv[1:] or ["wM_Edt_Hal","wM_JabPum","wM_Edit_San","wM_Set_Init","wM_Set_Sys","wBoq","wBoqRate"]
|
||||
found = {}
|
||||
for p in sorted(ROOT.rglob("*")):
|
||||
if not p.is_file(): continue
|
||||
try: b=p.read_bytes()
|
||||
except Exception: continue
|
||||
if b[:2]!=b"MZ": continue
|
||||
for m in re.finditer(rb"TPF0", b):
|
||||
s=m.end()
|
||||
if s>=len(b): continue
|
||||
n=b[s]
|
||||
if not (3<=n<=40): continue
|
||||
cls=b[s+1:s+1+n]
|
||||
if not re.fullmatch(rb"T[A-Za-z0-9_]+", cls): continue
|
||||
s2=s+1+n; n2=b[s2] if s2<len(b) else 0
|
||||
if not (1<=n2<=40): continue
|
||||
nm=b[s2+1:s2+1+n2]
|
||||
try: nms=nm.decode("latin1")
|
||||
except Exception: continue
|
||||
if nms not in want or nms in found: continue
|
||||
# 끝 찾기: 다음 TPF0 또는 넉넉히 자름
|
||||
nxt=b.find(b"TPF0", m.end())
|
||||
end = nxt if nxt>0 else min(len(b), m.start()+400000)
|
||||
blob = b[m.start():end]
|
||||
f = OUT/("%s.dfm"%nms)
|
||||
f.write_bytes(blob)
|
||||
found[nms]=(p.name, len(blob))
|
||||
for k in want:
|
||||
if k in found: print(" ✅ %-16s %-22s %7d B" % (k, found[k][0], found[k][1]))
|
||||
else: print(" ✗ %-16s 못 찾음" % k)
|
||||
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""골든셋 회귀 — 내역 → 직접비 → 간접비 → 총공사비를 다시 세워 XLSX 와 원 단위 대조."""
|
||||
import sys, io, pathlib, re, zipfile, struct, warnings
|
||||
from decimal import Decimal, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore")
|
||||
import openpyxl
|
||||
|
||||
BASE = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서\04.산불 봉화\01. 내역서")
|
||||
STC = BASE/"13-2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km.STC"
|
||||
XLS = BASE/"2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km.xlsx"
|
||||
D = Decimal
|
||||
def dn(x, q=0): return (D(x)).quantize(D(1).scaleb(-q), rounding=ROUND_DOWN)
|
||||
|
||||
def dbf(zf,name):
|
||||
b=zf.read(name); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
rows=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; d={};c=1
|
||||
for nm,ln in f: d[nm]=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
rows.append(d)
|
||||
return rows
|
||||
|
||||
z = zipfile.ZipFile(STC); boq = dbf(z,"BOQ11.DBF")
|
||||
def g(r,k):
|
||||
try: return D(r.get(k) or "0")
|
||||
except Exception: return D(0)
|
||||
|
||||
STRUCT = re.compile(r"^(T\d|E\d|C\d|:)$")
|
||||
# C* 로 닫히는 구역 = 제외금액 구역(관급자재대 등). 그 안의 행은 직접비에서 뺀다.
|
||||
excl_span = set()
|
||||
for i, r in enumerate(boq):
|
||||
bd = (r.get("BDCODE1") or "").strip()
|
||||
if bd.startswith("C"):
|
||||
end = r.get("ENDCODE"); start = None
|
||||
for j, q in enumerate(boq):
|
||||
if q.get("SORTCODE") == end: start = j; break
|
||||
if start is not None:
|
||||
for k in range(min(start, i), max(start, i)+1): excl_span.add(k)
|
||||
leaf = [r for i, r in enumerate(boq)
|
||||
if r.get("BDCODE1") and not STRUCT.match(r["BDCODE1"].strip()) and i not in excl_span]
|
||||
excl = [r for i, r in enumerate(boq)
|
||||
if r.get("BDCODE1") and not STRUCT.match(r["BDCODE1"].strip()) and i in excl_span]
|
||||
print("### 전체 %d행 → 말단 %d행 (제목 T*·계 E*·제외총계 C*·페이지 : 제외)" % (len(boq), len(leaf)))
|
||||
print(" 제외금액 구역 %d행 (합 재료 %s)" % (len(excl), f"{sum(g(r,'M_AMT1') for r in excl):,}"))
|
||||
|
||||
M = sum(g(r,"M_AMT1") for r in leaf); L = sum(g(r,"L_AMT1") for r in leaf); S = sum(g(r,"S_AMT1") for r in leaf)
|
||||
|
||||
# 행 금액이 ROUNDDOWN(수량 x 단가) 인지 검사
|
||||
ok=bad=0; badrows=[]
|
||||
for r in leaf:
|
||||
for comp,amt in (("MAMT1","M_AMT1"),("LAMT1","L_AMT1"),("SAMT1","S_AMT1")):
|
||||
exp = dn(g(r,"QTY1")*g(r,comp))
|
||||
act = g(r,amt)
|
||||
if exp == act: ok+=1
|
||||
else:
|
||||
bad+=1
|
||||
if len(badrows)<5: badrows.append((r.get("NAME1","")[:20], comp, exp, act))
|
||||
print("행 금액 = ROUNDDOWN(수량 × 단가) : 일치 %d / 불일치 %d" % (ok,bad))
|
||||
for nm,c,e,a in badrows: print(" %-20s %-7s 계산 %s ≠ 장부 %s" % (nm,c,e,a))
|
||||
|
||||
wb = openpyxl.load_workbook(XLS, data_only=True, read_only=True)
|
||||
ws = wb["공사원가계산서"]
|
||||
T={}; notes={}
|
||||
for row in ws.iter_rows(min_row=1,max_row=40,max_col=7,values_only=True):
|
||||
cells=["" if v is None else v for v in row]
|
||||
lab="".join(str(c) for c in cells[:3] if isinstance(c,str)).replace(" ","")
|
||||
amt=next((c for c in cells[3:5] if isinstance(c,(int,float))),None)
|
||||
note=next((str(c) for c in cells[4:7] if isinstance(c,str) and "%" in str(c)),"")
|
||||
if lab and amt is not None: T[lab]=D(str(amt)); notes[lab]=note
|
||||
|
||||
def cmp(label, calc, key):
|
||||
exp = T.get(key)
|
||||
if exp is None: print(" %-22s %14s (목표 없음)" % (label, calc)); return
|
||||
d = calc-exp
|
||||
print(" %-22s 계산 %14s 출력 %14s %s" % (label, f"{calc:,}", f"{exp:,}", "일치" if d==0 else "차 %+d" % d))
|
||||
|
||||
print("\n### 직접비")
|
||||
cmp("직접재료비", M, "순공사원가재료비직접재료비")
|
||||
cmp("직접노무비", L, "노무비직접노무비")
|
||||
cmp("산출경비", S, "경비산출경비")
|
||||
|
||||
print("\n### 간접비 — 문서의 밑수·요율 그대로 적용")
|
||||
간노 = dn(L*D("0.145")); cmp("간접노무비", 간노, "간접노무비")
|
||||
노무 = L + 간노
|
||||
산재 = dn(노무*D("0.0356")); cmp("산재보험료", 산재, "산재보험료")
|
||||
고용 = dn(노무*D("0.0101")); cmp("고용보험료", 고용, "고용보험료")
|
||||
건강 = dn(L*D("0.03545")); cmp("건강보험료", 건강, "건강보험료")
|
||||
장기 = dn(건강*D("0.1295")); cmp("노인장기요양보험료", 장기, "노인장기요양보험료")
|
||||
연금 = dn(L*D("0.045")); cmp("연금보험료", 연금, "연금보험료")
|
||||
기타 = dn((노무+M)*D("0.06")); cmp("기타경비", 기타, "기타경비")
|
||||
환경 = dn((M+L+S)*D("0.008")); cmp("환경보전비", 환경, "환경보전비")
|
||||
기계 = dn((M+L+S)*D("0.004")); cmp("건설기계대여금지급보증금액", 기계, "건설기계대여금지급보증금액")
|
||||
|
||||
관급 = D("143554309")
|
||||
안전 = dn((L+M+관급/D("1.1"))*D("0.0186")) + D("5349000")
|
||||
cmp("산업안전보건관리비", 안전, "산업안전보건관리비")
|
||||
print(" (비고 원문: %s)" % notes.get("산업안전보건관리비","")[:70])
|
||||
|
||||
경비계 = S+산재+고용+건강+장기+연금+T.get("산업안전보건관리비",D(0))+기타+환경+기계
|
||||
cmp("경비 소계", 경비계, "소계")
|
||||
순공사원가 = M + 노무 + 경비계
|
||||
일반 = dn(순공사원가*D("0.06")); cmp("일반관리비", 일반, "일반관리비")
|
||||
이윤 = dn((노무+경비계+일반)*D("0.15")); cmp("이윤", 이윤, "이윤")
|
||||
총원가 = 순공사원가+일반+이윤; cmp("총원가", 총원가, "총원가")
|
||||
부가 = dn(T.get("총원가",총원가)*D("0.1")); cmp("부가가치세", 부가, "부가가치세")
|
||||
|
||||
# ── 산업안전보건관리비의 관급재 밑수를 역산 ───────────────────────
|
||||
print("\n### 산업안전보건관리비 — 관급재 밑수 역산")
|
||||
목표 = T["산업안전보건관리비"] - D("5349000")
|
||||
관급대 = D("143554309"); 조달 = D("771029")
|
||||
for 이름, 값 in [("관급자재대 그대로", 관급대), ("관급자재대 − 조달수수료", 관급대-조달), ("조달수수료만", 조달)]:
|
||||
밑수 = L + M + 값/D("1.1")
|
||||
계산 = dn(밑수*D("0.0186"))
|
||||
print(" %-22s 밑수 %16s 율부분 %12s %s" % (이름, f"{밑수.quantize(D('1')):,}", f"{계산:,}",
|
||||
"일치" if 계산==목표 else "차 %+d" % (계산-목표)))
|
||||
|
||||
# ── 끝전조정 ─────────────────────────────────────────────
|
||||
print("\n### 끝전조정 — 이윤 차 339원의 정체")
|
||||
안전 = T["산업안전보건관리비"]
|
||||
경비계2 = S+산재+고용+건강+장기+연금+안전+기타+환경+기계
|
||||
순2 = M + 노무 + 경비계2
|
||||
일반2 = dn(순2*D("0.06"))
|
||||
이윤원 = dn((노무+경비계2+일반2)*D("0.15"))
|
||||
총원가원 = 순2+일반2+이윤원
|
||||
vat원 = dn(총원가원*D("0.1"))
|
||||
도급원 = 총원가원+vat원
|
||||
도급목표 = T["도급공사비"]
|
||||
차액 = 도급원 - 도급목표
|
||||
세전 = (D(차액)/D("1.1")).quantize(D("1"), rounding=ROUND_HALF_UP)
|
||||
print(" 보정 전 이윤 %14s" % f"{이윤원:,}")
|
||||
print(" 보정 전 총원가 %14s" % f"{총원가원:,}")
|
||||
print(" 보정 전 부가세 %14s" % f"{vat원:,}")
|
||||
print(" 보정 전 도급공사비 %14s 목표 %14s 차액 %s" % (f"{도급원:,}", f"{도급목표:,}", f"{차액:,}"))
|
||||
print(" 세전조정 = ROUND(차액/1.1) = %s" % f"{세전:,}")
|
||||
이윤2 = 이윤원 - 세전
|
||||
총2 = 순2+일반2+이윤2
|
||||
vat2 = dn(총2*D("0.1"))
|
||||
print()
|
||||
for lab,calc,key in [("이윤(보정후)",이윤2,"이윤"),("총원가",총2,"총원가"),
|
||||
("부가가치세",vat2,"부가가치세"),("도급공사비",총2+vat2,"도급공사비")]:
|
||||
exp=T[key]; print(" %-14s 계산 %16s 출력 %16s %s" % (lab, f"{calc:,}", f"{exp:,}",
|
||||
"일치" if calc==exp else "차 %+d" % (calc-exp)))
|
||||
관급자재대 = (관급대/D("1000")).quantize(D("1"), rounding=ROUND_UP)*D("1000")
|
||||
print("\n 관급자재대 = ROUNDUP(%s, 천원) = %s 출력 %s %s" % (
|
||||
f"{관급대:,}", f"{관급자재대:,}", f"{T['관급자재대']:,}", "일치" if 관급자재대==T["관급자재대"] else "불일치"))
|
||||
총공 = 총2+vat2+관급자재대
|
||||
print(" 총공사비 = 도급 + 관급 = %s 출력 %s %s" % (
|
||||
f"{총공:,}", f"{T['총공사비']:,}", "일치" if 총공==T["총공사비"] else "차 %+d"%(총공-T["총공사비"])))
|
||||
@@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""골든셋 회귀 — 실무 STC 전수에 같은 규칙을 걸어 원 단위 대조."""
|
||||
import sys, io, pathlib, re, zipfile, struct, warnings, difflib
|
||||
from decimal import Decimal as D, ROUND_DOWN, ROUND_HALF_UP, ROUND_UP
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore")
|
||||
import openpyxl
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
def dn(x): return D(x).quantize(D(1), rounding=ROUND_DOWN)
|
||||
|
||||
def dbf(zf,name):
|
||||
b=zf.read(name); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
rows=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; d={};c=1
|
||||
for nm,ln in f: d[nm]=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
rows.append(d)
|
||||
return rows
|
||||
def g(r,k):
|
||||
try: return D(r.get(k) or "0")
|
||||
except Exception: return D(0)
|
||||
STRUCT = re.compile(r"^(T\d|E\d|C\d|:)$")
|
||||
|
||||
def direct(boq):
|
||||
excl=set()
|
||||
for i,r in enumerate(boq):
|
||||
bd=(r.get("BDCODE1") or "").strip()
|
||||
if bd.startswith("C"):
|
||||
end=r.get("ENDCODE"); st=next((j for j,q in enumerate(boq) if q.get("SORTCODE")==end), None)
|
||||
if st is not None:
|
||||
for k in range(min(st,i),max(st,i)+1): excl.add(k)
|
||||
leaf=[r for i,r in enumerate(boq) if r.get("BDCODE1") and not STRUCT.match(r["BDCODE1"].strip()) and i not in excl]
|
||||
rowok=rowbad=0
|
||||
for r in leaf:
|
||||
for c,a in (("MAMT1","M_AMT1"),("LAMT1","L_AMT1"),("SAMT1","S_AMT1")):
|
||||
if dn(g(r,"QTY1")*g(r,c))==g(r,a): rowok+=1
|
||||
else: rowbad+=1
|
||||
return (sum(g(r,"M_AMT1") for r in leaf), sum(g(r,"L_AMT1") for r in leaf),
|
||||
sum(g(r,"S_AMT1") for r in leaf), len(leaf), rowok, rowbad)
|
||||
|
||||
def targets(xp):
|
||||
wb=openpyxl.load_workbook(xp,data_only=True,read_only=True)
|
||||
if "공사원가계산서" not in wb.sheetnames: return {},{}
|
||||
ws=wb["공사원가계산서"]; T={};N={}
|
||||
for row in ws.iter_rows(min_row=1,max_row=45,max_col=7,values_only=True):
|
||||
cells=["" if v is None else v for v in row]
|
||||
lab="".join(str(c) for c in cells[:3] if isinstance(c,str)).replace(" ","")
|
||||
amt=next((c for c in cells[3:5] if isinstance(c,(int,float))),None)
|
||||
note=next((str(c) for c in cells[4:7] if isinstance(c,str) and "%" in str(c)),"")
|
||||
if lab and amt is not None and lab not in T: T[lab]=D(str(amt)); N[lab]=note
|
||||
return T,N
|
||||
|
||||
RE_R = re.compile(r"([\d.]+)\s*%")
|
||||
pairs=[]
|
||||
for stc in sorted(ROOT.rglob("*.STC")):
|
||||
best=None;bs=0
|
||||
for x in list(stc.parent.rglob("*.xlsx"))+list(stc.parent.parent.rglob("*.xlsx")):
|
||||
if x.name.startswith("~$"): continue
|
||||
s=difflib.SequenceMatcher(None,stc.stem,x.stem).ratio()
|
||||
if s>bs: bs,best=s,x
|
||||
if best and bs>0.55: pairs.append((stc,best))
|
||||
|
||||
for stc,xp in pairs:
|
||||
try: T,N = targets(xp)
|
||||
except Exception as e: print("%-42s 엑셀 실패" % stc.stem[:42]); continue
|
||||
if not T: continue
|
||||
zf = zipfile.ZipFile(stc)
|
||||
have = {n.upper() for n in zf.namelist()}
|
||||
# 한 파일에 BOQ11·BOQ12 가 함께 있으면 BOQ12 가 현재안(출력 기준)이다
|
||||
tbl = "BOQ12.DBF" if "BOQ12.DBF" in have and dbf(zf,"BOQ12.DBF") else "BOQ11.DBF"
|
||||
boq = dbf(zf, tbl)
|
||||
M,L,S,nleaf,ok,bad = direct(boq)
|
||||
def get(*keys):
|
||||
for k in keys:
|
||||
if k in T: return T[k]
|
||||
return None
|
||||
tM=get("순공사원가재료비직접재료비","재료비직접재료비"); tL=get("노무비직접노무비"); tS=get("경비산출경비")
|
||||
print("\n== %s" % stc.stem[:60])
|
||||
print(" 말단 %d행 행금액 ROUNDDOWN 일치 %d / 불일치 %d" % (nleaf, ok, bad))
|
||||
for lab,c,e in (("직접재료비",M,tM),("직접노무비",L,tL),("산출경비",S,tS)):
|
||||
if e is None: print(" %-10s 계산 %16s (목표 없음)" % (lab, f"{c:,}")); continue
|
||||
print(" %-10s 계산 %16s 출력 %16s %s" % (lab, f"{c:,}", f"{e:,}", "일치" if c==e else "차 %+d"%(c-e)))
|
||||
# 요율은 비고에서 읽어 그대로 적용
|
||||
def rate(key):
|
||||
m=RE_R.search(N.get(key,"") or ""); return D(m.group(1))/100 if m else None
|
||||
r간=rate("간접노무비")
|
||||
if r간 and tL is not None:
|
||||
간=dn(tL*r간); e=T.get("간접노무비")
|
||||
print(" 간접노무비 계산 %16s 출력 %16s %s" % (f"{간:,}", f"{e:,}", "일치" if 간==e else "차 %+d"%(간-e)))
|
||||
노=tL+간
|
||||
for lab,base,key in (("산재보험료",노,"산재보험료"),("고용보험료",노,"고용보험료"),
|
||||
("건강보험료",tL,"건강보험료"),("연금보험료",tL,"연금보험료")):
|
||||
rr=rate(key)
|
||||
if rr is None or key not in T: continue
|
||||
v=dn(base*rr); e=T[key]
|
||||
print(" %-10s 계산 %16s 출력 %16s %s" % (lab, f"{v:,}", f"{e:,}", "일치" if v==e else "차 %+d"%(v-e)))
|
||||
@@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Start8x.Ini 설정과 SET_REP 보고서 카탈로그를 뽑는다. 원본은 읽기만."""
|
||||
import sys, io, struct, pathlib, re
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
D = pathlib.Path(r"C:\경영정보(주)\STmate 자료\설계자료\실습(맛보기)")
|
||||
|
||||
print("### Start8x.Ini — 절별 키")
|
||||
txt = (D/"Start8x.Ini").read_bytes().decode("cp949","replace")
|
||||
sec="(머리)"
|
||||
for line in txt.splitlines():
|
||||
line=line.strip()
|
||||
if not line: continue
|
||||
if line.startswith("["): sec=line; print("\n"+sec); continue
|
||||
print(" %s" % line[:110])
|
||||
|
||||
print("\n\n### 간접비 순서 코드")
|
||||
for k in ("KAN_ORD_1","KAN_ORD_2"):
|
||||
m=re.search(k+r"=(.*)", txt)
|
||||
if m:
|
||||
codes=[c for c in m.group(1).split(";") if c.strip()]
|
||||
print(" %s %d개: %s" % (k, len(codes), ", ".join(codes)))
|
||||
a=set(re.search(r"KAN_ORD_1=(.*)",txt).group(1).split(";"))
|
||||
b=set(re.search(r"KAN_ORD_2=(.*)",txt).group(1).split(";"))
|
||||
print(" 1에만: %s" % sorted(x for x in a-b if x.strip()))
|
||||
print(" 2에만: %s" % sorted(x for x in b-a if x.strip()))
|
||||
|
||||
print("\n\n### SET_REP.DBF — 보고서 서식 카탈로그")
|
||||
b2=(D/"SET_REP.DBF").read_bytes(); nrec,hlen,rlen=struct.unpack_from("<IHH",b2,4)
|
||||
f=[];o=32
|
||||
while b2[o]!=0x0D: f.append((b2[o:o+11].rstrip(b"\0").decode("cp949","replace"),b2[o+16]));o+=32
|
||||
n=0
|
||||
for i in range(nrec):
|
||||
r=b2[hlen+i*rlen:hlen+(i+1)*rlen]; v=[];c=1
|
||||
for nm,ln in f: v.append(r[c:c+ln].decode("cp949","replace").strip()); c+=ln
|
||||
if v[1]: n+=1; print(" %-5s %-24s %s" % (v[0], v[1][:24], v[2][:30]))
|
||||
print(" → 서식 %d종" % n)
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""중기사용료 호표에서 시간당 손료율·연료·잡품률을 뽑는다."""
|
||||
import sys, io, pathlib, warnings, re, collections
|
||||
from decimal import Decimal as D
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def dec(v):
|
||||
if v in (None,""): return None
|
||||
try: return D(str(v))
|
||||
except Exception: return None
|
||||
P = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서\04.산불 봉화\01. 내역서\2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km.xlsx")
|
||||
ws = openpyxl.load_workbook(P, data_only=True)["중기사용료"]
|
||||
rows=[list(r) for r in ws.iter_rows(min_row=1,max_row=ws.max_row,max_col=12,values_only=True)]
|
||||
호표=[]; cur=None; title=None
|
||||
for r in rows:
|
||||
nm=(str(r[0]) if r[0] is not None else "").strip()
|
||||
k=nm.replace(" ","")
|
||||
if re.match(r"^제?\d+호표", k): cur=[]; title=None; 호표.append((None,cur)); continue
|
||||
if cur is None: continue
|
||||
if k=="합계":
|
||||
호표[-1]=(title,cur+[("SUM",r)]); cur=None; continue
|
||||
if title is None and r[3] and not r[2]:
|
||||
title=(nm, str(r[1] or "")); continue
|
||||
cur.append(("ROW",r))
|
||||
|
||||
print("### 중기 시간당 사용료 구성 — 봉화 2024")
|
||||
print("%-24s %-14s %9s %12s %10s %8s %8s" % ("장비","규격","손료율","취득가(천원)","손료(원)","연료L/h","잡품%"))
|
||||
for title, blk in 호표:
|
||||
if not title: continue
|
||||
손료율=취득가=연료=잡품=None
|
||||
for k,r in blk:
|
||||
if k!="ROW": continue
|
||||
u=(str(r[3]) if r[3] else "").strip()
|
||||
nm=(str(r[0]) if r[0] else "").strip()
|
||||
if u=="천원": 손료율, 취득가 = dec(r[2]), dec(r[4])
|
||||
elif u=="L": 연료 = dec(r[2])
|
||||
elif u=="%" and "잡품" in nm: 잡품 = dec(r[2])
|
||||
if 손료율 is None: continue
|
||||
print("%-24s %-14s %9s %12s %10s %8s %8s" % (
|
||||
title[0][:24], title[1][:14], 손료율, f"{취득가:,}" if 취득가 else "-",
|
||||
f"{(손료율*취득가):,.1f}" if (손료율 and 취득가) else "-",
|
||||
연료 if 연료 is not None else "-", 잡품 if 잡품 is not None else "-"))
|
||||
|
||||
print("\n### 잡품 = 주연료비 × 율 검산")
|
||||
ok=bad=0
|
||||
for title, blk in 호표:
|
||||
연료액=None
|
||||
for k,r in blk:
|
||||
if k!="ROW": continue
|
||||
u=(str(r[3]) if r[3] else "").strip(); nm=(str(r[0]) if r[0] else "").strip()
|
||||
if u=="L": 연료액 = dec(r[9]) or dec(r[5])
|
||||
elif u=="%" and "잡품" in nm:
|
||||
율=dec(r[2]); 단=dec(r[4]); 금=dec(r[9]) or dec(r[5])
|
||||
if 연료액 is None or 율 is None: continue
|
||||
exp = 연료액*율/100
|
||||
same_base = (단 == 연료액)
|
||||
if abs(exp-(금 or D(0))) < D("0.05") and same_base: ok+=1
|
||||
else: bad+=1
|
||||
print(" 잡품 금액 = 연료비 × 율/100 : 일치 %d / 불일치 %d (잡품 단가칸 = 연료비)" % (ok,bad))
|
||||
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""자재 채택 규칙 검정 — 적용단가가 후보 중 무엇인가."""
|
||||
import sys, io, pathlib, warnings, collections
|
||||
from decimal import Decimal as D
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def dec(v):
|
||||
if v in (None,""): return None
|
||||
try: return D(str(v))
|
||||
except Exception: return None
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
SLOTS = [("조달가격",5),("물가자료",7),("물가정보",9),("거래가격",11),("견적단가",13)]
|
||||
APPLY = 15
|
||||
stat = collections.Counter(); examples=[]
|
||||
files=0
|
||||
for p in sorted(ROOT.rglob("*.xlsx")):
|
||||
if p.name.startswith("~$"): continue
|
||||
try: wb = openpyxl.load_workbook(p, data_only=True, read_only=True)
|
||||
except Exception: continue
|
||||
if "자재단가대비표" not in wb.sheetnames: continue
|
||||
files += 1
|
||||
ws = wb["자재단가대비표"]
|
||||
for row in ws.iter_rows(min_row=5, max_row=ws.max_row, max_col=17, values_only=True):
|
||||
name = row[1]
|
||||
if not name or not str(name).strip(): continue
|
||||
cands = {nm: dec(row[i-1]) for nm,i in SLOTS}
|
||||
nz = {k:v for k,v in cands.items() if v not in (None, D(0))}
|
||||
app = dec(row[APPLY-1]); src = (str(row[APPLY]) if len(row)>APPLY and row[APPLY] else "").strip()
|
||||
if app in (None, D(0)):
|
||||
stat["적용단가 없음(별도계상 등)"] += 1; continue
|
||||
if not nz:
|
||||
stat["후보 없이 적용단가만(유가·별도자료)"] += 1; continue
|
||||
mn = min(nz.values()); mx = max(nz.values())
|
||||
if len(set(nz.values())) >= 2:
|
||||
stat["복수후보 총"] += 1
|
||||
if app == mn: stat["복수후보 최저"] += 1
|
||||
elif app in nz.values(): stat["복수후보 최저아닌 후보"] += 1
|
||||
else: stat["복수후보 후보밖"] += 1
|
||||
if len(set(nz.values())) >= 3:
|
||||
stat["3개이상 총"] += 1
|
||||
if app == mn: stat["3개이상 최저"] += 1
|
||||
if app == mn: stat["최저가와 일치"] += 1
|
||||
elif app == mx: stat["최고가와 일치"] += 1
|
||||
elif app in nz.values(): stat["후보 중 하나지만 최저 아님"] += 1
|
||||
else:
|
||||
stat["어느 후보와도 다름"] += 1
|
||||
if len(examples)<6: examples.append((p.stem[:26], str(name)[:18], nz, app, src))
|
||||
stat["총"] += 1
|
||||
|
||||
print("### 자재 채택 규칙 검정 — 파일 %d개" % files)
|
||||
tot = stat["총"]
|
||||
for k in ("최저가와 일치","후보 중 하나지만 최저 아님","최고가와 일치","어느 후보와도 다름"):
|
||||
print(" %-24s %4d건 %5.1f%%" % (k, stat[k], 100*stat[k]/tot if tot else 0))
|
||||
print(" %-24s %4d건" % ("(후보 없이 적용만)", stat["후보 없이 적용단가만(유가·별도자료)"]))
|
||||
print(" %-24s %4d건" % ("(적용단가 0/별도계상)", stat["적용단가 없음(별도계상 등)"]))
|
||||
print(" ── 검정 대상 합계 %d건" % tot)
|
||||
print()
|
||||
for k in ("복수후보 총","복수후보 최저","복수후보 최저아닌 후보","복수후보 후보밖","3개이상 총","3개이상 최저"):
|
||||
print(" %-22s %4d건" % (k, stat[k]))
|
||||
if examples:
|
||||
print("\n### 어느 후보와도 다른 예")
|
||||
for f,n,nz,app,src in examples:
|
||||
print(" %-26s %-18s 적용 %-10s 출처 %-6s 후보 %s" % (f,n,app,src,
|
||||
", ".join("%s=%s"%(k,v) for k,v in nz.items())))
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""CPM 46개 모듈의 export 함수 전수 — 11번은 핵심 12개만 했음."""
|
||||
import sys, io, pathlib, struct, collections, re
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
ROOT = pathlib.Path(r"C:\경영정보(주)\System")
|
||||
|
||||
def exports(b):
|
||||
try:
|
||||
e=struct.unpack_from("<I",b,0x3C)[0]
|
||||
if b[e:e+4]!=b"PE\0\0": return []
|
||||
nsec=struct.unpack_from("<H",b,e+6)[0]; optsz=struct.unpack_from("<H",b,e+20)[0]
|
||||
opt=e+24; magic=struct.unpack_from("<H",b,opt)[0]
|
||||
dd=opt+(96 if magic==0x10b else 112)
|
||||
erva,esz=struct.unpack_from("<II",b,dd)
|
||||
if not erva: return []
|
||||
secs=[]; so=opt+optsz
|
||||
for i in range(nsec):
|
||||
s=b[so+i*40:so+i*40+40]
|
||||
vsz,va,rsz,ptr=struct.unpack_from("<IIII",s,8); secs.append((va,vsz,ptr,rsz))
|
||||
def r2o(r):
|
||||
for va,vsz,ptr,rsz in secs:
|
||||
if va<=r<va+max(vsz,rsz): return ptr+(r-va)
|
||||
eo=r2o(erva)
|
||||
if eo is None: return []
|
||||
nnames=struct.unpack_from("<I",b,eo+24)[0]
|
||||
namp=struct.unpack_from("<I",b,eo+32)[0]
|
||||
no=r2o(namp)
|
||||
out=[]
|
||||
for i in range(min(nnames,4000)):
|
||||
nr=struct.unpack_from("<I",b,no+4*i)[0]
|
||||
o=r2o(nr)
|
||||
if o is None: continue
|
||||
end=b.index(b"\0",o)
|
||||
out.append(b[o:end].decode("latin1"))
|
||||
return out
|
||||
except Exception: return []
|
||||
|
||||
per={}; allx=collections.Counter()
|
||||
for p in sorted(ROOT.rglob("*")):
|
||||
if not p.is_file() or p.suffix.upper() not in (".CPM",".EXE"): continue
|
||||
try: b=p.read_bytes()
|
||||
except Exception: continue
|
||||
if b[:2]!=b"MZ": continue
|
||||
x=exports(b)
|
||||
if x: per[p.name]=sorted(x)
|
||||
for n in x: allx[n]+=1
|
||||
|
||||
DONE={"ST_Boq.CPM","ST_EdiLWi.CPM","ST_KanJub.CPM","ST_SanCal.CPM","STmateQty8.CPM","ST_P_Joong.CPM",
|
||||
"ST_P_Collect.CPM","ST_P_ConEst.CPM","ST_U_ChgAmt.CPM","ST_U_ChgCode.CPM","ST_U_ConBD.CPM","ST_U_Convert.CPM"}
|
||||
print("### CPM/EXE export 전수 — 모듈 %d개, export %d개(중복포함)" % (len(per), sum(len(v) for v in per.values())))
|
||||
print(" 11번이 다룬 핵심 12개 말고 **%d개 모듈**이 남아 있었음\n" % len([k for k in per if k not in DONE]))
|
||||
for nm,x in sorted(per.items()):
|
||||
mark = "✅" if nm in DONE else " "
|
||||
print("%s %-22s %2d개 %s" % (mark, nm[:22], len(x), ", ".join(x)[:96]))
|
||||
print("\n### 여러 모듈이 공유하는 export (플러그인 공통 규약)")
|
||||
for n,c in allx.most_common(12): print(" %-22s %2d개 모듈" % (n,c))
|
||||
@@ -0,0 +1,70 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, io, pathlib, zipfile, struct, re, warnings
|
||||
from decimal import Decimal as D, ROUND_DOWN
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
B = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서\25년 산불진화임도(기번8)(울진소광)")
|
||||
def dbf(zf,name):
|
||||
b=zf.read(name); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
rows=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; d={};c=1
|
||||
for nm,ln in f: d[nm]=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
rows.append(d)
|
||||
return rows
|
||||
def g(r,k):
|
||||
try: return D(r.get(k) or "0")
|
||||
except Exception: return D(0)
|
||||
def dn(x): return D(x).quantize(D(1), rounding=ROUND_DOWN)
|
||||
STRUCT = re.compile(r"^(T\d|E\d|C\d|:)$")
|
||||
def direct(boq, sfx="1"):
|
||||
excl=set()
|
||||
for i,r in enumerate(boq):
|
||||
bd=(r.get("BDCODE"+sfx) or r.get("BDCODE1") or "").strip()
|
||||
if bd.startswith("C"):
|
||||
end=r.get("ENDCODE"); st=next((j for j,q in enumerate(boq) if q.get("SORTCODE")==end),None)
|
||||
if st is not None:
|
||||
for k in range(min(st,i),max(st,i)+1): excl.add(k)
|
||||
leaf=[r for i,r in enumerate(boq)
|
||||
if (r.get("BDCODE"+sfx) or r.get("BDCODE1")) and not STRUCT.match((r.get("BDCODE"+sfx) or r.get("BDCODE1")).strip()) and i not in excl]
|
||||
return (sum(g(r,"M_AMT"+sfx) for r in leaf), sum(g(r,"L_AMT"+sfx) for r in leaf),
|
||||
sum(g(r,"S_AMT"+sfx) for r in leaf), len(leaf))
|
||||
|
||||
orig = zipfile.ZipFile(B/"2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외).STC")
|
||||
chg = zipfile.ZipFile(B/"변경 1차분-2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외).STC")
|
||||
b11 = dbf(orig,"BOQ11.DBF"); b12 = dbf(orig,"BOQ12.DBF"); c11 = dbf(chg,"BOQ11.DBF")
|
||||
|
||||
print("### 원설계 파일 안의 두 표")
|
||||
for nm,t in (("BOQ11",b11),("BOQ12",b12)):
|
||||
M,L,S,n = direct(t)
|
||||
print(" %-6s %3d행 → 말단 %3d 재료 %13s 노무 %13s 경비 %13s" % (nm,len(t),n,f"{M:,}",f"{L:,}",f"{S:,}"))
|
||||
|
||||
wb = openpyxl.load_workbook(B/"2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외).xlsx", data_only=True, read_only=True)
|
||||
ws = wb["공사원가계산서"]; T={}
|
||||
for row in ws.iter_rows(min_row=1,max_row=45,max_col=7,values_only=True):
|
||||
cells=["" if v is None else v for v in row]
|
||||
lab="".join(str(c) for c in cells[:3] if isinstance(c,str)).replace(" ","")
|
||||
amt=next((c for c in cells[3:5] if isinstance(c,(int,float))),None)
|
||||
if lab and amt is not None and lab not in T: T[lab]=D(str(amt))
|
||||
print("\n### 출력 XLSX 가 어느 표를 썼나")
|
||||
tM=T.get("순공사원가재료비직접재료비"); tL=T.get("노무비직접노무비"); tS=T.get("경비산출경비")
|
||||
print(" 출력 재료 %13s 노무 %13s 경비 %13s" % (f"{tM:,}",f"{tL:,}",f"{tS:,}"))
|
||||
for nm,t in (("BOQ11",b11),("BOQ12",b12)):
|
||||
M,L,S,_ = direct(t)
|
||||
print(" %-6s %s" % (nm, "✅ 일치" if (M,L,S)==(tM,tL,tS) else "불일치"))
|
||||
|
||||
print("\n### 변경1차 파일의 BOQ11 차수1 이 원설계 파일의 BOQ12 와 같은가")
|
||||
M1,L1,S1,n1 = direct(c11,"1"); M2,L2,S2,n2 = direct(c11,"2")
|
||||
M12,L12,S12,_ = direct(b12)
|
||||
print(" 변경파일 차수1 말단 %3d 재료 %13s 노무 %13s 경비 %13s" % (n1,f"{M1:,}",f"{L1:,}",f"{S1:,}"))
|
||||
print(" 원설계 BOQ12 재료 %13s 노무 %13s 경비 %13s %s" % (f"{M12:,}",f"{L12:,}",f"{S12:,}",
|
||||
"✅ 같음" if (M1,L1,S1)==(M12,L12,S12) else "다름"))
|
||||
print(" 변경파일 차수2 말단 %3d 재료 %13s 노무 %13s 경비 %13s" % (n2,f"{M2:,}",f"{L2:,}",f"{S2:,}"))
|
||||
print("\n 직접공사비 당초 %s → 변경 %s 증감 %s" % (
|
||||
f"{M1+L1+S1:,}", f"{M2+L2+S2:,}", f"{(M2+L2+S2)-(M1+L1+S1):+,}"))
|
||||
pl = dbf(chg,"PRJLST.DBF")[0]
|
||||
print(" PRJLST 1차 T=%s 2차 T=%s (직접비 합과 %s)" % (
|
||||
f"{g(pl,'TCOST1'):,}", f"{g(pl,'TCOST2'):,}",
|
||||
"일치" if (g(pl,'TCOST1')==M1+L1+S1 and g(pl,'TCOST2')==M2+L2+S2) else "불일치"))
|
||||
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""설계변경 직접비 — 차수1(당초)·차수2(변경)를 내역에서 다시 세워 출력과 대조."""
|
||||
import sys, io, pathlib, zipfile, struct, re, warnings
|
||||
from decimal import Decimal as D, ROUND_DOWN
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def dn(x): return D(x).quantize(D(1), rounding=ROUND_DOWN)
|
||||
def dbf(zf,name):
|
||||
b=zf.read(name); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
rows=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; d={};c=1
|
||||
for nm,ln in f: d[nm]=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
rows.append(d)
|
||||
return rows
|
||||
def g(r,k):
|
||||
try: return D(r.get(k) or "0")
|
||||
except Exception: return D(0)
|
||||
STRUCT=re.compile(r"^(T\d|E\d|C\d|:)$")
|
||||
def direct(boq, s):
|
||||
excl=set()
|
||||
for i,r in enumerate(boq):
|
||||
bd=(r.get("BDCODE"+s) or r.get("BDCODE1") or "").strip()
|
||||
if bd.startswith("C"):
|
||||
end=r.get("ENDCODE"); st=next((j for j,q in enumerate(boq) if q.get("SORTCODE")==end),None)
|
||||
if st is not None:
|
||||
for k in range(min(st,i),max(st,i)+1): excl.add(k)
|
||||
leaf=[r for i,r in enumerate(boq)
|
||||
if (r.get("BDCODE"+s) or r.get("BDCODE1")) and not STRUCT.match((r.get("BDCODE"+s) or r.get("BDCODE1")).strip()) and i not in excl]
|
||||
ok=bad=0
|
||||
for r in leaf:
|
||||
for c,a in (("MAMT","M_AMT"),("LAMT","L_AMT"),("SAMT","S_AMT")):
|
||||
if dn(g(r,"QTY"+s)*g(r,c+s))==g(r,a+s): ok+=1
|
||||
else: bad+=1
|
||||
return (sum(g(r,"M_AMT"+s) for r in leaf), sum(g(r,"L_AMT"+s) for r in leaf),
|
||||
sum(g(r,"S_AMT"+s) for r in leaf), len(leaf), ok, bad)
|
||||
|
||||
BASE=pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서\20250527 2025년 계류보전사업(기번1-영덕.병곡.영리.산214-1)(변경)")
|
||||
stc=next(BASE.rglob("*.STC")); xls=next(p for p in BASE.rglob("*.xlsx") if p.stem==stc.stem)
|
||||
boq=dbf(zipfile.ZipFile(stc),"BOQ11.DBF")
|
||||
ws=openpyxl.load_workbook(xls,data_only=True)["공사원가계산서"]
|
||||
T={}
|
||||
for row in ws.iter_rows(min_row=1,max_row=45,max_col=6,values_only=True):
|
||||
cells=["" if v is None else v for v in row]
|
||||
lab="".join(str(c) for c in cells[:3] if isinstance(c,str)).replace(" ","")
|
||||
b=next((c for c in cells[3:4] if isinstance(c,(int,float))),None)
|
||||
c2=next((c for c in cells[4:5] if isinstance(c,(int,float))),None)
|
||||
if lab and b is not None: T[lab]=(D(str(b)), D(str(c2)) if c2 is not None else None)
|
||||
|
||||
print("### 설계변경 직접비 — 내역에서 다시 세움 (%s)" % stc.stem[:44])
|
||||
for s,tag in (("1","당초"),("2","변경")):
|
||||
M,L,S,n,ok,bad = direct(boq,s)
|
||||
print("\n-- 차수%s (%s) 말단 %d행, 행금액 절사 일치 %d / 불일치 %d" % (s,tag,n,ok,bad))
|
||||
for lab,calc,key in (("직접재료비",M,"순공사원가재료비직접재료비"),("직접노무비",L,"노무비직접노무비"),("산출경비",S,"경비산출경비")):
|
||||
pair=T.get(key)
|
||||
exp = pair[0] if s=="1" else (pair[1] if pair else None)
|
||||
if exp is None: print(" %-10s 계산 %14s (목표 없음)"%(lab,f"{calc:,}")); continue
|
||||
print(" %-10s 계산 %14s 출력 %14s %s"%(lab,f"{calc:,}",f"{exp:,}","일치" if calc==exp else "차 %+d"%(calc-exp)))
|
||||
M1,L1,S1,*_=direct(boq,"1"); M2,L2,S2,*_=direct(boq,"2")
|
||||
print("\n-- 증감")
|
||||
for lab,a,b,key in (("직접재료비",M1,M2,"순공사원가재료비직접재료비"),("직접노무비",L1,L2,"노무비직접노무비"),("산출경비",S1,S2,"경비산출경비")):
|
||||
print(" %-10s %+15s" % (lab, f"{b-a:,}"))
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""변경 차수 저장 구조 — DBF 슬롯 4벌 vs 화면 8차의 어긋남을 가른다."""
|
||||
import sys, io, re, pathlib, zipfile, struct, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
|
||||
def fields(b):
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append(b[o:o+11].rstrip(b"\0").decode("cp949","replace")); o+=32
|
||||
return f
|
||||
|
||||
print("### ① PRJLST.SMT 의 차수 섹션")
|
||||
for p in sorted(ROOT.rglob("*.STC")):
|
||||
try: z=zipfile.ZipFile(p); b=z.read("PRJLST.SMT")
|
||||
except Exception: continue
|
||||
secs=sorted({s for s in re.findall(r"\[([0-9]+)_KANJUB\]",
|
||||
"".join(ch for ch in b[0x200:].decode("cp949","replace")))}, key=int)
|
||||
print(" %-46s 섹션 %s" % (p.stem[:46], secs or "없음"))
|
||||
|
||||
print("\n### ② DBF 의 차수 슬롯 (접미사 숫자)")
|
||||
for p in list(sorted(ROOT.rglob("*.STC")))[:2]:
|
||||
z=zipfile.ZipFile(p)
|
||||
print("\n-- %s" % p.stem[:50])
|
||||
for t in ("PRJLST.DBF","BOQ11.DBF","COSTN.DBF"):
|
||||
try: f=fields(z.read(t))
|
||||
except Exception: continue
|
||||
sfx=collections.Counter()
|
||||
for n in f:
|
||||
m=re.search(r"(\d)$", n)
|
||||
if m: sfx[m.group(1)]+=1
|
||||
print(" %-12s 필드 %3d개 접미사별: %s" % (t, len(f), dict(sorted(sfx.items()))))
|
||||
|
||||
print("\n### ③ 화면·코드가 말하는 차수 상한")
|
||||
SYS=pathlib.Path(r"C:\경영정보(주)\System")
|
||||
pat=[rb"[1-9]\xec\xb0\xa8\xeb\xb3\x80\xea\xb2\xbd"] # UTF-8 'n차변경' — cp949 로 다시
|
||||
import re as _re
|
||||
need=[("%d차변경"%i).encode("cp949") for i in range(1,10)]
|
||||
hit=collections.Counter()
|
||||
for p in SYS.rglob("*"):
|
||||
if not p.is_file(): continue
|
||||
try: b=p.read_bytes()
|
||||
except Exception: continue
|
||||
if b[:2]!=b"MZ": continue
|
||||
for i,n in enumerate(need,1):
|
||||
if n in b: hit[i]+=1
|
||||
print(" 'n차변경' 문자열을 가진 모듈 수: %s" % dict(sorted(hit.items())))
|
||||
for tok in (b"BOQ1", b"BOQ2"):
|
||||
c=sum(1 for p in SYS.rglob("*") if p.is_file() and p.suffix.upper()==".CPM" and tok in p.read_bytes())
|
||||
print(" '%s' 를 가진 CPM 모듈 %d개" % (tok.decode(), c))
|
||||
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""화면 선택축 ↔ PRJLST.SMT 저장키 대응, 그리고 간접비 식 템플릿 전수."""
|
||||
import sys, io, re, pathlib, zipfile, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
PRAC = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
SYS = pathlib.Path(r"C:\경영정보(주)\System")
|
||||
|
||||
# ── PRJLST.SMT 키 모으기 ─────────────────────────
|
||||
keys=collections.Counter()
|
||||
for p in sorted(PRAC.rglob("*.STC")):
|
||||
try: b=zipfile.ZipFile(p).read("PRJLST.SMT")
|
||||
except Exception: continue
|
||||
txt=b[0x200:].decode("cp949","replace")
|
||||
for k in re.findall(r"([0-9]?_?[A-Z][A-Z0-9_]{2,})=", txt): keys[re.sub(r"^\d_","",k)]+=1
|
||||
print("### PRJLST.SMT 저장키 %d종 (접두 차수 제거)" % len(keys))
|
||||
grp=collections.defaultdict(list)
|
||||
for k in keys:
|
||||
m=re.match(r"^([CRIBY])_(.+)$", k)
|
||||
grp[m.group(1) if m else "기타"].append(k)
|
||||
LBL={"C":"금액 직접입력","R":"요율","I":"선택 인덱스","B":"켬/끔","Y":"연도·기타","기타":"섹션·기타"}
|
||||
for g in ("C","R","I","B","Y","기타"):
|
||||
if g not in grp: continue
|
||||
v=sorted(grp[g])
|
||||
print("\n■ %s_ %s %d개" % (g, LBL[g], len(v)))
|
||||
for i in range(0,len(v),5): print(" " + " ".join("%-20s"%x for x in v[i:i+5]))
|
||||
|
||||
# ── 간접비 식 템플릿 전수 (바이너리) ─────────────
|
||||
print("\n\n### 간접비 식 템플릿 — 바이너리에서 전수")
|
||||
TMPL=re.compile(rb"[A-Z_\[\]0-9+\-*/(). ]*\[[A-Z_0-9]{2,12}\][A-Z_\[\]0-9+\-*/(). ]{4,80}")
|
||||
found=collections.Counter()
|
||||
for p in SYS.rglob("*"):
|
||||
if not p.is_file() or p.suffix.upper()!=".CPM": continue
|
||||
try: b=p.read_bytes()
|
||||
except Exception: continue
|
||||
for m in TMPL.finditer(b):
|
||||
s=m.group().decode("latin1").strip()
|
||||
if s.count("[")<1 or len(s)<10: continue
|
||||
if not re.search(r"[*/+]", s): continue
|
||||
if re.search(r"[a-z]", s): continue
|
||||
found[s]+=1
|
||||
print(" 서로 다른 템플릿 %d개 (상위 30)" % len(found))
|
||||
for s,c in found.most_common(30): print(" %-72s %2d" % (s[:72], c))
|
||||
@@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""남은 시트 전수 훑기 — 머리 몇 줄과 수량×단가=금액 반올림 판정."""
|
||||
import sys, io, pathlib, warnings, collections
|
||||
from decimal import Decimal as D, ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def q(x,m): return D(x).quantize(D(1), rounding=m)
|
||||
def dec(v):
|
||||
if v in (None,""): return None
|
||||
try: return D(str(v))
|
||||
except Exception: return None
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
REST = ["중기시간금액집계표","일위대가수량금액집계표","단가산출근거수량금액집계표","일식견적수량금액집계표",
|
||||
"총괄설계내역서","총괄내역서","착공내역서","설계변경내역서",
|
||||
"일위대가목록표","단가산출근거목록표","중기목록표","재료비목록표","노무비목록표","경비목록표","일식견적목록표"]
|
||||
stat = collections.Counter(); head = {}
|
||||
for p in sorted(ROOT.rglob("*.xlsx")):
|
||||
if p.name.startswith("~$"): continue
|
||||
try: wb=openpyxl.load_workbook(p,data_only=True,read_only=True)
|
||||
except Exception: continue
|
||||
if "공사원가계산서" not in wb.sheetnames: continue
|
||||
for s in REST:
|
||||
if s not in wb.sheetnames: continue
|
||||
ws=wb[s]
|
||||
rows=[list(r) for r in ws.iter_rows(min_row=1,max_row=min(ws.max_row,400),max_col=14,values_only=True)]
|
||||
if s not in head:
|
||||
head[s]=[r for r in rows[:8]]
|
||||
for r in rows:
|
||||
nums=[(i,dec(v)) for i,v in enumerate(r) if isinstance(v,(int,float))]
|
||||
for a in range(len(nums)-2):
|
||||
qy,up,amt = nums[a][1],nums[a+1][1],nums[a+2][1]
|
||||
if not (qy and up and amt) or qy<=0 or up<=0 or amt<=0: continue
|
||||
raw=qy*up
|
||||
if raw==0 or abs(raw-amt) > raw*D("0.015"): continue
|
||||
for m,tag in ((ROUND_DOWN,"절사"),(ROUND_HALF_UP,"사사오입"),(ROUND_HALF_EVEN,"은행가")):
|
||||
if q(raw,m)==amt: stat[(s,tag)]+=1
|
||||
stat[(s,"총")]+=1; break
|
||||
|
||||
print("### 남은 시트 — 수량×단가=금액 반올림 판정")
|
||||
print("%-26s %6s %10s %10s %10s" % ("시트","표본","절사","사사오입","은행가"))
|
||||
for s in REST:
|
||||
t=stat[(s,"총")]
|
||||
if not t:
|
||||
print("%-26s %6s (계산 쌍 없음 = 색인·표지 성격)" % (s[:26],"-")); continue
|
||||
f=lambda k: "%d(%.0f%%)"%(stat[(s,k)],100*stat[(s,k)]/t)
|
||||
print("%-26s %6d %10s %10s %10s" % (s[:26],t,f("절사"),f("사사오입"),f("은행가")))
|
||||
|
||||
print("\n\n### 각 시트 머리 (구조 확인)")
|
||||
for s in REST:
|
||||
if s not in head: continue
|
||||
print("\n-- %s" % s)
|
||||
for r in head[s]:
|
||||
line=" | ".join(("" if v is None else (("%g"%v) if isinstance(v,float) else str(v)))[:15] for v in r[:10])
|
||||
if line.strip(" |"): print(" %s" % line)
|
||||
@@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""SIxMemo(*.SMT) 본문 덤프 — 0x200 부터가 본문. 원본은 읽기만."""
|
||||
import sys, io, re, pathlib
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
D = pathlib.Path(r"C:\경영정보(주)\STmate 자료\설계자료\실습(맛보기)")
|
||||
for f in ("PRJLST.SMT","COSTN.SMT","QCODE.SMT"):
|
||||
b=(D/f).read_bytes()
|
||||
out=[]
|
||||
for m in re.finditer(rb"[\x20-\x7e\xa1-\xfe]{3,}", b[0x200:]):
|
||||
try: out.append((0x200+m.start(), m.group().decode("cp949")))
|
||||
except Exception: pass
|
||||
print("\n" + "="*92)
|
||||
print("### %s %d B 본문 문자열 %d개" % (f, len(b), len(out)))
|
||||
for off,s in out: print(" 0x%05X %s" % (off, s[:86]))
|
||||
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""COSTN.SMT 전개 텍스트 전수 파싱 — 자원 참조·품·할증·중간라벨·Q식.
|
||||
문법 출처: original/원가계산/STmate/SIxMemo_구조.md (2026-08-15)."""
|
||||
import sys, io, re, pathlib, zipfile, collections, json
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
SRC = [pathlib.Path(r"C:\경영정보(주)\STmate 자료\설계자료\실습(맛보기)\COSTN.SMT")]
|
||||
for p in sorted(pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서").rglob("*.STC")):
|
||||
SRC.append(p)
|
||||
|
||||
RE_REF = re.compile(r"&([A-Z])(\d{5})([A-Z])&") # &M00034M& 종류·번호·성분
|
||||
RE_LABEL = re.compile(r":=\s*([A-Z]\d{2})") # :=A01
|
||||
RE_QTY = re.compile(r"([\d.]+)\s*'([^']{1,8})'") # 0.31'Kg'
|
||||
RE_PCT = re.compile(r"([\d.]+)\s*'%'") # 5'%'
|
||||
RE_Q = re.compile(r"\bQ\s*=\s*([^=\n']{3,60})") # Q = 3600*q*K*f*E/Cm
|
||||
|
||||
def body(b):
|
||||
out=[]
|
||||
for m in re.finditer(rb"[\x20-\x7e\xa1-\xfe]{3,}", b[0x200:]):
|
||||
try: out.append(m.group().decode("cp949"))
|
||||
except Exception: pass
|
||||
return out
|
||||
|
||||
tot=collections.Counter(); refs=collections.Counter(); comp=collections.Counter()
|
||||
qforms=collections.Counter(); pcts=collections.Counter(); units=collections.Counter()
|
||||
files=0; lines=0
|
||||
for s in SRC:
|
||||
try:
|
||||
if s.suffix.upper()==".STC":
|
||||
z=zipfile.ZipFile(s)
|
||||
nm=next((n for n in z.namelist() if n.upper()=="COSTN.SMT"), None)
|
||||
if not nm: continue
|
||||
b=z.read(nm)
|
||||
else: b=s.read_bytes()
|
||||
except Exception: continue
|
||||
if b[8:15]!=b"SIxMemo": continue
|
||||
files+=1
|
||||
for ln in body(b):
|
||||
lines+=1
|
||||
for k,n,c in RE_REF.findall(ln):
|
||||
refs[k]+=1; comp[c]+=1; tot["참조"]+=1
|
||||
for l in RE_LABEL.findall(ln): tot["중간라벨"]+=1
|
||||
for v,u in RE_QTY.findall(ln):
|
||||
if u!="%": units[u]+=1; tot["품(수량+단위)"]+=1
|
||||
for v in RE_PCT.findall(ln): pcts[v]+=1; tot["할증%"]+=1
|
||||
for q in RE_Q.findall(ln):
|
||||
qq=re.sub(r"\s+","",q)
|
||||
if len(qq)>4 and any(ch in qq for ch in "*/"): qforms[qq[:44]]+=1; tot["Q식"]+=1
|
||||
|
||||
print("### COSTN.SMT 전수 파싱 — 파일 %d개, 본문 줄 %d개" % (files, lines))
|
||||
print("\n요소별 검출")
|
||||
for k,v in tot.most_common(): print(" %-14s %5d" % (k,v))
|
||||
print("\n자원 참조 종류 (앞글자)")
|
||||
for k,v in refs.most_common(): print(" %s %5d" % (k,v))
|
||||
print("\n가져오는 성분 (끝글자)")
|
||||
for k,v in comp.most_common(): print(" %s %5d" % (k,v))
|
||||
print("\n할증률 값 분포 (상위 12)")
|
||||
for k,v in pcts.most_common(12): print(" %-8s %4d회" % (k+"%", v))
|
||||
print("\n단위 분포 (상위 12)")
|
||||
for k,v in units.most_common(12): print(" %-8s %4d회" % (k,v))
|
||||
print("\nQ식 꼴 (상위 10)")
|
||||
for k,v in qforms.most_common(10): print(" %-46s %3d회" % (k,v))
|
||||
@@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""STC 프로젝트 안의 표들이 실제로 읽히는지 본다. 복호화 시도 없음."""
|
||||
import pathlib, zipfile, struct, sys, io, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
roots = [pathlib.Path(r"C:\경영정보(주)"),
|
||||
pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")]
|
||||
stcs = []
|
||||
for r in roots:
|
||||
if r.exists(): stcs += sorted(r.rglob("*.STC"))
|
||||
print("찾은 STC %d개" % len(stcs))
|
||||
for s in stcs[:3]: print(" %s" % s)
|
||||
|
||||
def dbf_state(b):
|
||||
if len(b) < 64: return "짧음", 0, 0
|
||||
try:
|
||||
nrec, hlen, rlen = struct.unpack_from("<IHH", b, 4)
|
||||
n = 0; o = 32
|
||||
while o < len(b) and b[o] != 0x0D: n += 1; o += 32
|
||||
if not (1 <= n <= 300 and hlen and rlen): return "머리깨짐", 0, 0
|
||||
if nrec == 0: return "빈표", 0, n
|
||||
rec = b[hlen:hlen+rlen]
|
||||
pr = sum(1 for c in rec if 32 <= c < 127 or c >= 0xA1) / max(len(rec),1)
|
||||
return ("평문" if pr > 0.85 else "본문암호화"), nrec, n
|
||||
except Exception: return "해석실패", 0, 0
|
||||
|
||||
if stcs:
|
||||
for s in stcs[:2]:
|
||||
print("\n### %s" % s.name)
|
||||
try:
|
||||
z = zipfile.ZipFile(s)
|
||||
except Exception as e:
|
||||
print(" ZIP 열기 실패: %s" % e); continue
|
||||
enc = any(i.flag_bits & 0x1 for i in z.infolist())
|
||||
print(" ZIP 암호: %s 파일 %d개" % ("걸림" if enc else "없음", len(z.infolist())))
|
||||
for i in z.infolist():
|
||||
if not i.filename.upper().endswith((".DBF",)): continue
|
||||
try: b = z.read(i)
|
||||
except Exception as e: print(" %-16s 읽기실패" % i.filename); continue
|
||||
st, nrec, nf = dbf_state(b)
|
||||
print(" %-16s %-10s rec=%-6s fld=%s" % (i.filename, st, nrec, nf))
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, io, pathlib, re, zipfile, struct, collections, warnings, difflib
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
SRC = open(r"C:\Users\umsan\AppData\Local\Temp\claude\D--aislo-wt-sub\df75230a-b05b-4088-ad34-bb0c9da0a312\scratchpad\t1_consist.py",
|
||||
encoding="utf-8").read().split("items = collections.Counter()")[0]
|
||||
SRC = SRC.replace('sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")','')
|
||||
SRC = "\n".join(l for l in SRC.split("\n") if not l.strip().startswith("print("))
|
||||
ns = {}
|
||||
exec(SRC, ns)
|
||||
per = ns["per"]; tabs_by_proj = ns["tabs_by_proj"]
|
||||
out = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
def P(*a): print(*a, file=out)
|
||||
P("### 규모·기간 의존 항목 — 열이 고정인가 (RATE_R 행 0~44 안)")
|
||||
for lab in ("간접노무비","기타경비","일반관리비","이윤"):
|
||||
vals = {k: per[k][lab] for k in per if lab in per[k]}
|
||||
if len(vals) < 3: continue
|
||||
colsets=[]; detail=[]
|
||||
for k,v in vals.items():
|
||||
M = tabs_by_proj[k].get("RATE_R") or []
|
||||
hits = {(i,j+1) for i,row in enumerate(M[:45]) for j,x in enumerate(row) if x and abs(x-v)<1e-6}
|
||||
colsets.append({c for _,c in hits}); detail.append((k,v,sorted(hits)[:6]))
|
||||
common = set.intersection(*colsets) if colsets else set()
|
||||
P("\n■ %s 관측 %d건, 값 %s" % (lab, len(vals), sorted({v for v in vals.values()})))
|
||||
P(" 모든 프로젝트 공통 열: %s" % (sorted(common) or "없음"))
|
||||
for k,v,h in detail:
|
||||
P(" %-30s %7.3f%% %s" % (k[:30], v, ", ".join("(%d,%d)"%x for x in h) or "없음"))
|
||||
out.flush()
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""T1 대체 — 통제시험 없이, 모든 프로젝트에서 동시에 성립하는 좌표만 남긴다."""
|
||||
import sys, io, pathlib, re, zipfile, struct, collections, warnings, difflib
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
def dbf(zf,name):
|
||||
b=zf.read(name); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
M=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; v=[];c=1
|
||||
for nm,ln in f:
|
||||
s=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
try: v.append(round(float(s),4) if s else 0.0)
|
||||
except ValueError: v.append(0.0)
|
||||
M.append(v)
|
||||
return M
|
||||
RE = re.compile(r"([\d.]+)\s*%")
|
||||
def rates(xp):
|
||||
wb=openpyxl.load_workbook(xp,data_only=True,read_only=True)
|
||||
if "공사원가계산서" not in wb.sheetnames: return {}
|
||||
ws=wb["공사원가계산서"]; out={}
|
||||
for row in ws.iter_rows(min_row=1,max_row=45,max_col=7,values_only=True):
|
||||
cells=["" if v is None else v for v in row]
|
||||
lab=re.sub(r"\s+","",next((str(c) for c in cells[:3] if isinstance(c,str) and str(c).strip()),""))
|
||||
note=next((str(c) for c in cells[4:7] if isinstance(c,str) and "%" in str(c)),"")
|
||||
if not lab or not note: continue
|
||||
m=RE.search(note)
|
||||
if m and lab not in out: out[lab]=float(m.group(1))
|
||||
return out
|
||||
|
||||
pairs=[]
|
||||
for stc in sorted(ROOT.rglob("*.STC")):
|
||||
best=None;bs=0
|
||||
for x in list(stc.parent.rglob("*.xlsx"))+list(stc.parent.parent.rglob("*.xlsx")):
|
||||
if x.name.startswith("~$"): continue
|
||||
s=difflib.SequenceMatcher(None,stc.stem,x.stem).ratio()
|
||||
if s>bs: bs,best=s,x
|
||||
if best and bs>0.55: pairs.append((stc,best))
|
||||
|
||||
# 항목 -> {프로젝트: 요율}
|
||||
per = {}
|
||||
tabs_by_proj = {}
|
||||
for stc,xp in pairs:
|
||||
try: r = rates(xp)
|
||||
except Exception: continue
|
||||
if not r: continue
|
||||
z = zipfile.ZipFile(stc); tabs={}
|
||||
for n in z.namelist():
|
||||
if re.match(r"RATE.*\.DBF$", n, re.I):
|
||||
try: tabs[n.upper().replace(".DBF","")] = dbf(z,n)
|
||||
except Exception: pass
|
||||
key = stc.stem[:28]
|
||||
tabs_by_proj[key]=tabs; per[key]=r
|
||||
|
||||
items = collections.Counter()
|
||||
for k,r in per.items():
|
||||
for lab in r: items[lab]+=1
|
||||
print("프로젝트 %d건, 항목별 관측 수" % len(per))
|
||||
print()
|
||||
print("%-22s %5s %6s %s" % ("항목","관측","값종류","전 프로젝트에서 동시에 맞는 좌표"))
|
||||
print("-"*96)
|
||||
for lab, cnt in items.most_common():
|
||||
vals = {k: per[k][lab] for k in per if lab in per[k]}
|
||||
if len(vals) < 3: continue
|
||||
# 후보 좌표: 첫 프로젝트에서 값이 맞는 모든 자리
|
||||
first = next(iter(vals))
|
||||
cand = set()
|
||||
for tn, M in tabs_by_proj[first].items():
|
||||
for i,row in enumerate(M):
|
||||
for j,x in enumerate(row):
|
||||
if x and abs(x-vals[first])<1e-6: cand.add((tn,i,j))
|
||||
for k in list(vals)[1:]:
|
||||
keep=set()
|
||||
for (tn,i,j) in cand:
|
||||
M = tabs_by_proj[k].get(tn)
|
||||
if M and i<len(M) and j<len(M[i]) and abs(M[i][j]-vals[k])<1e-6: keep.add((tn,i,j))
|
||||
cand = keep
|
||||
if not cand: break
|
||||
nv = len(set(vals.values()))
|
||||
s = ", ".join("%s[%d,%d]"%(t,i,j+1) for t,i,j in sorted(cand)[:4]) or "없음"
|
||||
if len(cand)>4: s += " …외 %d" % (len(cand)-4)
|
||||
print("%-22s %5d %6d %s" % (lab[:22], len(vals), nv, s))
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, io, pathlib, struct, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
ROOT = pathlib.Path(r"C:\경영정보(주)\System\ST_BSC")
|
||||
def rd(p):
|
||||
b=p.read_bytes(); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
M=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; v=[];c=1
|
||||
for nm,ln in f:
|
||||
s=r[c:c+ln].decode("cp949","replace").strip();c+=ln
|
||||
try: v.append(round(float(s),4) if s else 0.0)
|
||||
except ValueError: v.append(0.0)
|
||||
M.append(v)
|
||||
return M
|
||||
|
||||
print("### JB_R 45·46행을 연도별로 — 부속표 영역의 열 의미 굳히기\n")
|
||||
for name in ["2019(상_0107)_제비율.JB_R","2022(상_0103)_제비율.JB_R",
|
||||
"2024(상_0101)_제비율.JB_R","2025(상_0101)_제비율.JB_R","2026(상_0101)_제비율.JB_R"]:
|
||||
p = ROOT/name
|
||||
if not p.exists(): continue
|
||||
M = rd(p)
|
||||
for r in (45, 46):
|
||||
vals = [(c+1, M[r][c]) for c in range(len(M[r])) if M[r][c]]
|
||||
print("%-26s 행%2d : %s" % (name[:26], r, " ".join("열%d=%g"%v for v in vals)))
|
||||
print()
|
||||
|
||||
print("### 해석")
|
||||
print("""
|
||||
행45 열1 = 산재보험료 (%) — 출력 엑셀에서 확인
|
||||
행45 열11~17 = 고용보험료 등급 1~7 (%) — 폼 rGrd_Rate_Kobo 가 7행(등급·요율)인 것과 일치,
|
||||
값이 내림차순이고 프로젝트는 열17(7등급이하)을 씀
|
||||
행46 열1 = 건강보험료 (%)
|
||||
행46 열6 = 연금보험료 (%)
|
||||
행46 열11 = 노인장기요양보험료 (%) — 건강보험료에 곱함
|
||||
행20 열17·18 = 산업안전보건관리비 율(%)·기초액(원)
|
||||
""")
|
||||
@@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""T1 확장 — 짝을 폴더 기준으로 넓게 잡고, 항목별 좌표 교집합을 낸다."""
|
||||
import sys, io, pathlib, re, zipfile, struct, collections, warnings, difflib
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore")
|
||||
import openpyxl
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
|
||||
def dbf(zf, name):
|
||||
b = zf.read(name); nrec, hlen, rlen = struct.unpack_from("<IHH", b, 4)
|
||||
f=[]; o=32
|
||||
while b[o] != 0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"), b[o+16])); o+=32
|
||||
M=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; v=[]; c=1
|
||||
for nm,ln in f:
|
||||
s=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
try: v.append(round(float(s),4) if s else 0.0)
|
||||
except ValueError: v.append(0.0)
|
||||
M.append(v)
|
||||
return M
|
||||
|
||||
RATE_RE = re.compile(r"([\d]+(?:\.[\d]+)?)\s*%")
|
||||
def sheet_rates(xp):
|
||||
wb = openpyxl.load_workbook(xp, data_only=True, read_only=True)
|
||||
if "공사원가계산서" not in wb.sheetnames: return []
|
||||
ws = wb["공사원가계산서"]; out=[]; seen=set()
|
||||
for row in ws.iter_rows(min_row=1, max_row=40, max_col=8, values_only=True):
|
||||
cells = ["" if v is None else str(v) for v in row]
|
||||
label = re.sub(r"\s+","", next((c for c in cells[:3] if c.strip()), ""))
|
||||
note = next((c for c in cells[4:7] if "%" in c), "")
|
||||
if not note or not label: continue
|
||||
for m in RATE_RE.finditer(note):
|
||||
k=(label, m.group(1))
|
||||
if k in seen: continue
|
||||
seen.add(k); out.append((label, float(m.group(1))))
|
||||
return out
|
||||
|
||||
# 폴더 안의 STC 와 xlsx 를 이름 비슷한 순으로 짝지음
|
||||
pairs=[]
|
||||
for stc in sorted(ROOT.rglob("*.STC")):
|
||||
best=None; bs=0
|
||||
for x in list(stc.parent.rglob("*.xlsx")) + list(stc.parent.parent.rglob("*.xlsx")):
|
||||
if x.name.startswith("~$"): continue
|
||||
s = difflib.SequenceMatcher(None, stc.stem, x.stem).ratio()
|
||||
if s > bs: bs, best = s, x
|
||||
if best and bs > 0.55: pairs.append((stc, best, bs))
|
||||
print("짝 %d건" % len(pairs))
|
||||
for s,x,r in pairs: print(" %.2f %s" % (r, s.stem[:52]))
|
||||
|
||||
coord = collections.defaultdict(collections.Counter) # 항목 -> 좌표 카운트
|
||||
uniq = collections.defaultdict(collections.Counter) # 항목 -> 유일히 잡힌 좌표
|
||||
for stc, xp, _ in pairs:
|
||||
try: rates = sheet_rates(xp)
|
||||
except Exception: continue
|
||||
if not rates: continue
|
||||
z = zipfile.ZipFile(stc)
|
||||
tabs = {}
|
||||
for n in z.namelist():
|
||||
if re.match(r"RATE.*\.DBF$", n, re.I):
|
||||
try: tabs[n.upper().replace(".DBF","")] = dbf(z,n)
|
||||
except Exception: pass
|
||||
for label, v in rates:
|
||||
found=[]
|
||||
for tn,M in tabs.items():
|
||||
for r,row in enumerate(M):
|
||||
for c,x in enumerate(row):
|
||||
if x and abs(x-v) < 1e-6: found.append("%s[%d,%d]"%(tn,r,c+1))
|
||||
for f in found: coord[label][f]+=1
|
||||
if len(found)==1: uniq[label][found[0]]+=1
|
||||
|
||||
print("\n=== 유일하게 잡힌 좌표 (다른 후보 없이 딱 하나) ===")
|
||||
for lab in sorted(uniq):
|
||||
for k,n in uniq[lab].most_common():
|
||||
print(" %-22s → %-16s 프로젝트 %d건에서 유일" % (lab[:22], k, n))
|
||||
|
||||
print("\n=== 여러 후보 중 모든 프로젝트에 공통인 좌표 ===")
|
||||
N = len(pairs)
|
||||
for lab in sorted(coord):
|
||||
all_hit = [k for k,n in coord[lab].items() if n == max(coord[lab].values()) and n >= 3]
|
||||
if all_hit and lab not in uniq:
|
||||
print(" %-22s → %s (각 %d건)" % (lab[:22], ", ".join(all_hit[:5]), max(coord[lab].values())))
|
||||
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""STmate 고유 폼 전수 — 캡션·선택지를 뽑아 UI 카탈로그를 만든다. 원본은 읽기만."""
|
||||
import sys, io, re, pathlib, importlib.util, collections
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
spec=importlib.util.spec_from_file_location("pd", r"D:\aislo-wt\sub\resources\knowledge\original\경쟁사 프로그램\STmate 분석\40_스크립트\parse_dfm.py")
|
||||
pd=importlib.util.module_from_spec(spec); spec.loader.exec_module(pd)
|
||||
ROOT=pathlib.Path(r"C:\경영정보(주)\System")
|
||||
THIRD=re.compile(r"^(Dclhrp60|designide60|RTL60|VCL60|DBRTL60|BDERTL60|RealGrid|Apollo|dsnide|JDLCHK)",re.I)
|
||||
HRLIB={"HReportUf","HRPageRf","HRPreVUf","HRSaveUf","LoginDialog","PasswordDialog"}
|
||||
|
||||
def blobs(b):
|
||||
out=[]; pos=[m.start() for m in re.finditer(rb"TPF0", b)]
|
||||
for i,s in enumerate(pos):
|
||||
e = pos[i+1] if i+1<len(pos) else len(b)
|
||||
p=s+4
|
||||
if p>=len(b): continue
|
||||
n=b[p]
|
||||
if not (3<=n<=40): continue
|
||||
cls=b[p+1:p+1+n]
|
||||
if not re.fullmatch(rb"T[A-Za-z0-9_]+", cls): continue
|
||||
p2=p+1+n; n2=b[p2] if p2<len(b) else 0
|
||||
if not (1<=n2<=40): continue
|
||||
nm=b[p2+1:p2+1+n2]
|
||||
if not re.fullmatch(rb"[A-Za-z_][A-Za-z0-9_]*", nm): continue
|
||||
out.append((nm.decode("latin1"), b[s:e]))
|
||||
return out
|
||||
|
||||
def walk(n, out):
|
||||
if isinstance(n,dict):
|
||||
pr=n.get("properties") or {}
|
||||
cap=pr.get("Caption")
|
||||
items=None
|
||||
for k,v in pr.items():
|
||||
if k.lower().endswith("items.strings") or k=="Items": items=v
|
||||
if isinstance(cap,str) and any(ord(c)>127 for c in cap): out["cap"].append(cap.strip())
|
||||
if isinstance(items,list) and items: out["items"].append((n.get("name",""), [str(x) for x in items]))
|
||||
for c in n.get("children",[]) or []: walk(c,out)
|
||||
|
||||
cat={}
|
||||
for p in sorted(ROOT.rglob("*")):
|
||||
if not p.is_file() or THIRD.match(p.stem): continue
|
||||
try: b=p.read_bytes()
|
||||
except Exception: continue
|
||||
if b[:2]!=b"MZ": continue
|
||||
for nm, blob in blobs(b):
|
||||
if nm in HRLIB or nm in cat: continue
|
||||
try:
|
||||
r=pd.Reader(blob); r.take(4); root=r.component(root=True)
|
||||
except Exception: continue
|
||||
o={"cap":[],"items":[]}; walk(root,o)
|
||||
if o["cap"] or o["items"]: cat[nm]=(p.name,o)
|
||||
|
||||
print("### 파싱 성공한 STmate 고유 폼 %d개\n" % len(cat))
|
||||
tot_i=0
|
||||
for nm,(mod,o) in sorted(cat.items()):
|
||||
if not o["items"]: continue
|
||||
tot_i+=len(o["items"])
|
||||
title=o["cap"][0] if o["cap"] else ""
|
||||
print("■ %-20s %-20s %s" % (nm[:20], mod[:20], ("«%s»"%title[:30]) if title else ""))
|
||||
for cn, its in o["items"][:8]:
|
||||
print(" %-18s %s" % (cn[:18], ", ".join(its)[:88]))
|
||||
print("\n선택지를 가진 폼 %d개, 선택축 %d개" % (sum(1 for _,(_,o) in cat.items() if o["items"]), tot_i))
|
||||
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""호표 성분 소계의 반올림 방식을 가른다 — 절사 / 사사오입 / 은행가 / 원값."""
|
||||
import sys, io, pathlib, warnings, re, collections
|
||||
from decimal import Decimal as D, ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def q(x,m): return D(x).quantize(D(1), rounding=m)
|
||||
def dec(v):
|
||||
if v in (None,""): return D(0)
|
||||
try: return D(str(v))
|
||||
except Exception: return D(0)
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
|
||||
def scan(path, sheet, stat):
|
||||
try: wb = openpyxl.load_workbook(path, data_only=True, read_only=True)
|
||||
except Exception: return
|
||||
if sheet not in wb.sheetnames: return
|
||||
ws = wb[sheet]
|
||||
rows=[list(r) for r in ws.iter_rows(min_row=1,max_row=ws.max_row,max_col=12,values_only=True)]
|
||||
blk=None
|
||||
for r in rows:
|
||||
nm=(str(r[0]) if r[0] is not None else "").strip().replace(" ","")
|
||||
if re.match(r"^제?\d+호표", nm): blk=[]; continue
|
||||
if blk is None: continue
|
||||
if nm=="합계":
|
||||
# 합계행의 금액칸 위치를 찾는다: 성분 원합과 가장 가까운 칸
|
||||
for idx,lab in ((7,"노무"),(9,"재료"),(11,"경비")):
|
||||
raw = sum(dec(c[idx]) for c in blk)
|
||||
if raw == 0: continue
|
||||
cands = {i: dec(r[i]) for i in (idx-1, idx) if r[i] is not None}
|
||||
for rm, tag in ((ROUND_DOWN,"절사"),(ROUND_HALF_UP,"사사오입"),(ROUND_HALF_EVEN,"은행가")):
|
||||
v = q(raw, rm)
|
||||
if v in cands.values(): stat[(sheet,tag)] += 1
|
||||
if raw in cands.values(): stat[(sheet,"원값")] += 1
|
||||
stat[(sheet,"총")] += 1
|
||||
blk=None; continue
|
||||
blk.append(r)
|
||||
|
||||
stat = collections.Counter()
|
||||
for p in ROOT.rglob("*.xlsx"):
|
||||
if p.name.startswith("~$"): continue
|
||||
for s in ("중기사용료","일위대가표"): scan(p, s, stat)
|
||||
|
||||
for sheet in ("중기사용료","일위대가표"):
|
||||
tot = stat[(sheet,"총")]
|
||||
if not tot: continue
|
||||
print("\n### %s — 성분 소계 %d건" % (sheet, tot))
|
||||
for tag in ("절사","사사오입","은행가","원값"):
|
||||
n = stat[(sheet,tag)]
|
||||
print(" %-8s %5d건 %5.1f%%" % (tag, n, 100*n/tot))
|
||||
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""단가산출근거 — Q식과 성분단가 = 시간단가/Q 의 반올림 규칙을 전수 확인."""
|
||||
import sys, io, pathlib, warnings, re, collections
|
||||
from decimal import Decimal as D, ROUND_DOWN, ROUND_HALF_UP
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
NUM = r"([\d,]+(?:\.\d+)?)"
|
||||
RE_Q = re.compile(r"Q\s*=\s*([0-9.*/\sqKfECm+\-]+?)\s*=\s*"+NUM)
|
||||
RE_C = re.compile(r"(노\s*무\s*비|재\s*료\s*비|경\s*비)\s*:\s*"+NUM+r"\s*/\s*Q\s*=\s*"+NUM)
|
||||
def d(s): return D(s.replace(",",""))
|
||||
|
||||
stat=collections.Counter(); ex=[]
|
||||
for p in sorted(ROOT.rglob("*.xlsx")):
|
||||
if p.name.startswith("~$"): continue
|
||||
try: wb=openpyxl.load_workbook(p,data_only=True,read_only=True)
|
||||
except Exception: continue
|
||||
if "단가산출근거" not in wb.sheetnames: continue
|
||||
curQ=None
|
||||
for row in wb["단가산출근거"].iter_rows(min_row=1,max_row=wb["단가산출근거"].max_row,max_col=3,values_only=True):
|
||||
txt = " ".join(str(c) for c in row[:2] if isinstance(c,str))
|
||||
if not txt: continue
|
||||
if re.search(r"제\s*\d+\s*호표", txt): curQ=None; continue
|
||||
m=RE_Q.search(txt)
|
||||
if m:
|
||||
curQ = d(m.group(2)); stat["Q식 발견"]+=1; continue
|
||||
for mm in RE_C.finditer(txt):
|
||||
if curQ in (None, D(0)): continue
|
||||
시간단가 = d(mm.group(2)); 결과 = d(mm.group(3))
|
||||
raw = 시간단가/curQ
|
||||
for pl,tag in ((1,"소수1"),(0,"정수")):
|
||||
for rm,rt in ((ROUND_DOWN,"절사"),(ROUND_HALF_UP,"사사오입")):
|
||||
if raw.quantize(D(1).scaleb(-pl), rounding=rm) == 결과: stat[(tag,rt)]+=1
|
||||
stat["성분 총"]+=1
|
||||
if len(ex)<4 and raw.quantize(D("0.1"),rounding=ROUND_DOWN)!=결과:
|
||||
ex.append((p.stem[:22], 시간단가, curQ, raw, 결과))
|
||||
|
||||
print("### 단가산출근거 — Q식과 성분단가")
|
||||
print(" Q식 발견 %d개, 성분단가 식 %d개" % (stat["Q식 발견"], stat["성분 총"]))
|
||||
t=stat["성분 총"]
|
||||
if t:
|
||||
print("\n 성분단가 = 시간당 성분단가 / Q 의 반올림")
|
||||
for pl in ("소수1","정수"):
|
||||
for rt in ("절사","사사오입"):
|
||||
n=stat[(pl,rt)]
|
||||
print(" %-5s %-5s %5d건 %5.1f%%" % (pl, rt, n, 100*n/t))
|
||||
for e in ex: print(" ✗ %-22s %s / %s = %s ≠ %s" % e)
|
||||
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""일위대가 호표 참조 그래프 — 구성행이 다른 호표를 가리키는지, 위상순서가 성립하는지."""
|
||||
import sys, io, pathlib, warnings, re, collections
|
||||
from decimal import Decimal as D, ROUND_DOWN
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def dn(x): return D(x).quantize(D(1), rounding=ROUND_DOWN)
|
||||
def dec(v):
|
||||
if v in (None,""): return None
|
||||
try: return D(str(v))
|
||||
except Exception: return None
|
||||
P = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서\04.산불 봉화\01. 내역서\2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km.xlsx")
|
||||
wb = openpyxl.load_workbook(P, data_only=True)
|
||||
|
||||
def blocks(sheet):
|
||||
ws=wb[sheet]
|
||||
rows=[list(r) for r in ws.iter_rows(min_row=1,max_row=ws.max_row,max_col=12,values_only=True)]
|
||||
out=[]; cur=None; title=None
|
||||
for r in rows:
|
||||
t=(str(r[0]) if r[0] else "").strip()
|
||||
k=t.replace(" ","")
|
||||
if re.match(r"^제?\d+호표$", k): cur={"no":int(re.sub(r"\D","",k)),"title":None,"comp":[],"sum":None}; out.append(cur); continue
|
||||
if cur is None: continue
|
||||
if cur["title"] is None and t and r[3] and not r[2]:
|
||||
cur["title"]=(t, str(r[1] or "").strip(), str(r[3] or "")); continue
|
||||
if k=="합계": cur["sum"]=r; cur=None; continue
|
||||
if t: cur["comp"].append(r)
|
||||
return out
|
||||
|
||||
B = blocks("일위대가표")
|
||||
print("### 일위대가표 호표 %d개" % len(B))
|
||||
# 호표 합계(단가) 사전
|
||||
unit = {}
|
||||
for b in B:
|
||||
if b["title"] and b["sum"]:
|
||||
unit[(b["title"][0], b["title"][1])] = dec(b["sum"][5])
|
||||
print(" 제목으로 찾을 수 있는 호표 %d개" % len(unit))
|
||||
|
||||
edges=collections.Counter(); hit=miss=0; ex=[]
|
||||
for b in B:
|
||||
for c in b["comp"]:
|
||||
nm=(str(c[0]) if c[0] else "").strip(); sz=str(c[1] or "").strip()
|
||||
up=dec(c[4])
|
||||
if up is None: continue
|
||||
key=(nm,sz)
|
||||
if key in unit and unit[key]==up:
|
||||
edges[b["no"]]+=1; hit+=1
|
||||
elif key in unit:
|
||||
miss+=1
|
||||
if len(ex)<4: ex.append((b["no"],nm[:16],sz[:12],up,unit[key]))
|
||||
print("\n 구성행이 다른 호표를 가리키고 단가가 일치: %d건" % hit)
|
||||
print(" 이름은 같은데 단가가 다름: %d건" % miss)
|
||||
for e in ex: print(" 제%d호표 %-16s %-12s 구성 %s ≠ 호표합계 %s" % e)
|
||||
print("\n 호표 사이 참조를 가진 호표 %d개 (전체 %d개)" % (len(edges), len(B)))
|
||||
|
||||
# 순환 검사
|
||||
adj=collections.defaultdict(set)
|
||||
byname={}
|
||||
for b in B:
|
||||
if b["title"]: byname[(b["title"][0],b["title"][1])]=b["no"]
|
||||
for b in B:
|
||||
for c in b["comp"]:
|
||||
key=((str(c[0]) if c[0] else "").strip(), str(c[1] or "").strip())
|
||||
if key in byname and byname[key]!=b["no"]: adj[b["no"]].add(byname[key])
|
||||
color={}
|
||||
cyc=[]
|
||||
def dfs(u, path):
|
||||
color[u]=1
|
||||
for v in adj[u]:
|
||||
if color.get(v)==1: cyc.append(path+[u,v])
|
||||
elif color.get(v) is None: dfs(v, path+[u])
|
||||
color[u]=2
|
||||
for b in B:
|
||||
if color.get(b["no"]) is None: dfs(b["no"], [])
|
||||
print(" 순환 참조: %s" % ("없음 → 위상순서 계산 가능" if not cyc else cyc[:2]))
|
||||
depth={}
|
||||
def dep(u):
|
||||
if u in depth: return depth[u]
|
||||
depth[u]=0
|
||||
depth[u]= 1+max([dep(v) for v in adj[u]], default=-1)
|
||||
return depth[u]
|
||||
for b in B: dep(b["no"])
|
||||
mx=max(depth.values()) if depth else 0
|
||||
print(" 참조 깊이 최대 %d단 (분포: %s)" % (mx, dict(collections.Counter(depth.values()))))
|
||||
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""누락 확인 ① 노임 시간당 환산식 ② 집계표 반올림 ③ INITIAL 설정"""
|
||||
import sys, io, pathlib, warnings, re, collections
|
||||
from decimal import Decimal as D, ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def dec(v):
|
||||
if v in (None,""): return None
|
||||
try: return D(str(v))
|
||||
except Exception: return None
|
||||
def q(x,m): return D(x).quantize(D(1), rounding=m)
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
XL=[p for p in sorted(ROOT.rglob("*.xlsx")) if not p.name.startswith("~$")]
|
||||
|
||||
print("### ① 노임 시간당 환산식 — 환율및기초자료 시트")
|
||||
for p in XL:
|
||||
try: wb=openpyxl.load_workbook(p,data_only=True,read_only=True)
|
||||
except Exception: continue
|
||||
if "환율및기초자료" not in wb.sheetnames: continue
|
||||
ws=wb["환율및기초자료"]
|
||||
rows=[list(r) for r in ws.iter_rows(min_row=1,max_row=ws.max_row,max_col=10,values_only=True)]
|
||||
print("\n-- %s" % p.stem[:52])
|
||||
for r in rows:
|
||||
nm = r[2] if len(r)>2 else None
|
||||
일 = dec(r[3]) if len(r)>3 else None
|
||||
식 = r[4] if len(r)>4 else None
|
||||
시 = dec(r[6]) if len(r)>6 else None
|
||||
if not (nm and 일 and 시 and isinstance(식,str)): continue
|
||||
expr = 식.strip().lstrip("*").strip()
|
||||
try:
|
||||
factor = eval(expr, {"__builtins__":{}})
|
||||
except Exception:
|
||||
factor = None
|
||||
calc = q(D(str(float(일)*factor)), ROUND_DOWN) if factor else None
|
||||
ok = "일치" if calc==시 else ("차 %+d"%(calc-시) if calc is not None else "식 해석실패")
|
||||
print(" %-16s 일 %9s × %-18s = %9s 출력 %9s %s" % (str(nm)[:16], f"{일:,}", expr, f"{calc:,}" if calc else "-", f"{시:,}", ok))
|
||||
|
||||
print("\n\n### ② 집계표 행 금액 반올림 — 수량 × 단가")
|
||||
SHEETS = ["재료비수량금액집계표","노무비수량금액집계표","경비수량금액집계표","중기시간금액집계표"]
|
||||
stat=collections.Counter()
|
||||
for p in XL:
|
||||
try: wb=openpyxl.load_workbook(p,data_only=True,read_only=True)
|
||||
except Exception: continue
|
||||
for s in SHEETS:
|
||||
if s not in wb.sheetnames: continue
|
||||
ws=wb[s]
|
||||
for r in ws.iter_rows(min_row=1,max_row=ws.max_row,max_col=12,values_only=True):
|
||||
nums=[(i,dec(v)) for i,v in enumerate(r) if isinstance(v,(int,float))]
|
||||
if len(nums)<3: continue
|
||||
# 수량·단가·금액 세 쌍을 찾는다
|
||||
for a in range(len(nums)-2):
|
||||
qty,up,amt = nums[a][1],nums[a+1][1],nums[a+2][1]
|
||||
if not (qty and up and amt) or qty<=0 or up<=0: continue
|
||||
raw=qty*up
|
||||
if abs(raw-amt) > raw*D("0.02"): continue
|
||||
for m,tag in ((ROUND_DOWN,"절사"),(ROUND_HALF_UP,"사사오입"),(ROUND_HALF_EVEN,"은행가")):
|
||||
if q(raw,m)==amt: stat[(s,tag)]+=1
|
||||
if raw==amt: stat[(s,"원값")]+=1
|
||||
stat[(s,"총")]+=1
|
||||
break
|
||||
for s in SHEETS:
|
||||
t=stat[(s,"총")]
|
||||
if not t: print(" %-22s 표본 없음"%s); continue
|
||||
print(" %-22s 표본 %4d 절사 %4d(%.0f%%) 사사오입 %4d(%.0f%%) 은행가 %4d(%.0f%%) 원값 %d" % (
|
||||
s, t, stat[(s,"절사")],100*stat[(s,"절사")]/t, stat[(s,"사사오입")],100*stat[(s,"사사오입")]/t,
|
||||
stat[(s,"은행가")],100*stat[(s,"은행가")]/t, stat[(s,"원값")]))
|
||||
@@ -1,5 +1,18 @@
|
||||
# STmate 분석
|
||||
|
||||
> ## 🔗 STmate 지식은 **세 폴더**에 나뉘어 있음 — 갈라지지 않게 서로를 가리킴
|
||||
>
|
||||
> | 트랙 | 자리 | 담당 |
|
||||
> |---|---|---|
|
||||
> | ① **계산 로직** | `technical_info/01_임도/05_원가정보/` | 밑수·단수처리·제비율·기계경비·노임·자재 적용. **Aislo 구현의 정본** |
|
||||
> | ② **STC 포맷** | `original/원가계산/STmate/` | 컨테이너·필드사전·난독화·SIxMemo·INITIAL. import/export 명세 |
|
||||
> | ③ **프로그램 분석** | `original/경쟁사 프로그램/STmate 분석/` | 실행모듈·UI 폼·계산사슬·골든셋 회귀 |
|
||||
>
|
||||
> ⚠ **새 STmate 작업을 시작하기 전에 세 폴더를 모두 볼 것.** 2026-09-13 에 ③이 ①②를 모르고 시작해
|
||||
> 단수처리 규칙·`INITIAL` 시트·`SMT` 문법·`BDQTY` 난독화를 다시 분석한 일이 있음 (③의 `21_정정과_트랙통합.md`).
|
||||
|
||||
|
||||
|
||||
경쟁사 설계예산 프로그램 **STmate**(경영정보(주)) 를 정적으로 뜯어본 기록.
|
||||
원본은 저장소 밖 `C:\경영정보(주)` — **원본은 손대지 않음**, 여기에만 정리물을 둠.
|
||||
옆 폴더 `../오솔길 분석/` 과 같은 얼개.
|
||||
@@ -40,6 +53,17 @@
|
||||
| 12 | 원가계산 4형식과 RATE표 복원 — **설정 83축** |
|
||||
| 13 | 통제시험 목록 T1~T11 — 아직 못 가른 경계 |
|
||||
| 14 | 최종 분석완료 보고서 |
|
||||
| 15 | **재구현 가능성 검증** — 무엇을 만들 수 있고 무엇이 막혀 있나 |
|
||||
| 16 | **골든셋 회귀 결과** — 원가계산을 다시 세워 원 단위로 맞춘 실증 |
|
||||
| 17 | **단가조립 검증** — 일위대가·중기 시간당 사용료 구조, 손료율 실측 |
|
||||
| 18 | **누락 계산로직 점검** — 노임 환산식·단가산출 Q식·설정 스냅샷 |
|
||||
| 19 | **진행률 장부** — 어디까지 봤고 무엇이 남았나 |
|
||||
| 20 | **설정파일과 보고서 카탈로그** — 간접비 코드 사전, 서식 75종 |
|
||||
| 21 | **정정과 트랙 통합** — `SMT` 정정, 지식DB 세 트랙, 암호화의 성격 |
|
||||
| 22 | **UI 폼 분석** — 선택축 272개, 화면에 남아 있던 계산 규칙 |
|
||||
| 23 | **`COSTN.SMT` 전수파싱** — 단가산출 원장, Q식 10꼴, 운반 사이클 |
|
||||
| 24 | **모듈구조 전수** — 46개 export, 계산 공용 라이브러리 73함수, 동글 |
|
||||
| 25 | **설정키 전수와 식템플릿** — 저장키 241종, 간접비 식 26개, 설정 사슬 닫힘 |
|
||||
|
||||
## 확정된 굵직한 것
|
||||
|
||||
@@ -49,6 +73,10 @@
|
||||
- 원가계산 **네 형식**(일반·수공·실적일반·실적수공)은 출력 모양이 아니라 **공식 집합이 통째로 다름**
|
||||
- 기준자료 `JB_R` 35열 확장 시점 **2020-05-27**, 기존 1~20열은 한 칸도 안 바뀜
|
||||
- 설정 **83축** 전수. 화면 순번과 내부값이 **뒤집힌 축 둘** 있음 — 그대로 저장하면 정반대 공식이 걸림
|
||||
- 보험 요율의 **표 좌표 확정** — 산재 `RATE_R[45,1]`, 건강 `[46,1]`, 연금 `[46,6]`, 장기요양 `[46,11]`, 고용 등급1~7 `[45,11..17]`. 5개 연도 시계열이 실제 법정요율 변천과 맞음
|
||||
- **계산 사슬 전체가 드러남** — 일 노임 → 시간당 → 중기 사용료 → 단가산출(Q식) → 일위대가 → 내역 → 간접비 → 총공사비 (18번)
|
||||
- **원가계산 전 과정을 원 단위로 재현함** — 실무 프로젝트 6건, 내역 행 1,515건 무결, 총공사비까지 1원도 안 틀림 (16번)
|
||||
- ⚠ **기초단가 원천은 전부 암호화** — 자재 5.7만 건·노임·중기·표준시장단가를 한 건도 못 읽음. 「계산 엔진」은 만들 수 있으나 「기준자료를 갖춘 완제품」은 못 만듦 (15번)
|
||||
|
||||
## 원자료 — `30_원자료/`
|
||||
|
||||
|
||||
@@ -6,6 +6,19 @@ status: draft
|
||||
목적: STC(STmate 프로젝트 파일) import/export 구현을 위한 컨테이너·DBF 명세·검증 근거 축적
|
||||
---
|
||||
|
||||
> ## 🔗 STmate 지식은 **세 폴더**에 나뉘어 있음 — 갈라지지 않게 서로를 가리킴
|
||||
>
|
||||
> | 트랙 | 자리 | 담당 |
|
||||
> |---|---|---|
|
||||
> | ① **계산 로직** | `technical_info/01_임도/05_원가정보/` | 밑수·단수처리·제비율·기계경비·노임·자재 적용. **Aislo 구현의 정본** |
|
||||
> | ② **STC 포맷** | `original/원가계산/STmate/` | 컨테이너·필드사전·난독화·SIxMemo·INITIAL. import/export 명세 |
|
||||
> | ③ **프로그램 분석** | `original/경쟁사 프로그램/STmate 분석/` | 실행모듈·UI 폼·계산사슬·골든셋 회귀 |
|
||||
>
|
||||
> ⚠ **새 STmate 작업을 시작하기 전에 세 폴더를 모두 볼 것.** 2026-09-13 에 ③이 ①②를 모르고 시작해
|
||||
> 단수처리 규칙·`INITIAL` 시트·`SMT` 문법·`BDQTY` 난독화를 다시 분석한 일이 있음 (③의 `21_정정과_트랙통합.md`).
|
||||
|
||||
|
||||
|
||||
> **왜 이 폴더인가**: STC는 설계자가 발주처에 제출하는 **최종 산출물**이며, Aislo도 STC **불러오기(import)·내보내기(export)가 필수 요구사항**이다(사용자 확정 2026-08-15). 따라서 STC 포맷 해석은 연구성 작업이 아니라 **납품 호환성 확보를 위한 구현 선행 명세**다.
|
||||
> 계산 로직(밑수·절사·채택 규칙)은 `technical_info/01_임도/05_원가정보/`가 담당하고, 이 폴더는 **포맷 구조·필드 사전·검증 근거**만 담는다. 두 트랙은 분리한다.
|
||||
> 실무 관측 진입점 = [original/실무문서/_STC포맷분석.md](../../실무문서/_STC포맷분석.md) (이 폴더로 명세 이관, 그 문서는 요약·링크로 유지).
|
||||
|
||||
@@ -9,6 +9,19 @@ status: draft
|
||||
last_updated: 2026-08-14
|
||||
---
|
||||
|
||||
> ## 🔗 STmate 지식은 **세 폴더**에 나뉘어 있음 — 갈라지지 않게 서로를 가리킴
|
||||
>
|
||||
> | 트랙 | 자리 | 담당 |
|
||||
> |---|---|---|
|
||||
> | ① **계산 로직** | `technical_info/01_임도/05_원가정보/` | 밑수·단수처리·제비율·기계경비·노임·자재 적용. **Aislo 구현의 정본** |
|
||||
> | ② **STC 포맷** | `original/원가계산/STmate/` | 컨테이너·필드사전·난독화·SIxMemo·INITIAL. import/export 명세 |
|
||||
> | ③ **프로그램 분석** | `original/경쟁사 프로그램/STmate 분석/` | 실행모듈·UI 폼·계산사슬·골든셋 회귀 |
|
||||
>
|
||||
> ⚠ **새 STmate 작업을 시작하기 전에 세 폴더를 모두 볼 것.** 2026-09-13 에 ③이 ①②를 모르고 시작해
|
||||
> 단수처리 규칙·`INITIAL` 시트·`SMT` 문법·`BDQTY` 난독화를 다시 분석한 일이 있음 (③의 `21_정정과_트랙통합.md`).
|
||||
|
||||
|
||||
|
||||
관련 문서: [제비율](제비율.md) · [설계·감리대가](설계·감리대가.md) · [원가_기초데이터_원천](원가_기초데이터_원천.md) · [노임단가_적용](노임단가_적용.md) · [자재단가_적용](자재단가_적용.md) · [기계경비_산정](기계경비_산정.md) · [수량산출_일반](../04_수량분석정보/수량산출_일반.md) · [품셈_적용기준](../04_수량분석정보/품셈_적용기준.md)
|
||||
|
||||
## 1. 공사원가 구성 트리
|
||||
|
||||
Reference in New Issue
Block a user