From f76e5d659f0cbe3a0c1bec6377c6884cd97b0482 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 13 Sep 2026 12:26:44 +0900 Subject: [PATCH] =?UTF-8?q?knowledge(=EA=B2=BD=EC=9F=81=EC=82=AC):=20STmat?= =?UTF-8?q?e=20=EB=8B=A8=EA=B0=80=EC=A1=B0=EB=A6=BD=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=E2=80=94=20=EC=A4=91=EA=B8=B0=20=EC=86=90=EB=A3=8C=EC=9C=A8?= =?UTF-8?q?=20=EC=8B=A4=EC=B8=A1,=20=EC=84=B1=EB=B6=84=20=EC=86=8C?= =?UTF-8?q?=EA=B3=84=EB=8A=94=20=EC=A0=88=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 15번이 「BDQTY 암호화로 단가 조립 불가」·「중기 손료 계수 어디에도 없음」이라 적은 것을 정정. 출력 XLSX 의 일위대가표·중기사용료 시트에 구성 자원·수량· 단가·성분별 금액이 전부 평문으로 있음. 입력 원장은 못 읽어도 조립 규칙은 읽힘. 밝힌 것 - 호표 성분 소계는 절사. 중기사용료 398건 100%, 일위대가 325/345. 집계표 행 금액(ROUND)과는 다른 자리이므로 섞으면 안 됨 - 중기 시간당 사용료 = 손료(취득가 x 손료율) + 운전원 + 주연료 + 잡품(연료비 x 율) 성분별로 절사한 뒤 합 - 잡품은 수량x단가가 아니라 연료비에 비율을 곱하는 가산 행 - 시간당 손료율 25개 장비 실측. 같은 장비도 암석 조건이면 손료율이 오르고 (0.2085 -> 0.2405), 부착 장비는 취득가가 부착물만의 값임 - 일위대가 호표가 다른 호표를 참조하므로 위상순서 계산이 실제로 필요함 ⚠ 손료율 값은 STmate 가 담은 것임. Aislo 기본값은 현행 건설기계 경비 산정 기준에서 확정할 것이며 이 표는 대조용. 문서 17 신규, 검증 스크립트 2개 추가. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Prk9BCHG1EMAywk9k8wegA --- .../20_분석/00_분석_요약과_읽는법.md | 4 + .../20_분석/15_재구현_가능성_검증.md | 12 +- .../STmate 분석/20_분석/17_단가조립_검증.md | 127 ++++++++++++++++++ .../40_스크립트/machine_rate_extract.py | 59 ++++++++ .../40_스크립트/unit_assembly_check.py | 50 +++++++ .../경쟁사 프로그램/STmate 분석/README.md | 1 + 6 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/17_단가조립_검증.md create mode 100644 resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/machine_rate_extract.py create mode 100644 resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/unit_assembly_check.py diff --git a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/00_분석_요약과_읽는법.md b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/00_분석_요약과_읽는법.md index 1fbfa47c..c16be6e5 100644 --- a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/00_분석_요약과_읽는법.md +++ b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/00_분석_요약과_읽는법.md @@ -23,6 +23,7 @@ | 14 최종 분석완료 보고서 | 도달 수준, A등급 확정 목록, 남은 것, 완료 판정 기준, 산출물 | https://share.onorca.dev/a/AmgldP7q_Qzc | | 15 재구현 가능성 검증 | **무엇을 만들 수 있고 무엇이 막혀 있나** — 층별 확보표, 문서 오류 정정, T1 좌표 확정 | https://share.onorca.dev/a/t6HGy0HlbEBl | | 16 골든셋 회귀 결과 | **원가계산을 다시 세워 원 단위로 맞춘 실증** — 프로젝트 6건 전 항목 일치 | (저장소 파일) | +| 17 단가조립 검증 | **일위대가·중기 시간당 사용료 구조와 손료율 실측** | (저장소 파일) | > **정본은 저장소 파일이다** — `resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/`. > 위 Orca 링크는 보기용이며, 문서를 지식DB 자리로 옮기면서 주소가 한 번 바뀌었다. @@ -54,6 +55,9 @@ | 내역 말단 행 판별 | 없음 | **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 | 분석일: 2026-09-13 대상: `C:\경영정보(주)` diff --git a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/15_재구현_가능성_검증.md b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/15_재구현_가능성_검증.md index a2212d4f..24988258 100644 --- a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/15_재구현_가능성_검증.md +++ b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/15_재구현_가능성_검증.md @@ -28,7 +28,7 @@ Aislo 입장에서는 이것으로 충분함 — Aislo는 **자기 기초단가 | | `*.STBSC` 28개 (노임·기계경비 배포) | ZIP 컨테이너, 내부 암호 | ❌ | | **3 단가 계산** | `COSTN.DBF` 단가 네 성분 | **평문 — 읽힘** | ✅ | | | `COSTN.SMT` 구성 토큰 | 평문, 문법 문서화됨 | ✅ | -| | 중기 손료 계수(감가상각·정비·관리율) 실제 값 | **어디에도 없음** | ❌ | +| | 중기 시간당 손료율·연료·잡품률 | **출력 `중기사용료` 시트에 전부 있음** (17번) | ✅ | | | 자재 6후보 중 자동채택 규칙 | C등급 | ❌ | | **4 내역** | `BOQ11.DBF` 수량·단가·금액 | 평문 | ✅ | | | 계층 집계(`SORTCODE`·`ENDCODE`·`ITM_TYP`) | 문서화됨 | ✅ | @@ -126,8 +126,8 @@ A등급 불변조건으로 적혀 있으나 실데이터에 **59행 중 2행이 ### 지금 자료로 불가능한 것 1. **기초단가 배포 체계** — 자재 5.7만 건·노임·중기·표준시장단가를 못 읽음. 암호를 풀지 않는 한 영구 불가 -2. **수량 할증 재현** — `BDQTY` 암호화로 `X_QTY`·`X_HAL` 실값을 못 봄 -3. **중기 시간당 단가 재현** — 손료 계수(감가상각률·정비율·관리비율) 실제 값이 자료에 없음 +2. **수량 할증 재현** — `BDQTY` 암호화로 `X_QTY`·`X_HAL` 실값을 못 봄. 출력에는 **할증 후 수량만** 남음 +3. ~~중기 시간당 단가 재현~~ → **가능해짐** (17번). 출력 `중기사용료` 호표에 손료율·취득가·연료·잡품률이 다 들어 있음 4. **자재 자동채택** — 6후보 중 무엇을 고르는지 규칙 없음 5. **수공형식 검증** — `RATE_A`·`RATE_B`가 실무 표본에 없음 @@ -148,7 +148,7 @@ A등급 불변조건으로 적혀 있으나 실데이터에 **59행 중 2행이 |---:|---|---| | ~~1~~ | ~~골든셋 회귀 구현~~ | ✅ **완료 — 16번.** 프로젝트 6건 전 항목 원 단위 일치 | | 2 | 남은 요율 좌표(연금·일반관리비·이윤·환경보전비) 확정 | 통제시험 T1 나머지 | -| 3 | 중기 손료 계수를 **법령·품셈에서** 확보 | STmate 자료로는 영구 불가 — 국가 기준으로 대체 | -| 4 | `BDQTY` 대조시험 (T7) | 할증 규칙 | +| 3 | 중기 손료 계수를 **법령·품셈과 대조** | 값은 17번에서 확보했으나, Aislo 기본값은 현행 기준에서 가져와야 함 | +| 4 | `BDQTY` 대조시험 (T7) | 할증 규칙 — 여전히 막힌 유일한 입력 | -3번은 STmate를 뜯어서 얻을 수 없음. `resources/knowledge/technical_info/` 의 **건설기계 경비 산정 기준**으로 가야 함. +3번의 값 자체는 17번 §4에 25개 장비분이 있음. 다만 **Aislo 기본값은 STmate 값이 아니라 현행 「건설기계 경비 산정 기준」에서** 확정해야 하며, 17번 표는 대조용임. diff --git a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/17_단가조립_검증.md b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/17_단가조립_검증.md new file mode 100644 index 00000000..ed75c1d2 --- /dev/null +++ b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/17_단가조립_검증.md @@ -0,0 +1,127 @@ +# 단가 조립 검증 — 일위대가와 중기 시간당 사용료 + +실행일: 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이 말한 의존 그래프가 실제로 필요하다. + +## 6. 이로써 바뀌는 판정 + +| 항목 | 15번의 판정 | 지금 | +|---|---|---| +| 중기 시간당 단가 재현 | ❌ 손료 계수가 어디에도 없음 | ✅ **호표에 손료율·취득가·연료·잡품률이 전부 있음** | +| 단가 조립 규칙 | ❌ `BDQTY` 암호화로 불가 | ✅ **출력 시트로 조립 규칙·반올림 확인됨** | +| 성분 소계 반올림 | 미확정 | ✅ **절사** (중기 398건 100%) | + +여전히 막힌 것은 **입력 쪽**이다. + +- `BDQTY`의 수량 **할증** 필드(`X_QTY`·`X_HAL`) 실값 — 출력에는 할증 후 수량만 남는다 +- 품셈에서 구성 수량이 **어떻게 뽑혔는가** +- 자재 6후보 중 **채택 규칙** + +## 7. 다음 + +1. 일위대가 호표 사이 **참조 그래프**를 복원해 위상순서 계산을 재현 — 지금 자료로 됨 +2. `단가산출근거` 시트 구조 확인 — 시트는 있으나 이번 구획 규칙으로는 안 잡힘 +3. 자재단가대비표 시트로 **6후보 채택** 단서 찾기 — 시트 이름이 이미 있음 diff --git a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/machine_rate_extract.py b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/machine_rate_extract.py new file mode 100644 index 00000000..afa10a50 --- /dev/null +++ b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/machine_rate_extract.py @@ -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)) diff --git a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/unit_assembly_check.py b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/unit_assembly_check.py new file mode 100644 index 00000000..f4ef0194 --- /dev/null +++ b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/40_스크립트/unit_assembly_check.py @@ -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)) diff --git a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/README.md b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/README.md index a6b764ec..82a6487c 100644 --- a/resources/knowledge/original/경쟁사 프로그램/STmate 분석/README.md +++ b/resources/knowledge/original/경쟁사 프로그램/STmate 분석/README.md @@ -42,6 +42,7 @@ | 14 | 최종 분석완료 보고서 | | 15 | **재구현 가능성 검증** — 무엇을 만들 수 있고 무엇이 막혀 있나 | | 16 | **골든셋 회귀 결과** — 원가계산을 다시 세워 원 단위로 맞춘 실증 | +| 17 | **단가조립 검증** — 일위대가·중기 시간당 사용료 구조, 손료율 실측 | ## 확정된 굵직한 것