Merge remote-tracking branch 'origin/dev' into sub_laptop_1

This commit is contained in:
2026-09-14 01:31:56 +09:00
9 changed files with 143 additions and 35 deletions
@@ -671,6 +671,7 @@ def build_table(
tree_waste: dict[str, Any] | None = None,
tree_waste_unit_price_krw_per_ton: Any = None,
waste_separate_order: Any = False,
tree_waste_root_method: Any = None,
) -> dict[str, Any]:
"""화면·인계가 그대로 쓰는 모양. **못 서는 줄도 목록에 남는다.**"""
rows = (
@@ -687,7 +688,11 @@ def build_table(
+ chipping_rows(chipping_enabled, chipping_volume_m3)
# 임목폐기물 — 톤 · 비목은 경비(내역 줄 아님 · 2026-09-14 판정).
+ tree_waste_rows(
slope_totals, tree_waste, tree_waste_unit_price_krw_per_ton, waste_separate_order
slope_totals,
tree_waste,
tree_waste_unit_price_krw_per_ton,
waste_separate_order,
tree_waste_root_method,
)
+ ancillary_rows(ancillary_counts)
)
@@ -1,13 +1,21 @@
"""준비공 — 임목폐기물 처리 한 줄 (톤) · 2026-09-14 판정 「폐기물처리비」.
⭐ 수량 산식 — 한국건설기술연구원 「건설현장 임목폐기물의 재활용 및 자원화 기술개발」(2012) 3.2
지상부 WA = k × π × (B/2)² × h × (1+p) × W1 × N (kg)
뿌리부 WR = WA × 뿌리 분배비 ÷ 지상 분배비 = WA × 15/85 (일반 수목류, 같은 보고서 표)
= 환경친화적인 도로건설 지침 부록1 문단 493~508 「3) 임목폐기물」(산출근거: 조경설계요람)
지상부 WA = k × 3.14 × (B/2)² × h × (1+p) × W1 × N (kg · 지침 식이 π 를 3.14 로 적음)
뿌리부 — 지침이 두 방법을 둔다. 고르는 칸.
① 분배비(기본) WR = WA × 15/85 (KICT 일반 수목류 — 2026-09-14 판정 유지)
② 뿌리분 체적 WR = 0.3927 ×× 1,300 × N (지침 「V = πD²(D/2 + RH2/3) = 0.3927D³」
· 「UW1 뿌리부분의 단위중량 1,300kg/m3」). ⚠ D 는 지침이 「뿌리분의 직경(DBH, m)」으로
적었으나 2026-09-14 판정으로 **뿌리분 직경**(흉고직경 아님) — 따로 받고 기본값 없음.
k 수간 형상계수 0.5 · p 지엽 보합률(임목 0.3) — **산식 상수**라 칸이 없다.
B 평균 흉고직경 · h 평균 수고 · W1 수목 단위체적중량 · N 본수 —
**현장 조사값**이라 칸으로 받는다.
N = 표본지 1,000㎡ 실측 본수 × 대상 면적 ÷ 1,000 (실정보고 예 「134/1000 × 7,450」 모양).
대상 면적 = 벌목·지장목제거 면적(같은 나무를 벤 자리).
⚠ 실무 실정보고(임목폐기물처리, 한 현장)는 뿌리 14:86 · 무게 0.65톤/㎥ ·
처리비를 공급가액 뒤(승률 밖)에 두었다 — 판정은 법 문언·공인 자료 쪽(PLAN 10장).
발주처가 요구하면 칸으로 열 자리.
⚠ 금액은 **처리단가가 없어** 못 선다(KCRA 중간처리단가 미확보) — 수동 단가를 넣으면
금액이 서되 「미확정」으로 센다(빨간 테두리). 비목은 **경비**라 내역 줄이 아니고
@@ -24,7 +32,11 @@ from B08_Quantity.B08_Quantity_Engine_Preparation_Status import STATUS_PENDING,
TREE_WASTE_ITEM = "임목폐기물 처리"
STEM_FORM_FACTOR = 0.5 # k
FOLIAGE_ALLOWANCE = 0.3 # p — 임목(고립목은 1.0)
PI_AS_WRITTEN = 3.14 # 지침 부록1 식 원문 · 실정보고도 3.14
ROOT_SHARE, ABOVE_SHARE = 15.0, 85.0 # 일반 수목류 분배비
ROOT_BALL_FACTOR = 0.3927 # V = 0.3927 D³ (지침 부록1)
ROOT_BALL_UNIT_WEIGHT = 1300.0 # ㎏/㎥ (지침 부록1 UW1)
ROOT_METHOD_RATIO, ROOT_METHOD_BALL = "ratio", "root_ball"
#: 칸 이름 · 화면 이름 · 단위 — 모두 채워야 수량이 선다.
TREE_WASTE_INPUTS: tuple[tuple[str, str], ...] = (
@@ -34,13 +46,18 @@ TREE_WASTE_INPUTS: tuple[tuple[str, str], ...] = (
("unit_weight_kg_m3", "단위체적중량(㎏/㎥)"),
)
TREE_WASTE_BASIS = (
"한국건설기술연구원(2012) 3.2 임목 발생량 — k 0.5 · p 0.3(임목)"
" · 뿌리 15 : 지상 85(일반 수목류)"
"환경친화적인 도로건설 지침 부록1 · 한국건설기술연구원(2012) 3.2 — k 0.5 · p 0.3(임목) · π 3.14"
" · N = 표본지 1,000㎡ 본수 × 벌목·지장목제거 면적"
)
#: 톤 경계 — 화면이 알릴 뿐 값을 바꾸지 않는다.
ROOT_RATIO_BASIS = "뿌리 WR = WA × 15/85(분배비 · 일반 수목류)"
ROOT_BALL_BASIS = "뿌리 WR = 0.3927 ×× 1,300㎏/㎥ × N(뿌리분 체적 · 지침 부록1)"
ROOT_BALL_LABEL = "뿌리분 직경 D(m, 흉고직경 아님 · 굴취 흙덩이 직경)"
#: 배출자 신고 경계 — 화면이 알릴 뿐 값을 바꾸지 않는다.
REPORT_THRESHOLD_TON = 5.0
SEPARATE_ORDER_THRESHOLD_TON = 100.0
NOT_SEPARATE_NOTE = (
"임목폐기물은 분리발주 대상 아님(환경부 질의회신 — 임목폐기물처리 실정보고 p4 ·"
" 건설폐기물 업무처리지침: 건설폐기물 아닌 사업장 일반폐기물)"
)
EXPENSE_NOTE = (
"비목은 경비(예정가격작성기준 제19조③18호) — 내역 줄이 아니라 원가계산서 폐기물처리비로 감"
)
@@ -54,23 +71,37 @@ def _positive(value: Any) -> float | None:
return number if number > 0 else None
def tree_waste_tons(area_m2: float, inputs: dict[str, Any]) -> tuple[float, float] | None:
def _missing(inputs: dict[str, Any], root_method: str) -> list[str]:
"""비어 있는 칸 이름 — 뿌리분 체적법이면 뿌리분 직경도 든다."""
missing = [label for key, label in TREE_WASTE_INPUTS if _positive(inputs.get(key)) is None]
if root_method == ROOT_METHOD_BALL and _positive(inputs.get("root_ball_diameter_m")) is None:
missing.append(ROOT_BALL_LABEL)
return missing
def tree_waste_tons(
area_m2: float, inputs: dict[str, Any], root_method: str = ROOT_METHOD_RATIO
) -> tuple[float, float] | None:
"""(지상부 톤, 뿌리부 톤). 칸이 하나라도 비거나 면적이 0 이면 `None`."""
values = {key: _positive(inputs.get(key)) for key, _ in TREE_WASTE_INPUTS}
if area_m2 <= 0 or any(value is None for value in values.values()):
if area_m2 <= 0 or _missing(inputs, root_method):
return None
stems = values["stems_per_1000m2"] * area_m2 / 1000.0
diameter = values["dbh_cm"] / 100.0
stems = float(inputs["stems_per_1000m2"]) * area_m2 / 1000.0
diameter = float(inputs["dbh_cm"]) / 100.0
above_kg = (
STEM_FORM_FACTOR
* math.pi
* PI_AS_WRITTEN
* (diameter / 2.0) ** 2
* values["height_m"]
* float(inputs["height_m"])
* (1.0 + FOLIAGE_ALLOWANCE)
* values["unit_weight_kg_m3"]
* float(inputs["unit_weight_kg_m3"])
* stems
)
return above_kg / 1000.0, above_kg * ROOT_SHARE / ABOVE_SHARE / 1000.0
if root_method == ROOT_METHOD_BALL:
ball = float(inputs["root_ball_diameter_m"])
root_kg = ROOT_BALL_FACTOR * ball**3 * ROOT_BALL_UNIT_WEIGHT * stems
else:
root_kg = above_kg * ROOT_SHARE / ABOVE_SHARE
return above_kg / 1000.0, root_kg / 1000.0
def tree_waste_rows(
@@ -78,11 +109,14 @@ def tree_waste_rows(
inputs: dict[str, Any] | None,
unit_price_krw_per_ton: Any = None,
separate_order: Any = False,
root_method: Any = None,
) -> list[dict[str, Any]]:
"""임목폐기물 처리 한 줄 — 값이 없어도 줄은 선다(무엇을 넣으면 풀리는지 적음)."""
slope = slope_totals or {}
area = float(slope.get("tree_removal_fill", 0.0)) + float(slope.get("tree_removal_cut", 0.0))
inputs = inputs or {}
method = ROOT_METHOD_BALL if root_method == ROOT_METHOD_BALL else ROOT_METHOD_RATIO
root_basis = ROOT_BALL_BASIS if method == ROOT_METHOD_BALL else ROOT_RATIO_BASIS
base = {
"group": "준비공",
"item": TREE_WASTE_ITEM,
@@ -93,9 +127,9 @@ def tree_waste_rows(
"bill_reason": EXPENSE_NOTE,
"separate_order": bool(separate_order),
}
tons = tree_waste_tons(area, inputs)
tons = tree_waste_tons(area, inputs, method)
if tons is None:
missing = [label for key, label in TREE_WASTE_INPUTS if _positive(inputs.get(key)) is None]
missing = _missing(inputs, method)
why = (
f"현장 조사값이 비어 있음 — {' · '.join(missing)}"
if missing
@@ -106,23 +140,20 @@ def tree_waste_rows(
**base,
"amount": None,
"status": STATUS_PENDING,
"reason": f"{why}. 산식: {TREE_WASTE_BASIS}",
"reason": f"{why}. 산식: {TREE_WASTE_BASIS} · {root_basis}",
"reference_amount": area,
}
]
above, root = tons
total = above + root
notes = [
f"지상부 WA {above:,.2f}톤 + 뿌리부 WR {root:,.2f}(WA × 15/85)",
f"지상부 WA {above:,.2f}톤 + 뿌리부 WR {root:,.2f}",
TREE_WASTE_BASIS,
root_basis,
EXPENSE_NOTE,
NOT_SEPARATE_NOTE,
]
if total >= SEPARATE_ORDER_THRESHOLD_TON:
notes.append(
"⚠ 100톤 이상 — 건설폐기물이면 분리발주 대상(업무처리지침). 다만 임목폐기물은"
" 건설폐기물이 아닌 사업장 일반폐기물로 분류됨(같은 지침) — 산출 조건에서 고를 것"
)
elif total >= REPORT_THRESHOLD_TON:
if total >= REPORT_THRESHOLD_TON:
notes.append("5톤 이상 — 배출자 신고 대상 사업장(폐기물관리법 시행령 제2조8호)")
price = _positive(unit_price_krw_per_ton)
row = {
@@ -178,6 +178,7 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
settings.get("tree_waste") or {},
settings.get("tree_waste_unit_price_krw_per_ton"),
settings.get("waste_separate_order"),
settings.get("tree_waste_root_method"),
)
method, method_is_default = concrete_placing_method(settings)
# ⚠ 금액에 바로 걸리는 값이라 「기본값으로 돌고 있음」을 응답에 실어 화면이 띄우게 한다.
@@ -464,6 +465,8 @@ class QuantitySettingsBody(BaseModel):
tree_waste: dict[str, float | None] | None = None
tree_waste_unit_price_krw_per_ton: float | None = None
waste_separate_order: bool | None = None
# 뿌리 산정법 — "root_ball"(뿌리분 체적 × 1,300) · `""` 는 기본(분배비 15/85)으로 되돌림.
tree_waste_root_method: str | None = None
# 토량환산계수(다짐) — `{갈래: {"compacted": C, "reason": 사유}}`.
# ⚠ **기본값을 복사해 넣지 않는다** — 안 고른 갈래는 키가 없어야 정본이 선다.
# 빈 dict 는 「전부 기본값으로 되돌림」이라 통째로 갈아 끼운다.
@@ -531,6 +534,9 @@ async def save_quantity_settings(project_id: UUID, body: QuantitySettingsBody) -
for key, value in (values["tree_waste"] or {}).items()
if isinstance(value, (int, float)) and not isinstance(value, bool) and value > 0
}
if "tree_waste_root_method" in values:
method = values["tree_waste_root_method"]
values["tree_waste_root_method"] = method if method == "root_ball" else None
if "topsoil_target" in values:
# 기본(노면 + 절토)은 저장하지 않는다 — 「안 정함」과 같게 둬 법 문언이 선다.
target = values["topsoil_target"]
@@ -571,6 +577,7 @@ async def save_quantity_settings(project_id: UUID, body: QuantitySettingsBody) -
"concrete_placing_method",
"topsoil_target",
"tree_waste",
"tree_waste_root_method",
"ancillary_counts",
# 고른 계수를 **기본값으로 되돌릴 길**이 있어야 한다 — 병합이면 못 지운다.
"conversion_factors_override",
@@ -114,6 +114,8 @@ export interface QuantitySettings {
tree_waste_unit_price_krw_per_ton?: number | null;
/** 폐기물 분리발주 — 기본 아님. */
waste_separate_order?: boolean | null;
/** 뿌리 산정법 — 없음이면 분배비 15/85 · `"root_ball"` 은 뿌리분 체적 × 1,300. */
tree_waste_root_method?: string | null;
}
/** 갈래 하나의 「무엇을 골랐나」. 서버 `earthwork_conversion_choices` 와 짝이다. */
+3
View File
@@ -114,6 +114,7 @@ async function saveQuantitySettings(projectId: string, draft: DraftSettings): Pr
tree_waste: draft.tree_waste,
tree_waste_unit_price_krw_per_ton: draft.tree_waste_unit_price_krw_per_ton,
waste_separate_order: draft.waste_separate_order,
tree_waste_root_method: draft.tree_waste_root_method,
// 개소는 **통째로** 보낸다 — 지운 항목까지 그대로 가야 되돌릴 길이 있다.
ancillary_counts: draft.ancillary_counts,
// 토량환산계수 — 고른 갈래만 담긴다. 빈 dict 는 「전부 기본값으로 되돌림」이다.
@@ -309,6 +310,7 @@ interface DraftSettings {
tree_waste: Record<string, number | null>;
tree_waste_unit_price_krw_per_ton: number | null;
waste_separate_order: boolean;
tree_waste_root_method: string;
// 자재별 관급/사급 — 표 안에서 줄마다 고른 값.
material_supply: Record<string, SupplyChoice>;
// 갈래별 토량환산계수(다짐) — `compacted` 가 `null` 이면 「안 고름」이라 기본값이 선다.
@@ -1017,6 +1019,7 @@ export async function renderB08Quantity(root: HTMLElement): Promise<void> {
tree_waste_unit_price_krw_per_ton:
(stored.tree_waste_unit_price_krw_per_ton as number | null) ?? null,
waste_separate_order: Boolean(stored.waste_separate_order),
tree_waste_root_method: (stored.tree_waste_root_method as string) ?? "",
ancillary_counts: {
...((stored.ancillary_counts ?? {}) as Record<string, number | null>),
},
@@ -16,6 +16,8 @@ export interface TreeWasteDraft {
tree_waste: Record<string, number | null>;
tree_waste_unit_price_krw_per_ton: number | null;
waste_separate_order: boolean;
/** 뿌리 산정법 — `""` 분배비 15/85(기본) · "root_ball" 뿌리분 체적 × 1,300. */
tree_waste_root_method: string;
dirty: boolean;
}
@@ -67,6 +69,34 @@ export function appendTreeWasteFields(
}
panel.append(h.hintRow(L("B08_Quantity_Side_TreeWaste_Hint")));
// 뿌리 — 지침 부록1 이 두 방법을 둠. 뿌리분 직경은 흉고직경과 갈라 따로 받음(지침 표기가 모호).
panel.append(
h.selectField(
L("B08_Quantity_TreeWaste_RootMethod"),
draft.tree_waste_root_method,
[
{ value: "", label: L("B08_Quantity_TreeWaste_RootRatio") },
{ value: "root_ball", label: L("B08_Quantity_TreeWaste_RootBall") },
],
(value) => {
draft.tree_waste_root_method = value;
draft.dirty = true;
},
),
);
panel.append(
h.optionalNumberField(
L("B08_Quantity_TreeWaste_RootBallDiameter"),
draft.tree_waste["root_ball_diameter_m"] ?? null,
"0.01",
(value) => {
draft.tree_waste["root_ball_diameter_m"] = value;
draft.dirty = true;
},
),
);
panel.append(h.hintRow(L("B08_Quantity_TreeWaste_Root_Hint")));
const price = h.optionalNumberField(
L("B08_Quantity_TreeWaste_Price"),
draft.tree_waste_unit_price_krw_per_ton,
@@ -163,6 +163,9 @@ def default_settings() -> dict[str, Any]:
# 임목폐기물 — 현장 조사값 넷 `{stems_per_1000m2, dbh_cm, height_m, unit_weight_kg_m3}`.
# ⚠ 기본값을 두지 않는다 — 비면 톤이 안 선다(수종·임분마다 달라 지어내지 않음).
"tree_waste": {},
# 뿌리 산정법 — `None` 은 분배비 15/85(기본) · `"root_ball"` 은 뿌리분 체적 × 1,300㎏/㎥
# (환경친화적인 도로건설 지침 부록1). 뿌리분 직경은 `tree_waste.root_ball_diameter_m`.
"tree_waste_root_method": None,
# 처리단가(원/톤) — KCRA 중간처리단가 미확보라 **수동**. 넣으면 금액이 서되 미확정.
"tree_waste_unit_price_krw_per_ton": None,
# 폐기물 분리발주 — 기본 아님(경비로 원가에 듦 · 2026-09-14 판정).
+18 -5
View File
@@ -7,7 +7,6 @@
from __future__ import annotations
import math
import sys
from pathlib import Path
@@ -28,8 +27,8 @@ FIELD = {"stems_per_1000m2": 134, "dbh_cm": 16, "height_m": 10.0, "unit_weight_k
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)
# 지침 부록1 식·실정보고 모두 π 를 3.14 로 적음그대로 셈해 130.40 과 같다.
assert above == pytest.approx(130.40, abs=0.01)
assert root == pytest.approx(above * 15 / 85)
@@ -48,9 +47,9 @@ def test_단가가_없으면_금액이_안_서고_있으면_미확정() -> None:
assert priced["amount_krw"] == int(priced["amount"] * 60000)
def test_톤_경계를_알() -> None:
def test_톤_경계를_알리고_분리발주_대상_아님을_적는() -> None:
big = tree_waste_rows(SLOPE, FIELD)[0] # 약 153톤
assert big["amount"] >= 100 and "100톤 이상" in big["reason"]
assert "분리발주 대상 아님" in big["reason"] and "환경부" 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"]
@@ -64,3 +63,17 @@ def test_경비라_내역에_안_서고_제외_사유가_간다() -> None:
row = handed["임목폐기물 처리"]
assert row["in_bill"] is False and row["blocked_kind"] is None
assert "경비" in row["in_bill_reason"]
def test_뿌리분_체적법은_D_세제곱_곱하기_1300() -> None:
"""지침 부록1 — V = 0.3927 D³ · 뿌리분 중량 = V × 1,300㎏/㎥ (본수만큼)."""
field = {**FIELD, "root_ball_diameter_m": 0.5}
above, root = tree_waste_tons(7450.0, field, "root_ball")
stems = 134 / 1000 * 7450
assert root == pytest.approx(0.3927 * 0.5**3 * 1300 * stems / 1000)
assert above == pytest.approx(tree_waste_tons(7450.0, FIELD)[0])
def test_체적법인데_뿌리분_직경이_비면_안_선다() -> None:
row = tree_waste_rows(SLOPE, FIELD, root_method="root_ball")[0]
assert row["amount"] is None and "뿌리분 직경" in row["reason"]
+18 -4
View File
@@ -717,8 +717,22 @@ export const ui_locales_b2 = {
B08_Quantity_TreeWaste_Height: ["평균 수고(m)", "Mean tree height (m)"],
B08_Quantity_TreeWaste_UnitWeight: ["단위체적중량(㎏/㎥)", "Unit weight (kg/㎥)"],
B08_Quantity_Side_TreeWaste_Hint: [
"현장 조사값 넷을 넣으면 준비공 「임목폐기물 처리」가 톤으로 섭니다. 산식: 지상부 WA = 0.5 × π × (흉고직경/2)² × 수고 × (1+0.3) × 단위체적중량 × 본수 · 뿌리부 WR = WA × 15/85 · 본수 = 1,000㎡당 본수 × 벌목·지장목제거 면적 — 한국건설기술연구원(2012) 3.2. 단위체적중량 참고: 소나무·침엽수 1,210~1,250 · 단풍·산벚 1,250~1,300 · 느티 1,300~1,340 · 상수리·졸참 1,340 이상(조경설계요람). 기본값 없음 — 수종·임분마다 다름",
"Enter the four field-survey values to raise the tree-waste row in tonnes. WA = 0.5 × π × (DBH/2)² × height × 1.3 × unit weight × stems; roots WR = WA × 15/85 (KICT 2012). No defaults — values differ by species and stand",
"현장 조사값 넷을 넣으면 준비공 「임목폐기물 처리」가 톤으로 섭니다. 산식: 지상부 WA = 0.5 × 3.14 × (흉고직경/2)² × 수고 × (1+0.3) × 단위체적중량 × 본수 · 본수 = 1,000㎡당 본수 × 벌목·지장목제거 면적 — 환경친화적인 도로건설 지침 부록1 · 한국건설기술연구원(2012) 3.2. 단위체적중량 참고: 소나무·침엽수 1,210~1,250 · 단풍·산벚 1,250~1,300 · 느티 1,300~1,340 · 상수리·졸참 1,340 이상(조경설계요람). 기본값 없음 — 수종·임분마다 다름",
"Enter the four field-survey values to raise the tree-waste row in tonnes. WA = 0.5 × 3.14 × (DBH/2)² × height × 1.3 × unit weight × stems (road guideline appendix 1, KICT 2012). No defaults — values differ by species and stand",
],
B08_Quantity_TreeWaste_RootMethod: ["뿌리부 산정법", "Root estimate"],
B08_Quantity_TreeWaste_RootRatio: [
"분배비 WA × 15/85 (기본)",
"Share ratio WA × 15/85 (default)",
],
B08_Quantity_TreeWaste_RootBall: ["뿌리분 체적 × 1,300㎏/㎥", "Root-ball volume × 1,300 kg/㎥"],
B08_Quantity_TreeWaste_RootBallDiameter: [
"뿌리분 직경 D(m, 흉고직경 아님) — 체적법만",
"Root-ball diameter D (m, not DBH) — volume method",
],
B08_Quantity_TreeWaste_Root_Hint: [
"환경친화적인 도로건설 지침 부록1 이 두 방법을 둡니다. 분배비(기본): 뿌리 15 : 지상 85(일반 수목류, 한국건설기술연구원 2012). 뿌리분 체적: V = πD²(D/2 + RH2/3) = 0.3927D³ · 뿌리분 중량 = V × 1,300㎏/㎥ × 본수. ⚠ 지침은 D 를 「뿌리분의 직경(DBH, m)」으로 적어 흉고직경인지 뿌리분 직경인지 갈리지 않음 — 흉고직경으로 두지 않고 따로 받습니다. 참고: 실무 실정보고(한 현장)는 뿌리 14 : 86 · 0.65톤/㎥ 를 씀",
"Road guideline appendix 1 gives two methods: share ratio 15:85 (default), or root-ball volume 0.3927·D³ × 1,300 kg/㎥ × stems. The guideline labels D ambiguously, so it is entered separately",
],
B08_Quantity_TreeWaste_Price: ["처리단가(원/톤) — 수동", "Disposal price (KRW/t) — manual"],
B08_Quantity_TreeWaste_Price_Hint: [
@@ -729,8 +743,8 @@ export const ui_locales_b2 = {
B08_Quantity_TreeWaste_Separate_Off: ["분리발주 아님(기본)", "No (default)"],
B08_Quantity_TreeWaste_Separate_On: ["분리발주", "Yes"],
B08_Quantity_TreeWaste_Separate_Hint: [
"기본은 분리발주 아님 — 원가계산서 경비에 듭니다. 켜면 총원가 밖에 서고 총공사비에만 더합니다. 참고: 건설폐기물 위탁처리 100톤 이상 분리발주 대상(건설폐기물 업무처리지침)이나, 같은 지침은 임목폐기물을 건설폐기물이 아닌 사업장 일반폐기물로 분류함 · 5톤 이상은 배출자 신고 대상(폐기물관리법 시행령 제2조8호)",
"Default: not separate — included in the expense category. When on, it sits outside total cost. Note: the 100 t separate-order rule is for construction waste; tree waste is classified as business general waste. 5 t or more requires a discharge report",
"⚠ 임목폐기물은 분리발주 대상 아님(환경부 질의회신 — 임목폐기물처리 실정보고 p4 · 건설폐기물 업무처리지침: 건설폐기물 아닌 사업장 일반폐기물) — 기본 꺼짐으로 원가계산서 경비에 듭니다. 이 칸은 다른 건설폐기물(위탁처리 100톤 이상 분리발주 대상)이 생길 때 쓰는 칸 — 켜면 총원가 밖에 서고 총공사비에만 더합니다. 5톤 이상은 배출자 신고 대상(폐기물관리법 시행령 제2조8호)",
"Tree waste is not subject to separate ordering (Ministry of Environment reply; classified as business general waste) — off by default, inside the expense category. Use this switch for other construction waste (100 t or more). When on, it sits outside total cost. 5 t or more requires a discharge report",
],
B08_Quantity_Side_SubgradeCompaction: ["노체다짐", "Subgrade Compaction"],
B08_Quantity_SubgradeCompaction_Label: ["노체다짐을 셀 것인가", "Count subgrade compaction"],