실무 대조: 오솔길 BOM 6벌 면적 셈법 ±0.11% · 거창 줄 없음 · 영월 성토면 × 50% · 원문 9-19-1 성토면·9-19-2·3 식재 조건부 배정 프로젝트: 인계 줄 +3(성토면 12,350㎡ · 절토면 토사 3,085.51 · 암 2,262.62) 외 변화 0 · 본체 합계 105,512,060 그대로 · 규격 글로 시작하는 갈래는 규격에 한 번만(도자 운반 「토사 토사」 → 「토사」 · 금액 불변) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
144 lines
8.6 KiB
Python
144 lines
8.6 KiB
Python
"""B09 원가계산 — **원문에는 있는데 단가에 못 실린 몫** (2026-09-09).
|
|
|
|
「반쪽 단가」 표시는 여태 **이름을 카탈로그에서 못 찾은 줄**만 잡았다. 그물 밖에 둘이 더 있다.
|
|
|
|
㉠ 표에 줄은 있는데 **값을 못 적는** 것 — 기초잡석 「운반 | 덤프트럭(15ton)」
|
|
㉡ 표에 아예 없고 **[주]가 별도라 한** 것 — 규준틀 목재·표지판
|
|
|
|
둘 다 **금액이 서 있는 줄**이라 표시가 없으면 **완성된 값으로 읽힌다**(2026-09-09 실측:
|
|
규준틀 둘이 4,921,267원인데 인력만의 값이었다).
|
|
|
|
⚠ **사유를 뭉뚱그리지 않는다.** 「원문이 값을 안 줌」은 **영영 막힌 것**으로 읽히고,
|
|
「거리 미정」·「설계수량 대기」는 **곧 풀릴 것**으로 읽힌다. 사용자가 보는 뜻이 다르다.
|
|
|
|
⚠ **여기 적는 것은 원문에 있는 말뿐이다.** 값을 만들지 않는다 — 무엇이 왜 빠졌는지만 적는다.
|
|
⚠ **마스터가 [주]를 싣게 되면 이 표는 지운다** — 두 곳에 같은 말을 두면 한쪽만 고쳐진다.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
#: 공종코드 → (짧은 딱지, 사유 한 줄). 접두사로 맞춘다(갈래가 붙어도 걸리게).
|
|
KNOWN_GAPS: dict[str, tuple[str, str]] = {
|
|
# ⚠ 규준틀 둘은 **B08 인계가 이미 「재료량은 [주]④ 설계수량에 따른다라 미확보」**를 싣는다.
|
|
# 그러니 그 말을 되풀이하지 않고 **B09 쪽에서만 아는 것**만 보탠다 —
|
|
# ㉠ 지금 선 값이 **인력 품만**이라는 것 ㉡ 손율은 원문에 이미 있다는 것.
|
|
# (2026-09-09: 처음엔 「표시가 아무것도 없다」고 봤는데, 인계 사유가 화면 문구 뒤쪽에
|
|
# 잘려 안 보였던 것이다. 잘린 자리를 사유가 없는 자리로 읽지 말 것.)
|
|
"FP-11-02": (
|
|
"인력 품만",
|
|
"ⓘ 이 단가는 **인력 품만**입니다 — 목재·못은 **자재 축**으로 따로 갑니다"
|
|
"(2026-09-09 각재·판재·못이 자재 축에 섰음. 다만 관급·사급이 안 갈려 아직 금액 0). "
|
|
"재료가 금액으로 서면 손율이 함께 걸립니다 — 품셈 [주]③ 「목재의 손율은 1개소 사용당 50%」.",
|
|
),
|
|
"FP-11-03": (
|
|
"인력 품만",
|
|
"ⓘ 이 단가는 **인력 품만**입니다 — 목재·못은 **자재 축**으로 따로 갑니다"
|
|
"(2026-09-09 각재·판재·못이 자재 축에 섰음. 다만 관급·사급이 안 갈려 아직 금액 0). "
|
|
"재료가 금액으로 서면 손율이 함께 걸립니다 — 품셈 [주]③ 「목재의 손율은 1개소 사용당 80%」.",
|
|
),
|
|
"FP-12-11-03": (
|
|
"인력 품만",
|
|
"⚠ 이 값은 **인력 품만**입니다 — 파형강관·커플링밴드·크레인(5ton)·모래부설이 아직 "
|
|
"안 붙었습니다. 관·모래는 원문이 「별산」·「설계수량」이라 한 자리이고, 크레인은 "
|
|
"기계 카탈로그에 없어서입니다. "
|
|
"ⓘ 커플링밴드는 값이 표에 없지만 [주]① 이 「**1EA/6m**」를 줍니다 — 자재가 서면 "
|
|
"m 당 1/6 EA 로 셀 수 있습니다(다만 「필요시 별도 산정」이라 조건이 하나 더 붙습니다).",
|
|
),
|
|
# 2026-09-14 ⑤ 판정 Ⓓ·Ⓔ — 두 표를 갈래로 읽은 뒤 남는 [주] 둘. 값을 짓지 않고 말만 둠.
|
|
"FP-09-19-01": (
|
|
"원문 [주]",
|
|
"ⓘ 절토면 암 갈래(연암·보통암ㆍ경암): [주]③ 「소형브레이커 조작 인력품은 착암공으로 한다」"
|
|
" — 표에 착암공 수량이 없어 줄을 안 세움(보통인부는 표 그대로). "
|
|
"ⓘ 절토면 풍화암: [주]② 「소형브레이커를 사용할 시는 연암 고르기 품을 **준용할 수 있다**」"
|
|
" — 선택지라 따로 갈래를 안 만듦(브레이커를 쓰면 연암 갈래를 고를 것). "
|
|
"ⓘ 성토면 · 기계: 굴착기 0.6㎥ 가 무한궤도·타이어 둘이라 **규격 미정** — 이 갈래는 안 섬"
|
|
"(굴착기 규격 칸과 함께 풀 자리 · 2026-09-14 판정 Ⓓ).",
|
|
),
|
|
"FP-12-25": (
|
|
"운반거리 미정",
|
|
"⚠ 이 값에는 **운반 몫이 빠져 있습니다** — 품셈 12-25 는 「운반 | 덤프트럭(15ton)」 줄을 "
|
|
"두었으나 시간을 적지 않았습니다. 그 값은 품셈 10-12(덤프운반)가 **운반거리로** 냅니다"
|
|
"(같은 15ton 장비). 거리가 정해지면 사토 운반·덤프 운반과 **함께** 섭니다.",
|
|
),
|
|
}
|
|
|
|
#: 조건이 서면 **빠져야 하는** 줄 — 지금은 무조건 붙어 있다.
|
|
#: ⚠ 다른 조건부 줄들(계획서 9-9)과 달리 **이것은 금액이 서 있는 줄**이다.
|
|
CONDITIONAL_INCLUDED: dict[str, str] = {
|
|
"FP-12-25": (
|
|
"ⓘ 소할(할석공 0.06인)은 품셈 12-25 가 「**브레이커 사용할 때 제외**」라 적은 줄입니다 — "
|
|
"브레이커 갈래가 서면 이 몫이 빠져야 합니다. 지금은 붙어 있습니다."
|
|
),
|
|
}
|
|
|
|
|
|
#: **일부러 풀지 않은 표** — 풀면 금액이 조용히 틀리는 자리. 막힌 사유를 이 문구로 **갈아 끼운다**
|
|
#: (표 읽기 사유 「박리제 줄을 못 풀었습니다」는 사람을 엉뚱한 데로 보낸다).
|
|
#: ⚠ 이 구현이 서면 그 줄을 지운다.
|
|
BLOCKED_BY_DESIGN: dict[str, str] = {
|
|
# 2026-09-13 브레인 판정 — B09 일위대가 일감(B08 Formwork 머리말 「횟수별 재료 환산은 여기서
|
|
# 하지 않는다」). 표를 그대로 풀면 1회 사용 값(재료·노무 100 %)으로 조용히 비싸진다.
|
|
"FP-12-04": (
|
|
"거푸집 **사용횟수별 비율**(12-4 「1회 100 % · 4회 40 %…」)을 일위대가에 거는 셈이"
|
|
" 아직 없어"
|
|
" 풀지 않았습니다 — 풀면 1회 사용 값으로 비싸게 섭니다"
|
|
),
|
|
}
|
|
|
|
|
|
def known_gap_note(code: str | None) -> str:
|
|
"""그 공종에 **원문에는 있는데 못 실린 몫**이 있으면 사유 한 줄."""
|
|
if not code:
|
|
return ""
|
|
plain = str(code).split("#")[0]
|
|
parts = []
|
|
for prefix, (_label, note) in KNOWN_GAPS.items():
|
|
if plain.startswith(prefix):
|
|
parts.append(note)
|
|
for prefix, note in CONDITIONAL_INCLUDED.items():
|
|
if plain.startswith(prefix):
|
|
parts.append(note)
|
|
# 다른 품셈에서 빌린 밑수(사용자 확정) — 교차 참조와 실무 어긋남을 값 옆에(2026-09-14 · 종전엔
|
|
# 고르기형 표 안내가 「못 붙은 줄」로 섰고 이 문구는 아무 데서도 안 불렸음).
|
|
from B09_Estimation.B09_Estimation_WorkItemUnit import borrowed_unit_note
|
|
|
|
borrowed = borrowed_unit_note(plain)
|
|
if borrowed:
|
|
parts.append(f"ⓘ {borrowed}")
|
|
return " / ".join(parts)
|
|
|
|
|
|
#: 관부설 품셈 표가 다루는 관경 — 그 밖은 **표에 없는 것**이지 값이 틀린 것이 아니다.
|
|
PIPE_TABLE_DIAMETERS_MM = (800, 1000, 1200)
|
|
|
|
#: 교본이 큰 관을 넘기는 자리 — **교본 기준이지 법령·행정규칙이 아니다.**
|
|
#: `횡단배수관_암거.md §5`(교본 3장) 「BOX암거 적용 — 수리계산상 배수관 **Ø1,500㎜ 이상**
|
|
#: 적용 유역 · 계곡 횡단경사 40% 이내 · 구체 수평 설치 원칙」.
|
|
#: ⚠ 그래서 「Ø1,500 이면 반드시 암거」라고 **단정하지 않는다** — 프로젝트 규칙이
|
|
#: 「기본값은 현행 법령·행정규칙, 교본은 과거 참조」이기 때문이다. **가리키기만** 한다.
|
|
CULVERT_HINT_MM = 1500
|
|
|
|
|
|
def pipe_diameter_note(code: str | None, variant_value: object) -> str:
|
|
"""관경이 품셈 표 밖일 때 **어디를 봐야 하는지**를 가리킨다.
|
|
|
|
⚠ 「품셈 표에 그 관경이 없음」만 적으면 사용자가 **엉뚱한 것을 정하러 간다** —
|
|
정할 것은 단가가 아니라 **시설 선택**일 수 있다.
|
|
"""
|
|
if not code or not str(code).startswith("FP-12-11"):
|
|
return ""
|
|
try:
|
|
diameter = int(float(str(variant_value)))
|
|
except (TypeError, ValueError):
|
|
return ""
|
|
if diameter in PIPE_TABLE_DIAMETERS_MM:
|
|
return ""
|
|
note = f"⚠ 품셈 관부설 표는 ∅800·∅1000·∅1200 까지만 있습니다 — Ø{diameter} 는 표 밖입니다."
|
|
if diameter >= CULVERT_HINT_MM:
|
|
note += (
|
|
" ⓘ 임도기술교본 3장은 **Ø1,500㎜ 이상을 BOX암거 적용 유역**으로 봅니다"
|
|
"(`횡단배수관_암거 §5`). 관으로 놓을 자리가 아닐 수 있으니 **단가가 아니라"
|
|
" 시설 선택**을 먼저 볼 것. ⚠ 교본 기준이라 법령·행정규칙은 아닙니다."
|
|
)
|
|
return note
|