Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
103 lines
5.1 KiB
Python
103 lines
5.1 KiB
Python
"""예취기·분무기 인력 표 한 모양 넷 — 2026-09-15 브레인 차례 ② (사유 없던 49 다음).
|
||
|
||
산림 6-2-2 줄베기 · 6-2-3 모두베기 · 6-4-1 덩굴걷기 · 6-4-2 덩굴 약제 표는 「공법 | (갈래) | 사용도구 | 단위 | 소요인력 |
|
||
인력구분」 줄 — 이름이 끝 칸이라 줄 읽기가 한 줄도 못 읽었음(6-4-1 은 사유조차 없었음).
|
||
6-2-2 묘목찾기 낫 인/100본 0.07 보통인부 · 줄베기 1,500본 미만 1.50 · 1,500~3,000 2.00 · 3,000 이상 2.50 특별인부(인/ha)
|
||
6-2-3 묘목찾기 0.07 · 모두베기 조림 당해 연도 3.50 · 2년차 4.00 · 3년차 이상 5.00
|
||
6-4-1 기계작업 예취기 인/ha 3.30 특별인부 · 6-4-2 약제살포 배부식분무기 1.00 특별인부 + 작업보조 1.50 보통인부
|
||
⭐ 6-2-2·6-2-3 [주]④⑤ 「묘목찾기와 줄베기(모두베기) 품을 **합하여** 적용」 — 조림목 본수(본/ha)는 설계 입력 칸 ·
|
||
비면 「일부만」 으로 막음(반만 선 틀린 값보다 안 선 값) · 제안값 없음(부록 예시 2,700본/ha 는 품도 표와 다른 자료)
|
||
2장: 예취기 휘발유 5.0ℓ/대/일 · 잡품 10%(2-1-1 2 [주]① 줄베기·모두베기·지상부 덩굴걷기에만) · 손료 0.0084(2-2-2) ·
|
||
배부식분무기 손료 0.0084(2-2-4) — 체인톱 한 벌 길 · 1대당 1인
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from decimal import Decimal
|
||
|
||
import pytest
|
||
|
||
from B09_Estimation.B09_Estimation_KnownGaps import known_gap_note
|
||
from B09_Estimation.B09_Estimation_UnitPrice import cached_build
|
||
|
||
CUTTER = "X-AR-X-da716e54"
|
||
SPRAYER = "X-AR-X-5d0a0416"
|
||
|
||
|
||
def _rows(book, title: str) -> dict[str, Decimal]:
|
||
return {r.ref_code: r.quantity for r in book.details[title]}
|
||
|
||
|
||
def test_덩굴걷기는_특별인부와_예취기_1대_1일() -> None:
|
||
build = cached_build()
|
||
rows = _rows(build.book, "B-FP-06-04-01")
|
||
assert rows["1003"] == Decimal("3.30") and rows[CUTTER] == Decimal("3.30"), rows
|
||
assert "FP-06-04-01" not in build.partial_ratio and "FP-06-04-01" not in build.basis_missing
|
||
assert build.book.titles["B-FP-06-04-01"].unit == "ha"
|
||
cutter = _rows(build.book, CUTTER)
|
||
assert cutter["M-FUEL-휘발유"] == Decimal("5.0"), cutter
|
||
misc = next(r for r in build.book.details[CUTTER] if r.percent_of_material is not None)
|
||
assert misc.percent_of_material == Decimal(10)
|
||
assert "예취기가격" in " ".join(build.unattached["FP-06-04-01"])
|
||
|
||
|
||
def test_덩굴_약제는_두_인력을_더하고_분무기_손료_칸() -> None:
|
||
build = cached_build()
|
||
rows = _rows(build.book, "B-FP-06-04-02")
|
||
assert rows["1003"] == Decimal("1.00") and rows["1002"] == Decimal("1.50"), rows
|
||
assert SPRAYER not in rows # 연료 없는 장비 — 구입가가 없으면 빈 호표를 안 세움
|
||
assert "배부식분무기가격" in " ".join(build.unattached["FP-06-04-02"])
|
||
assert "FP-06-04-02" not in build.basis_missing # 단위 칸 인/ha 가 밑수
|
||
priced = cached_build(material_prices=(("AR-M-d6394831", "150000", "견적"),))
|
||
assert _rows(priced.book, "B-FP-06-04-02")[SPRAYER] == Decimal("1.00")
|
||
assert priced.book.resolve(SPRAYER).expense == Decimal("1260.0000") # 150,000 × 0.0084
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("code", "variants"),
|
||
[
|
||
(
|
||
"FP-06-02-02",
|
||
{"1,500본미만": "1.50", "1,500본이상3,000본미만": "2.00", "3,000본이상": "2.50"},
|
||
),
|
||
(
|
||
"FP-06-02-03",
|
||
{
|
||
"조림당해연도(전년도추기조림포함)": "3.50",
|
||
"조림2년차": "4.00",
|
||
"조림3년차이상": "5.00",
|
||
},
|
||
),
|
||
],
|
||
)
|
||
def test_풀베기는_갈래마다_특별인부_예취기_묘목찾기는_본수_칸(code, variants) -> None:
|
||
empty = cached_build()
|
||
for key, people in variants.items():
|
||
rows = _rows(empty.book, f"B-{code}#{key}")
|
||
assert rows["1003"] == Decimal(people) and rows[CUTTER] == Decimal(people), rows
|
||
assert "1002" not in rows # 본수가 없으면 묘목찾기를 안 셈
|
||
assert code in empty.partial_ratio # 반만 선 값으로 안 붙게 막음
|
||
assert "조림목 본수" in empty.component_gaps[code], empty.component_gaps.get(code)
|
||
filled = cached_build(seedlings_per_ha="2700")
|
||
for key in variants:
|
||
rows = _rows(filled.book, f"B-{code}#{key}")
|
||
assert rows["1002"] == Decimal("0.07") * Decimal(27), rows # 0.07인/100본 × 2,700본/ha
|
||
assert code not in filled.partial_ratio
|
||
assert "조림목 본수" not in (filled.component_gaps.get(code) or "")
|
||
|
||
|
||
def test_본수_칸은_양수만_받음() -> None:
|
||
from B09_Estimation.B09_Estimation_Brushcutting import parse_seedlings_per_ha
|
||
|
||
assert parse_seedlings_per_ha("") is None
|
||
assert parse_seedlings_per_ha("2,700") == Decimal(2700)
|
||
for bad in ("0", "-5", "많이"):
|
||
with pytest.raises(ValueError):
|
||
parse_seedlings_per_ha(bad)
|
||
|
||
|
||
def test_부록_예시와_표의_어긋남은_기록만() -> None:
|
||
note = known_gap_note("FP-06-02-02")
|
||
assert "부록" in note and "0.10" in note and "0.07" in note, note
|
||
assert "2,700" not in (cached_build().component_gaps.get("FP-06-02-02") or "") # 제안값 없음
|