feat(b09): 단가산출 Q 식 — f·Q 소수 2자리 확정 뒤 나눔 · D 줄 0.1원·머리 원 미만 절사

- fix2(사사오입 0.01) — 굴착기·도자·다짐 작업량, 암 평균 Q, 표 직접 작업량
- TRUNCATED_KINDS 에 PRICE_BASIS(D) 더함 — B 는 다음 차례
- 골든셋 ③ 단가산출근거 실무 6건 208 호표 전수 일치(제외 소계·% 줄·산근 참조·계약단가 읽기) · 회귀 3,545.5 → 3,545
- 검증 프로젝트 내역 본체 122,924,846 → 122,870,975 · 전체 시험 1606 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
2026-09-14 00:26:45 +09:00
co-authored by Claude Opus 5
parent e5295dc181
commit af23a5d209
7 changed files with 164 additions and 31 deletions
@@ -30,6 +30,7 @@ from B09_Estimation.B09_Estimation_MachineProductivity import (
ProductivityError,
_first_measure,
extract_cycle_factors,
fix2,
parse_measure,
)
@@ -112,12 +113,13 @@ class DozerFactors:
def formula_text(self) -> str:
return (
f"Q = 60 ÷ {self.cycle_minutes:.4f}분 × ({self.blade_capacity_m3} × "
f"{self.distance_factor}) × {self.volume_factor} × {self.efficiency}"
f"{self.distance_factor}) × {fix2(self.volume_factor)} × {self.efficiency}"
+ (f" = {dozer_hourly_output(self)} ㎥/hr" if self.cycle_minutes > 0 else "")
)
def dozer_hourly_output(factors: DozerFactors) -> Decimal:
"""불도저 시간당 작업량 `Q` (㎥/hr). **밑수는 60(분)** 이다."""
"""불도저 시간당 작업량 `Q` (㎥/hr). **밑수는 60(분)** — f 와 Q 는 소수 2자리로 확정(명세 7장)."""
if factors.cycle_minutes <= 0:
raise ProductivityError(f"{factors.work_item_code}: 싸이클 시간이 0 이하입니다.")
blade = factors.blade_capacity_m3 * factors.distance_factor
@@ -125,12 +127,12 @@ def dozer_hourly_output(factors: DozerFactors) -> Decimal:
_MINUTES_PER_HOUR
/ factors.cycle_minutes
* blade
* factors.volume_factor
* fix2(factors.volume_factor)
* factors.efficiency
)
if output <= 0:
raise ProductivityError(f"{factors.work_item_code}: 시간당 작업량이 {output} 입니다.")
return output
return fix2(output)
def dozer_speeds(