- 원가계산서: 폐기물처리비를 경비 줄로(예정가격작성기준 제19조③18호) · 법정경비 뒤라 그 밑수엔 안 섞임 - 분리발주 칸(기본 아님) — 켜면 총원가 밖 · 총공사비에만 더함 - 준비공 「임목폐기물 처리」 톤: WA = 0.5·π·(B/2)²·h·1.3·W1·N · WR = WA × 15/85 (한국건설기술연구원 2012) - 조사값 넷(1,000㎡당 본수·흉고직경·수고·단위체적중량) 칸 · 기본값 없음 · 5톤·100톤 경계 알림 - 수동 처리단가 빨간 테두리 + 「미확정 N건」 · 내역엔 안 서고 제외 사유 「경비」 - 시험: 승률 구조(경비·일반관리비·이윤 밑수 증가 · 법정경비 불변 · 분리발주) · 실정보고 부피 대조 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
67 lines
2.8 KiB
Python
67 lines
2.8 KiB
Python
"""임목폐기물 처리 — 톤으로 서는가 · 경비라 내역에 안 서는가 (2026-09-14 판정).
|
||
|
||
⚠ 산식 — 한국건설기술연구원(2012) 3.2: WA = 0.5 × π × (B/2)² × h × 1.3 × W1 × N · WR = WA × 15/85.
|
||
⚠ 실정보고 예(p26) 「V1 = 0.5×π×(0.16/2)²×10.0×1.3×134/1000×7,450 = 130.40㎥」 로
|
||
지상부 부피를 대 봄.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import math
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from B08_Quantity.B08_Quantity_Engine_Preparation_TreeWaste import ( # noqa: E402
|
||
tree_waste_rows,
|
||
tree_waste_tons,
|
||
)
|
||
|
||
SLOPE = {"tree_removal_fill": 4000.0, "tree_removal_cut": 3450.0} # 7,450㎡
|
||
FIELD = {"stems_per_1000m2": 134, "dbh_cm": 16, "height_m": 10.0, "unit_weight_kg_m3": 1000}
|
||
|
||
|
||
def test_지상부는_실정보고_부피와_같다() -> None:
|
||
"""W1 = 1,000㎏/㎥ 로 두면 지상부 톤 = 지상부 부피(㎥) — 실정보고 130.40㎥."""
|
||
above, root = tree_waste_tons(7450.0, FIELD)
|
||
# 실정보고는 π 를 3.14 로 셈 — 같은 π 로 되돌리면 130.40 과 같다.
|
||
assert above * 3.14 / math.pi == pytest.approx(130.40, abs=0.01)
|
||
assert root == pytest.approx(above * 15 / 85)
|
||
|
||
|
||
def test_칸이_비면_톤이_안_서고_무엇이_빈지_적는다() -> None:
|
||
row = tree_waste_rows(SLOPE, {**FIELD, "height_m": None})[0]
|
||
assert row["amount"] is None and "평균 수고" in row["reason"]
|
||
assert row["reference_amount"] == 7450.0
|
||
|
||
|
||
def test_단가가_없으면_금액이_안_서고_있으면_미확정() -> None:
|
||
no_price = tree_waste_rows(SLOPE, FIELD)[0]
|
||
assert no_price["unit"] == "톤" and no_price["amount"] > 0
|
||
assert no_price["amount_krw"] is None and no_price["unconfirmed"] == 0
|
||
priced = tree_waste_rows(SLOPE, FIELD, 60000)[0]
|
||
assert priced["unconfirmed"] == 1
|
||
assert priced["amount_krw"] == int(priced["amount"] * 60000)
|
||
|
||
|
||
def test_톤_경계를_알린다() -> None:
|
||
big = tree_waste_rows(SLOPE, FIELD)[0] # 약 153톤
|
||
assert big["amount"] >= 100 and "100톤 이상" in big["reason"]
|
||
small = tree_waste_rows({"tree_removal_fill": 500.0}, FIELD)[0] # 약 10톤
|
||
assert 5 <= small["amount"] < 100 and "5톤 이상" in small["reason"]
|
||
|
||
|
||
def test_경비라_내역에_안_서고_제외_사유가_간다() -> None:
|
||
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff
|
||
from B08_Quantity.B08_Quantity_Engine_Preparation import build_table
|
||
|
||
table = build_table(SLOPE, tree_waste=FIELD, tree_waste_unit_price_krw_per_ton=60000)
|
||
handed = {r["name"]: r for r in build_handoff(preparation_table=table)["work_items"]}
|
||
row = handed["임목폐기물 처리"]
|
||
assert row["in_bill"] is False and row["blocked_kind"] is None
|
||
assert "경비" in row["in_bill_reason"]
|