"""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㎥ 형식을 안 적어 무한궤도·타이어 두 갈래로 세움" "(제안 무한궤도 — 영월 실무 「06M3 B/H」 · 2026-09-14 브레인 ②).", ), # 2026-09-14 ㉮ — 사용횟수 갈래로 푼 뒤 남는 원문 몫. 값을 짓지 않고 말만. # 봉상후렉시블 셋의 표 나머지 줄은 판정표 자동 목록(`_unread_rows`)이 맡음 — 줄이 아닌 [주] 만 여기. "FP-12-12": ("원문 [주]", "ⓘ [주] 「성토부 날개벽 설치시 인건비 30% 할증」 은 안 걺(선택)."), # ㉡-3(2026-09-15) — 시간 줄 표 둘 · 막힌 셋은 옛 값이 왜 틀렸는지(풀리는 날 두 번 세지 않게 · 브레인 ③). "FP-14-01": ( "원문 표", "ⓘ 원문 「0.8㎥ 굴착기(우드그랩 부착)」 가 굴착기 형식을 안 적어 무한궤도·타이어 두 갈래 · 우드그랩 ↔ 카탈로그" " 「부착용 집게 7206-0070(0.6∼0.8)」 — 이름이 다름 · 인력 「h」 는 시간이라 총칙 일일 작업시간 8시간으로 ÷8.", ), "FP-14-02": ( "원문 표", "ⓘ 원문 「0.8㎥ 굴착기」 가 형식을 안 적어 무한궤도·타이어 두 갈래 · 인력 「h」 는 시간이라 총칙 일일 작업시간" " 8시간으로 ÷8 · 옛 11,356원은 0.66h 를 인으로 셈(8배) + 굴착기 줄이 빠진 값.", ), "FP-12-26": ( "막힘 까닭", "ⓘ 양수기(150㎜)·디젤엔진(15HP 11.19kW) 가동시간이 원문 표에 없음 · 카탈로그 양수기는 1.49㎾ 뿐 · 엔진(디젤)" " 손료계수 없음 → 금액 안 세움 · 옛 172,068원은 보통인부 1인 값뿐(기계·목도 4인 빠짐).", ), "FP-10-07-04": ( "막힘 까닭", "ⓘ 운전 단가표(1일) 보통인부 2 는 적재·짐부리기 보통인부 2인(「운전조작」 포함)과 같은 사람 — 두 번 세지 말 것 ·" " 연료 「ps × 0.253ℓ × 6h」 의 ps 입력 없음 · 본기계·레일은 임대료·견적 → 금액 안 세움 · 옛 688,272원은" " 보통인부를 두 번 센 4인 값.", ), "FP-10-08-03": ( "막힘 까닭", "ⓘ 연료 = 윈치 기관 출력([주]② 36~73kW) × 운전시간([주]① 4.3~6.7h) × ℓ/kWh — ℓ/kWh 가 원문에 없음 · 보통인부는" " 10-8-1 짐내리기 품 · 운반기구 손료 별도 → 금액 안 세움 · 옛 226,122원은 특별인부 1인 값뿐.", ), "FP-08-06-01": ( "원문 [주]", "ⓘ 2-1-8 [주]② 「소방관서의 급수 지원을 받은 경우에는 휘발유(양수기)는 미반영」 — 해당하면 양수기 몫을" " 뺄 것 · 8-6-1 [주]③ 실제 사용하지 않는 품은 제외.", ), "FP-08-11": ( "원문 [주]", "ⓘ [주]③ 장비 운반비는 별도 계상(기계 수송비 칸) · [주]④ 우드그랩은 원목 규격에 따라 별도 · [주]② 추가" " 인력(파쇄 후 마대담기 등)은 조사해 반영 · [주]⑥ 이 규격 외 파쇄기는 견적 — 이 일위대가엔 안 넣음.", ), # 원문 대 실무 어긋남 기록(2026-09-15 브레인 규칙 — 원문이 또렷하면 원문 · 어긋남은 늘 기록). "FP-09-15-02": ( "실무 어긋남", "ⓘ 실무 영월 「표토제거 답외구간」 호표 Q=576.95 는 E 자리에 e(0.96)를 넣은 값으로 역산됨" "(60×3.07×0.77×0.96÷(1.18×0.2)) — 원문 9-15-2 가 E=0.4 로 또렷해 원문으로 셈 · 다른 실무(봉화·대흥·" "소광·거창)엔 이 호표 없음.", ), "FP-12-34-01": ( "원문 머리", "ⓘ 원문 머리 「(단위: 개소당)」 ↔ 인력 콘크리트공 0.17·보통인부 0.29 가 12-16 맨홀 ㎥당(0.17인/㎥" " · 0.29인/㎥)과 같고 기계도 Q=5.4㎥/hr ⇒ ㎥당으로 읽음(고른 쪽 ㎥당 · 버린 쪽 개소당) ·" " 봉상후렉시블(45mm) 대 2 는 엔진식 진동기의 봉이라 두 번 안 셈(2026-09-14 브레인).", ), "FP-12-04": ( "원문 [주]", "ⓘ 「사용고재 평가기준 23%(합판과 각재의 설계단가 기준)」 은 원문이 셈을 안 줘 값으로 안 씀" " — [주]① 2회 이상 사용 고재량은 재료비 비율에 기포함. " "ⓘ 재료(합판·못 카탈로그 없음 · 각재·철선·박리제 규격 미정)는 못 붙은 줄 — 지금 값은" " **인력 품만** · [주]③ 동바리 별도 · [주]⑥ 소형구조물 인력품 30% 할증(선택)은 안 걺.", ), "FP-05-24": ( "실무 역산", "ⓘ 원문이 애매해 실무(거창 「초류종자살포(씨드스프레이)」)를 따른 자리 둘 — 뒤집을 수 있게 어긋남을" " 나란히: ① 「트럭 4.5ton」 → 덤프트럭 4.5(원문은 트럭 · 카탈로그에 트럭 없음 · 거창이 덤프트럭 4.5Ton)" " ② 「종자살포기 2,500-3,000ℓ」 → 취부기 11.94㎾(원문 규격은 ℓ 탱크 용량 · 카탈로그는 ㎾ · 건설 4-1-3" "·거창이 취부기) · 취부기는 손료만(8-4 운전경비표에 줄 없음 · 거창도 손료만) · ⚠ 거창은 디젤엔진 11.19㎾" " 를 따로 세움 — 산림 5-24 · 건설 4-1-3 표 둘 다에 없어 안 넣음 · 물탱크 조종원은 화물차운전사(8-1-2 5호" " 살수차) · [주]⑦ 물주기(인력) 보통인부 0.0005인은 필요시라 안 걺(2026-09-15 브레인).", ), "FP-06-02-02": ( "부록 예시와 다름", "ⓘ 부록 단가산출서 예시(2025 · 조림목 2,700본/ha)는 묘목찾기 0.10인/100본 · 줄베기 1.40인/ha 로 적어" " 본문 표(묘목찾기 0.07 · 줄베기 1.50~2.50)와 다름 — 본문 표로 셈(기록만 · 2026-09-15 브레인).", ), "FP-06-02-03": ( "부록 예시와 다름", "ⓘ 부록 단가산출서 예시(2025 · 조림목 2,700본/ha)는 묘목찾기 0.10인/100본 · 모두베기 3.10인/ha 로 적어" " 본문 표(묘목찾기 0.07 · 모두베기 3.50~5.00)와 다름 — 본문 표로 셈(기록만 · 2026-09-15 브레인).", ), "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] = { # (비어 있음) 12-4 합판거푸집은 2026-09-14 사용횟수 갈래로 풀려 걷음(`_JudgedTable`). } 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) #: 표 형태 이름 — 판정 까닭 한 줄에 붙임. _FORM_LABELS = { "reference": "참조표", "coefficient": "계수표", "productivity": "생산량형", "requirement": "소요량형", } def form_judgment_note(code: str | None) -> str: """사람이 가른 표 형태 까닭 — 원문 근거 없는 **SW 규칙**이라 화면에 드러냄(10-A ⑭ · 09-14). 정본은 B08 마스터 빌더의 판정표 한 벌(`_Forms.FORM_JUDGMENTS`) — 여기서 다시 적지 않고 읽음. """ plain = str(code or "").split("#")[0] if not plain: return "" from B08_Quantity.B08_Quantity_Build_WorkItemMaster_Forms import FORM_JUDGMENTS from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master node = next( (n for n in load_work_item_master()["work_items"] if n.get("work_item_code") == plain), {}, ) parts = [] for table in node.get("tables") or []: found = FORM_JUDGMENTS.get(str(table.get("pum_table_id"))) if found: _section, form, why = found parts.append(f"{table['pum_table_id']} {_FORM_LABELS.get(form, form)}: {why}") if not parts: return "" return "ⓘ 표 형태는 사람이 가름(원문 근거 없음 · SW 규칙) — " + " / ".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