feat(b08,b09): 폐기물처리비 — 경비 비목(일반관리비·이윤 밑수) · 임목폐기물 톤 · 분리발주 칸

- 원가계산서: 폐기물처리비를 경비 줄로(예정가격작성기준 제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
This commit is contained in:
2026-09-14 00:55:41 +09:00
co-authored by Claude Opus 5
parent 0cf8b41823
commit 7d856f37a3
14 changed files with 531 additions and 13 deletions
+22 -11
View File
@@ -88,10 +88,14 @@ class CostInput:
#: 이윤 수동 조정액 — 설계자 명시 입력일 때만. 프로그램이 스스로 채우지 않는다.
profit_adjustment_krw: Decimal = _ZERO
#: 폐기물처리비 — 요율이 아니라 **실비**. 총원가 밖, 관급자재대와 나란히.
#: TODO(미결 PLAN 8-14·9-6): 자리 확정 대기 (사용자 「실무자 확인 후 재공유」).
#: 실무 근거는 거창 원가계산서의 `총공사비 = 도급액 + 관급자재대 + 폐기물처리비` 한 줄뿐.
#: 폐기물처리비 — 요율이 아니라 **실비**(수량 × 처리단가).
#: ⭐ 2026-09-14 판정 — **비목은 경비**(예정가격작성기준 제19조③18호)라 순공사원가에 들고
#: **일반관리비·이윤 밑수에 든다.** 빠지면 총액이 조용히 작아진다.
#: ⚠ 법정경비 밑수(직접공사비·노무비)에는 안 넣는다 — 그 밑수는 요율 데이터가 정한 합이다.
waste_disposal_krw: Decimal = _ZERO
#: 분리발주 — 켜면 공사와 따로 발주하는 용역이라 **총원가 밖**, 총공사비에만 더한다
#: (거창 원가계산서 `총공사비 = 도급액 + 관급자재대 + 폐기물처리비` 모양). 기본 꺼짐.
waste_separate_order: bool = False
#: 환경보전비 공종 (`rate_environment.all_work_types` 의 값).
#: TODO(미결 PLAN 9-6): 임도가 「도로 0.9 %」인지 「기타 토목 0.8 %」인지 미확정.
@@ -353,12 +357,14 @@ def _calculate_with_scale(
scale_reference=scale,
)
statutory = statutory_expenses(dataset, data, ctx, result, emit)
# 폐기물처리비 — 분리발주가 아니면 경비 한 줄. 법정경비 **뒤**에 둬 그 밑수에 안 섞인다.
waste_in_expense = _ZERO if data.waste_separate_order else _waste_line(data, emit)
expense_total = data.direct_expense_krw + statutory
expense_total = data.direct_expense_krw + statutory + waste_in_expense
emit(
key="expense",
name="경비",
base_label="직접경비(산출경비)+법정경비",
base_label="직접경비(산출경비)+법정경비" + ("+폐기물처리비" if waste_in_expense else ""),
base=expense_total,
amount=expense_total,
)
@@ -413,13 +419,14 @@ def _calculate_with_scale(
)
owner_total = _owner_supplied_line(data, emit)
waste = _waste_line(data, emit)
# 분리발주면 공사 원가가 아니다 — 총원가 밖에 서고 총공사비에만 더한다.
waste = _waste_line(data, emit) if data.waste_separate_order else _ZERO
grand_total = contract_amount + owner_total + waste
emit(
key="grand_total",
name="총공사비",
base_label="도급공사비+관급자재대" + ("+폐기물처리비" if waste else ""),
base_label="도급공사비+관급자재대" + ("+폐기물처리비(분리발주)" if waste else ""),
base=grand_total,
amount=grand_total,
)
@@ -436,7 +443,7 @@ def _calculate_with_scale(
"vat": vat,
"contract_amount": contract_amount,
"owner_supplied_material_total": owner_total,
"waste_disposal": waste,
"waste_disposal": waste_in_expense + waste,
"grand_total": grand_total,
}
return result
@@ -508,16 +515,20 @@ def _owner_supplied_line(data: CostInput, emit) -> Decimal:
def _waste_line(data: CostInput, emit) -> Decimal:
"""폐기물처리비 — 요율이 아니라 실비. 설계자 입력이 있을 때만 줄이 선다."""
"""폐기물처리비 — 요율이 아니라 실비. 금액이 있을 때만 줄이 선다."""
if not data.waste_disposal_krw:
return _ZERO
return emit(
key="waste_disposal",
name="폐기물처리비",
base_label="설계자 입력(실비)",
base_label="수량 × 처리단가(실비)",
base=data.waste_disposal_krw,
amount=floor_won(data.waste_disposal_krw),
note="⚠ 자리 미확정 — 실무 관측 한 줄이 유일한 근거 (PLAN 8-14)",
note=(
"분리발주 — 총원가 밖 별도 표기"
if data.waste_separate_order
else "경비(예정가격작성기준 제19조③18호) — 일반관리비·이윤 밑수에 듦"
),
)