Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
85 lines
4.0 KiB
Python
85 lines
4.0 KiB
Python
"""치즐 소모량(Ⓐ-3 · 2026-09-14 브레인 판정) — **칸을 채우면 치즐 공종 상태가 움직이는가**로 잼.
|
||
|
||
「칸이 섰다」가 아니라 「칸을 채우면 못 붙은 줄이 풀린다」가 판정(81공종 다시 셈에서 칸을 다 채워도
|
||
상태가 안 움직였던 까닭이 치즐 15공종). 단가는 없음 — 시험의 223,000 원은 실무 원본(영월 M00118)
|
||
대조용 값이고 프로그램에 넣지 않음.
|
||
|
||
근거 — 실무 원본 단가산출근거 「(2) 치즐 손료 · M=0.006 × 223,000 / Q = 382.2 W/㎥」(재료비) ·
|
||
품셈 제8장 [주]⑤ 「브레이커 … 손료 및 치즐 소모율을 **추가**」(기계 손료에 안 듦 → 이중계상 아님).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from decimal import Decimal
|
||
|
||
from B09_Estimation.B09_Estimation_Chisel import CHISEL_CODE
|
||
from B09_Estimation.B09_Estimation_UnitPrice import cached_build
|
||
|
||
#: 기본 조립에서 「치즐소모량」 이 못 붙은 줄로 남던 15공종.
|
||
CHISEL_ITEMS = (
|
||
"FP-09-04-01",
|
||
"FP-09-05-02",
|
||
"FP-09-12-02",
|
||
"FP-09-12-03",
|
||
*(f"FP-09-13-{n:02d}" for n in range(7, 19)),
|
||
)
|
||
PRICE = (CHISEL_CODE, "223000", "시험")
|
||
|
||
|
||
def _chisel_labels(build, code: str) -> list[str]:
|
||
return [label for label in build.unattached.get(code, []) if "치즐" in label]
|
||
|
||
|
||
def test_칸을_안_채우면_종전대로_못_붙은_줄() -> None:
|
||
base = cached_build()
|
||
assert CHISEL_CODE not in base.book.titles
|
||
assert all(_chisel_labels(base, code) for code in CHISEL_ITEMS)
|
||
# 「자재 단가」 탭에 칸이 서는 자리 — 치즐을 쓰는 16공종이 다 실림(암질 표 둘 포함)
|
||
assert set(CHISEL_ITEMS) <= set(base.material_uses[CHISEL_CODE])
|
||
|
||
|
||
def test_칸을_채우면_치즐_못_붙은_줄이_풀린다() -> None:
|
||
filled = cached_build(material_prices=(PRICE,))
|
||
left = {code: _chisel_labels(filled, code) for code in CHISEL_ITEMS}
|
||
# 9-4-1 평균 갈래만 남음 — 원문 [주]① 은 Q 평균만 정하고 치즐 평균은 안 줌(지어내지 않음)
|
||
assert {code: labels for code, labels in left.items() if labels} == {
|
||
"FP-09-04-01": [left["FP-09-04-01"][0]]
|
||
}
|
||
assert "평균" in left["FP-09-04-01"][0]
|
||
whole = [code for code in CHISEL_ITEMS if not filled.unattached.get(code)]
|
||
assert len(whole) >= 12, (
|
||
whole
|
||
) # 못 붙은 줄이 아예 없어진 공종(9-13-14·15 는 들어내기 줄이 남음)
|
||
|
||
|
||
def test_치즐은_시간당_소모량_나누기_Q_로_재료비에_붙음() -> None:
|
||
base = cached_build()
|
||
filled = cached_build(material_prices=(PRICE,))
|
||
# 9-4-1 연암 — 0.006 본/hr ÷ Q 5.0 × 223,000 = 267.6 원/㎥ (배분율 없음)
|
||
gap = (
|
||
filled.book.resolve("B-FP-09-04-01#연암").material
|
||
- base.book.resolve("B-FP-09-04-01#연암").material
|
||
)
|
||
# 줄 0.1원·소계 원 미만 절사가 기존 재료비 끝전과 겹쳐 267 또는 268
|
||
assert Decimal("267") <= gap <= Decimal("268")
|
||
rock = next(d for d in filled.book.details["D-FP-09-04-01#연암"] if d.ref_code == CHISEL_CODE)
|
||
assert rock.quantity == Decimal("0.006") / Decimal("5.0") and rock.output == Decimal("5.0")
|
||
# 9-12-2 암절취 — 장비(90%) 몫 · 0.006 ÷ 3.5 × 0.9 × 223,000 = 344.05
|
||
detail = next(d for d in filled.book.details["D-FP-09-12-02"] if d.ref_code == CHISEL_CODE)
|
||
assert detail.quantity == Decimal("0.006") / Decimal("3.5") * Decimal("0.9")
|
||
assert detail.output == Decimal("3.5")
|
||
|
||
|
||
def test_치즐은_기계_손료와_겹치지_않음() -> None:
|
||
"""㉠ 브레이커 시간당 사용료는 손료만(부착 장비) — 치즐이 X 층 안에 없음."""
|
||
filled = cached_build(material_prices=(PRICE,))
|
||
breaker = next(code for code in filled.book.titles if code.startswith("X-0230-"))
|
||
stack, seen = [breaker], set()
|
||
while stack:
|
||
code = stack.pop()
|
||
assert code != CHISEL_CODE
|
||
if code in seen:
|
||
continue
|
||
seen.add(code)
|
||
stack.extend(d.ref_code for d in filled.book.details.get(code, []) if d.ref_code != code)
|