From 9158a886426192c93d4b57f965a33dacb8de46d6 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 01:57:00 +0900 Subject: [PATCH 1/4] =?UTF-8?q?knowledge(=EC=B6=95C):=20=EC=A7=91=EA=B3=84?= =?UTF-8?q?=ED=91=9C=20=EB=8B=A8=EA=B0=80=20=EC=97=B4=C2=B7=EC=A4=91?= =?UTF-8?q?=EA=B8=B0=20=EB=B0=98=EC=98=AC=EB=A6=BC=20=EC=A0=95=EC=A0=95=20?= =?UTF-8?q?=E2=80=94=20=EA=B3=A8=EB=93=A0=EC=85=8B=20=EC=8B=A4=EC=B8=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0154EMdwPYNPuKc9eZv8SLEX --- docs/raw/verification/2026-09-13_축C_명세.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/raw/verification/2026-09-13_축C_명세.md b/docs/raw/verification/2026-09-13_축C_명세.md index 4cd301b1..2b3a1862 100644 --- a/docs/raw/verification/2026-09-13_축C_명세.md +++ b/docs/raw/verification/2026-09-13_축C_명세.md @@ -328,7 +328,8 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 | 자리 | 규칙 | 근거 | | --- | --- | --- | | **호표 안 성분 소계** | **절사** | 중기사용료 398건 100% · 일위대가 345건 94.2% | -| **집계표 행 금액** | `ROUND` | 자원·일위대가·중기 집계표 | +| **집계표 행 금액** | `ROUND` | 자원 셋(재료비·노무비·경비) **279/279** | +| **중기시간금액집계표** | **성분마다 반올림한 뒤 합** | **131/144**. ⚠ 합계를 한 번에 반올림하면 107/144 로 떨어짐 | | **내역에 실릴 때** | 정수 절사 | 착공내역서 178건 100% | | **단가산출 성분단가** | 소수 1자리 절사 | 211건 중 137건 설명 | | 내부 계산 | 은행가(최근접-짝수) | STmate 내부. ⚠ 뽑은 엑셀은 사사오입이라 20개 경계 중 10개가 갈림 | From 7be245db2bcbe284a7465df9637d544630065535 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 07:48:34 +0900 Subject: [PATCH 2/4] =?UTF-8?q?knowledge(=EC=B6=95C=20=EB=AA=85=EC=84=B8):?= =?UTF-8?q?=20=ED=92=88=EC=85=88=20=ED=8C=90=20=EA=B7=9C=EC=B9=99=C2=B7?= =?UTF-8?q?=EB=B0=98=EC=98=AC=EB=A6=BC=20=EA=B3=A0=EC=8B=9C=20=EA=B7=BC?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 2장 — FP- 는 산림사업 표준품셈 한 판, 건설품셈은 기계 층만. 고시 2025-82호 총칙 7 조문 근거 - 7장 — 금액의 단위표준 표 다섯 줄과 실측 대조. 넷은 맞고 「총액 1,000원 미만버림」만 미확인 - 7장 — 수량의 단위표준 [주] 셋. 「규정 없는 자리 소수 2자리」는 이 표에도 없는 자리에만 - 막히면 산림품셈을 먼저 볼 것 — 덤프 운반이 건설 8-2-8 만 보다 막혔던 자리 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0154EMdwPYNPuKc9eZv8SLEX --- docs/raw/verification/2026-09-13_축C_명세.md | 43 ++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/raw/verification/2026-09-13_축C_명세.md b/docs/raw/verification/2026-09-13_축C_명세.md index 2b3a1862..bcb21055 100644 --- a/docs/raw/verification/2026-09-13_축C_명세.md +++ b/docs/raw/verification/2026-09-13_축C_명세.md @@ -90,6 +90,24 @@ FP-<절>-<절>-<절> 예) FP-04-02-02 (벌목, 5m 미만) - 정본은 **품셈 절 번호**. 표의 행은 그 절의 **조건별 변형**(`variant` 칸)임 - **이름은 표시용, 잇기는 코드로.** 이름으로 맞추는 코드가 남아 있으면 그것이 곧 결함임 + +### ⭐ 어느 품셈 판인가 — 2026-09-14 확인 (조문 근거 있음) + +**`FP-` 는 산림사업 표준품셈 한 판임.** 건설공사 표준품셈은 **공종 축이 아니라 기계 층에만** 들어옴. + +``` +공종 축 477/477 FP- · 원천 pum_forest_2026.json (산림청고시 2025-82호, 2026-01-01) +기계 층 pum_const_2026.json · mach_base_2026.json (건설품셈 제8장 — 취득가·손료계수·연료·운전원) +빌려 온 몇 곳 「교차 참조」 딱지를 붙임 (예: 뿌리뽑기 밑수 3-9-2) +``` + +**근거 — 같은 고시 총칙 7**: 「본 품셈에 명시되지 않은 품은 타 부문(토목, 건축, 전기, 기계 등)의 +표준품셈에 명시된 부분의 품을 적용하고, **타 부문과 유사한 공종의 품은 본 품셈을 우선하여 적용**한다.」 + +⇒ 지금 얼개가 이 조문 그대로임. **건설품셈 절번호로 선 공종 줄은 0 건**(소스에 「8-2-8」 참조 0건). +⚠ 자료를 찾을 때 건설품셈만 보면 「값이 없다」고 잘못 결론 내기 쉬움 — 덤프 운반이 그랬음 +(건설 8-2-8 에는 단위중량·적재용량 표가 없으나 **산림 10-12 [주]에 토사 1.9 · 암 2.4 ton/㎥ · 덤프 15 ton 이 있었음**). +**막히면 산림품셈 쪽을 먼저 볼 것.** - 부모·잎이 있음. ⚠ **「부모에 수량 금지」는 반쪽만 맞았음** — 2026-09-13 정정. **부모가 두 모양이고 규칙이 다름**(랩탑 메인 실측). @@ -334,6 +352,31 @@ B08 인계본 `ground_class_aliases` — **B09 가 안 읽음**)을 한 벌로 | **단가산출 성분단가** | 소수 1자리 절사 | 211건 중 137건 설명 | | 내부 계산 | 은행가(최근접-짝수) | STmate 내부. ⚠ 뽑은 엑셀은 사사오입이라 20개 경계 중 10개가 갈림 | +### ⭐⭐ 2026-09-14 — 위 실측이 **고시 조문과 그대로 맞았음** + +**산림청고시 제2025-82호 「산림사업 표준품셈」 1-2-2 「2. 금액의 단위표준」**: + +| 종목 | 단위 | 지위 | 비고 | 우리 실측과 | +| --- | --- | --- | --- | --- | +| 설계서의 **총액** | 원 | **1,000** | 미만버림 | ⚠ **아직 확인 안 됨** — 데스크탑 서브가 재는 중 | +| 설계서의 소계 | 원 | 1 | 미만버림 | ✅ | +| 설계서의 **금액란** | 원 | 1 | 미만버림 | ✅ 「내역에 실릴 때 정수 절사」와 같음 | +| **일위대가표의 계금** | 원 | 1 | 미만버림 | ✅ 「호표 안 성분 소계 절사」와 같음 | +| **일위대가표의 금액란** | 원 | **0.1** | 미만버림 | ✅ 「단가산출 성분단가 소수 1자리 절사」와 같음 | + +⇒ **STmate 를 재서 얻은 규칙이 법 조문이었음.** 경쟁사를 베낀 것이 아니라 둘 다 같은 고시를 따른 것임. + +**같은 고시 1-2-2 「1. 수량의 단위표준」 [주]**: + +``` +① 설계서 수량의 단위와 소수자리 표시는 본 표에 따르며, 반올림하여 적용 +② 품목이 제시한 소숫자리가 본 표와 다르면 항목 쪽을 우선 +③ 본 표에 없는 품은 유사품의 규격·단위수량을 참고, C.G.S 단위 원칙 +``` + +⇒ 아래 「규정이 없는 자리 = 소수 2자리」는 **이 표에도 없는 자리에만** 적용할 것. +표에 실린 품목(관류 길이 m 소수 2 · 수로연장 m 소수 1 · 옹벽 ㎡ 소수 1 …)은 **표가 규정임.** + ### ⭐ 규정이 없는 자리 — 소수점 2자리가 기본값 (2026-09-13 사용자 확정) **단수처리에 법적 근거가 없음이 확인됐음** — 기재부 **예정가격작성기준 전 조문에 From b84ee14bea12fb3ebadf08924f80d80d5a53ce45 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 07:48:39 +0900 Subject: [PATCH 3/4] auto: 2026-09-14 07:48 (EOMSANGDON-HOME) --- main.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/main.py b/main.py index 98f805d1..a682f00f 100644 --- a/main.py +++ b/main.py @@ -65,6 +65,12 @@ from B08_Quantity.B08_Quantity_Router_Earthwork import router as b08_earthwork_r from B08_Quantity.B08_Quantity_Router_Material import router as b08_material_router from B08_Quantity.B08_Quantity_Router_StructureSheet import router as b08_structure_sheet_router from B09_Estimation.B09_Estimation_Router import router as b09_estimation_router +from B09_Estimation.B09_Estimation_Router_Contract import router as b09_contract_router +from B09_Estimation.B09_Estimation_Router_Execution import router as b09_execution_router +from B09_Estimation.B09_Estimation_Router_Progress import router as b09_progress_router +from B09_Estimation.B09_Estimation_Router_CostSheet import router as b09_cost_sheet_router +from B09_Estimation.B09_Estimation_Router_Edits import router as b09_edits_router +from B09_Estimation.B09_Estimation_Router_Factors import router as b09_factors_router from common_util.common_util_audit import note_api_call, record_call_burst from common_util.common_util_auth import ( require_company, @@ -636,6 +642,12 @@ app.include_router(b08_earthwork_router, dependencies=protected_with_company) app.include_router(b08_material_router, dependencies=protected_with_company) app.include_router(b08_structure_sheet_router, dependencies=protected_with_company) app.include_router(b09_estimation_router, dependencies=protected_with_company) +app.include_router(b09_cost_sheet_router, dependencies=protected_with_company) +app.include_router(b09_contract_router, dependencies=protected_with_company) +app.include_router(b09_execution_router, dependencies=protected_with_company) +app.include_router(b09_progress_router, dependencies=protected_with_company) +app.include_router(b09_edits_router, dependencies=protected_with_company) +app.include_router(b09_factors_router, dependencies=protected_with_company) # 개발 전용 잠금 해제 — 다른 라우터와 **같은 보호**를 받는다(로그인·회사·프로젝트 접근). # 그 위에 서버가 환경까지 한 번 더 본다. app.include_router(dev_unlock_router, dependencies=protected_with_company) From 808cf0f289433ecbec1d3d7bc3e2e092d6d7b606 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 07:49:02 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix(b09):=20=EA=B8=B0=EA=B3=84=20=EC=B9=B4?= =?UTF-8?q?=ED=83=88=EB=A1=9C=EA=B7=B8=20=EC=9D=B4=EB=A6=84=20=EB=90=98?= =?UTF-8?q?=EC=82=B4=EB=A6=BC=20=E2=80=94=20=ED=91=9C=20=EB=A8=B8=EB=A6=AC?= =?UTF-8?q?=EA=B8=80=20=EB=B6=99=EC=9D=80=205=EA=B1=B4=20=C2=B7=20?= =?UTF-8?q?=EB=B9=88=20=EC=9D=B4=EB=A6=84=2029=EA=B1=B4(=EC=9B=90=EC=B2=9C?= =?UTF-8?q?=20=EC=AA=BD=20=EB=84=98=EA=B9=80=20=ED=8C=8C=EC=8B=B1=20?= =?UTF-8?q?=EB=B3=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 쪽 끝 기종 이름에 다음 쪽 머리글·다음 분류 이름이 붙고, 다음 분류 첫 기종들은 이름이 비던 병 - 이름·규격은 건설공사 표준품셈 제8장 기종 목록 줄에서 읽음 · 취득가·손료계수는 원천 그대로 - 대형 브레이커 되살림 표 옆에 같은 약속으로 둠(원천이 실으면 지움) - 자원 축 781/386 · 범위 81공종 상태 전후 같음 · 시험 4건 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn --- B09_Estimation/B09_Estimation_MachineCost.py | 31 ++++++++++ .../tester/test_b09_machine_catalog_names.py | 60 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 resources/tester/test_b09_machine_catalog_names.py diff --git a/B09_Estimation/B09_Estimation_MachineCost.py b/B09_Estimation/B09_Estimation_MachineCost.py index ca1615f1..3c1b5576 100644 --- a/B09_Estimation/B09_Estimation_MachineCost.py +++ b/B09_Estimation/B09_Estimation_MachineCost.py @@ -132,6 +132,32 @@ MASHED_MACHINE_FIXES: dict[str, tuple[str, str]] = { #: (0230) 표의 「시간당 계」 — 규격이 달라도 같은 값이다(원문 여섯 줄 모두 6,601). MASHED_LOSS_COEFFICIENT = Decimal("0.0006601") +#: ⚠ **같은 파싱 병의 나머지** (2026-09-14 · 축 C 1장 ① 브레인 차례). +#: 원천이 쪽 끝 기종의 이름 칸에 **다음 쪽 머리글 + 다음 분류 이름**을 붙여 넣어 ① 그 기종은 +#: 이름에 표 머리글이 붙고 규격이 비고 ② 다음 분류 첫 기종들은 **이름이 빔**. 이름으로 찾아지지 +#: 않고 화면에도 빈 이름으로 선다. 이름·규격은 **건설공사 표준품셈 제8장 표**(`pum_const_2026.json` +#: 기종 목록 줄 · 붙은 꼬리 「(분류번호) 이름」)에서 읽음 — 취득가·손료계수는 원천 그대로. +#: ⚠ 원천이 바로 실으면 이 표는 지운다(위 표와 같은 약속). +GLUED_MACHINE_FIXES: dict[str, tuple[str, str]] = { + "5220-0015": ("소형브레이커(전기식)", "1.5㎾"), + "6532-0220": ("진동파일 해머(유압식)", "162㎾"), + "7101-0450": ("고성능 착정기", "335.70㎾"), + "7120-0746": ("버킷식준설기", "7.46kW"), + "7995-0050": ("배관파이프", "ø50-2.6m"), +} +#: 이름이 빈 분류 — 분류번호(코드 앞 넷) → 원문 이름. 규격은 원천 값 그대로. +EMPTY_NAME_BY_GROUP: dict[str, str] = { + "0240": "유압식 진동콤팩터(굴착기 부착용)", + "3611": "콘크리트 피니셔(중앙분리대용)", + "5330": "드릴웨곤", + "6540": "워터젯트", + "6802": "파일천공전용장비", + "7202": "자동세륜기(롤 타입)", + "7930": "모터", + "8201": "3D GNSS 머신 가이던스(굴착기용)", + "9070": "이우선(비자항)", +} + class MachineCostError(LookupError): """기계경비를 세울 수 없는 경우. 0 으로 때우지 않고 멈춘다.""" @@ -205,6 +231,11 @@ def load_machine_catalog(file_name: str = "mach_base_2026.json") -> MachineCatal # 뭉개진 줄 — 원문으로 이름·규격을 되살리고 손료계수를 채운다. row = {**row, "machine_name": fixed[0], "specification": fixed[1]} coefficient = {**coefficient, "loss_coefficient_per_hour": MASHED_LOSS_COEFFICIENT} + elif code in GLUED_MACHINE_FIXES: + name, spec = GLUED_MACHINE_FIXES[code] + row = {**row, "machine_name": name, "specification": spec} + elif not str(row.get("machine_name") or "").strip() and code[:4] in EMPTY_NAME_BY_GROUP: + row = {**row, "machine_name": EMPTY_NAME_BY_GROUP[code[:4]]} catalog.machines[code] = MachineSpec( machine_code=code, name=row["machine_name"], diff --git a/resources/tester/test_b09_machine_catalog_names.py b/resources/tester/test_b09_machine_catalog_names.py new file mode 100644 index 00000000..5feaf783 --- /dev/null +++ b/resources/tester/test_b09_machine_catalog_names.py @@ -0,0 +1,60 @@ +"""기계 카탈로그 이름 — 원천 파싱 병(쪽 끝 이름에 다음 쪽 머리글이 붙고 다음 분류 이름이 빔) 되살림. + +2026-09-14 축 C 1장 ① (브레인 차례) · 원문 = 건설공사 표준품셈 제8장 기종 목록 줄. + ① 표 머리글(「시 간 당」·「(10-7)」)이 붙은 이름이 하나도 없음 + ② 빈 이름이 하나도 없음 + ③ 되살린 이름·규격이 원문 차례와 같음 · 취득가는 원천 그대로 +""" + +from __future__ import annotations + +import json +import sys +from decimal import Decimal +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) + +from B09_Estimation.B09_Estimation_MachineCost import load_machine_catalog # noqa: E402 + +CATALOG = load_machine_catalog() + + +def test_표_머리글이_붙은_이름이_없다() -> None: + glued = [ + m.machine_code + for m in CATALOG.machines.values() + if "시 간 당" in m.name or "(10-7)" in m.name + ] + assert glued == [] + + +def test_빈_이름이_없다() -> None: + assert [m.machine_code for m in CATALOG.machines.values() if not m.name.strip()] == [] + + +def test_되살린_이름과_규격() -> None: + expected = { + "5220-0015": ("소형브레이커(전기식)", "1.5㎾"), + "7101-0450": ("고성능 착정기", "335.70㎾"), + "0240-0007": ("유압식 진동콤팩터(굴착기 부착용)", "0.7"), + "3611-0142": ("콘크리트 피니셔(중앙분리대용)", "105.9"), + "6802-0100": ("파일천공전용장비", "100"), + "9070-0020": ("이우선(비자항)", ""), + } + for code, (name, spec) in expected.items(): + machine = CATALOG.machines[code] + assert (machine.name, machine.specification) == (name, spec), code + + +def test_취득가는_원천_그대로() -> None: + raw = json.loads( + (ROOT / "resources/data_cost_input_value/mach_base_2026.json").read_text(encoding="utf-8") + ) + prices = { + r["machine_code"]: r["price_thousand_krw"] + for r in raw["variables"]["mach_price"]["records"] + } + for code in ("5220-0015", "0240-0007", "6802-0040", "7930-0100"): + assert CATALOG.machines[code].price_thousand_krw == Decimal(str(prices[code]))