Files
Aislo/resources/tester/test_b09_consumables_chainsaw.py
eomsangdonandClaude Opus 5 852ad6c7d8 feat(b09): ㉡ 2장 소요재료·기계손료 표를 읽는 한 벌 — 체인톱부터(4-2-2 단목베기 · 6-5 어린나무가꾸기)
· 장비 한 벌(Equipment)에 「어느 표 줄을 읽을지」 만 적고 값은 2장 표에서 읽음 — 나머지 소형 장비도 같은 길로 더함
· 체인톱 1대 1일 호표 = 휘발유 5.6ℓ(2-1-1) + 잡품 주연료비 40% + 손료 0.0084(2-2-1) × 구입가(「자재 단가」 칸 · 없으면 사유)
· 쓰는 공종에 표가 이름으로 밝힌 인원 줄 수량만큼(4-2-2 「벌목부」 · 6-5 「특별인부 (체인톱 사용)」 · 넓히지 않음)
· 체인오일 일반/친환경은 「자재 단가」 에 넣은 쪽 × 인원 × 2.1ℓ · 둘 다면 사유(브레인 판정 ①②③)

오른 제목: 4-2-2 +31·+41·+53원(+3.6%) · 6-5 +43,821원(+4.29%) · 나머지 그대로 · 프로젝트 내역 지장목제거 잡관목제거 +548,652(본체 109,122,948 → 109,671,600) · 잴 시험 넷 빨강→초록 · 끄기 넷 빨강 · 전체 시험 1712 + 381 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
2026-09-15 02:15:48 +09:00

81 lines
4.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""2장 소요재료·기계손료 표를 읽는 한 벌 — 체인톱부터(2026-09-15 브레인 ㉡ · 판정 ①②③).
산림품셈 2-1-1 체인톱 표(F0042): 보통휘발유(주연료) 5.6ℓ/대/일 · 잡품 주연료비 40% · 체인오일(일반·친환경) 2.1ℓ/대/일
「체인톱 대수는 산출된 벌목부 또는 특별인부의 100% 적용」 · 2-2-1 체인톱 손료(F0064): 45cc 0.0084(1일 1대) × 체인톱가격
체인톱을 쓰는 공종(4-2-2 단목베기 · 6-5 어린나무가꾸기)이 인력만으로 서서 연료·잡품·손료가 통째로 빠져 있었음.
③ 표가 이름으로 밝힌 인원 줄만 — 4-2-2 「벌목부」 · 6-5 「특별인부 (체인톱 사용)」
① 체인오일 일반/친환경은 「자재 단가」 에 넣은 쪽 · 둘 다면 안 붙고 사유
② 체인톱가격·체인오일 시중가격은 칸 + 사유 · 휘발유는 유가 판으로 바로 섬
"""
from __future__ import annotations
from decimal import ROUND_FLOOR, Decimal
from B09_Estimation.B09_Estimation_UnitPrice import cached_build
SAW = "X-AR-X-61d1681d"
SAW_PRICE = "AR-M-5649cf3f"
OIL = "AR-M-fa7fbf6d"
ECO_OIL = "AR-M-de2fe662"
def _rows(book, code: str) -> list:
own = book.details.get(code) or []
return [
*own,
*(r for d in own if d.ref_code.startswith("D-") for r in book.details[d.ref_code]),
]
def test_체인톱_1대_1일_호표는_표의_휘발유와_잡품_40퍼센트() -> None:
book = cached_build().book
rows = book.details[SAW]
fuel = next(r for r in rows if r.ref_code == "M-FUEL-휘발유")
assert fuel.quantity == Decimal("5.6"), fuel
misc = next(r for r in rows if r.percent_of_material is not None)
assert misc.percent_of_material == Decimal(40)
price = book.titles["M-FUEL-휘발유"].slots[-1]
expected = (Decimal("5.6") * price * Decimal("1.4")).quantize(Decimal(1), rounding=ROUND_FLOOR)
assert abs(book.resolve(SAW).material - expected) <= 1, (book.resolve(SAW), expected)
def test_단목베기는_벌목부_인원만큼_체인톱_어린나무가꾸기는_체인톱_사용_특별인부만큼() -> None:
book = cached_build().book
rows = _rows(book, "B-FP-04-02-02#5m미만")
faller = next(r for r in rows if book.titles[r.ref_code].name == "벌목부")
saw = next(r for r in rows if r.ref_code == SAW)
assert saw.quantity == faller.quantity, (saw, faller)
rows = _rows(book, "B-FP-06-05")
special = next(
r
for r in rows
if book.titles.get(r.ref_code) and book.titles[r.ref_code].name == "특별인부"
)
saw = next(r for r in rows if r.ref_code == SAW)
assert saw.quantity == special.quantity, (saw, special)
# 보통인부(작업 보조)는 체인톱을 안 씀 — 표가 이름으로 밝힌 줄만(③)
assert sum(1 for r in rows if r.ref_code == SAW) == 1
def test_체인오일은_넣은_쪽만_둘_다면_사유() -> None:
none = cached_build()
assert any("체인오일" in label for label in none.unattached.get("FP-04-02-02", []))
one = cached_build(material_prices=((OIL, "2250", "시중"),))
rows = _rows(one.book, "B-FP-04-02-02#5m미만")
faller = next(r for r in rows if one.book.titles[r.ref_code].name == "벌목부")
oil = next(r for r in rows if r.ref_code == OIL)
assert oil.quantity == faller.quantity * Decimal("2.1"), oil
both = cached_build(material_prices=((OIL, "2250", "시중"), (ECO_OIL, "6000", "시중")))
assert not any(r.ref_code in (OIL, ECO_OIL) for r in _rows(both.book, "B-FP-04-02-02#5m미만"))
assert any("둘 다" in label for label in both.unattached.get("FP-04-02-02", []))
def test_체인톱가격이_들면_손료_0_0084_가_경비로() -> None:
none = cached_build()
assert any("체인톱가격" in label for label in none.unattached.get("FP-06-05", []))
priced = cached_build(material_prices=((SAW_PRICE, "900000", "견적"),))
assert priced.book.resolve(SAW).expense == Decimal(7560) # 900,000 × 0.0084
for code in (SAW_PRICE, OIL, ECO_OIL):
assert "FP-04-02-02" in none.material_uses.get(code, []), code