- 계수로 미리 접으면 267,360 → 55,699 로 1원 틀림 — 식 1/8*16/12*25/20 을 차례로 풀어 55,700 - 중기 호표 운전원 줄은 시간당 노임 제목(#시간) × 인수로 부름(STmate 운전원 1 × 55,700 모양) - 골든셋 시험 test_b09_golden_stmate — 실무 6건 환율및기초자료 시간당 칸 전수 재현 (다섯 건 원 미만 절사 · 2025 울진 소광 0.1원 미만 절사 — 자리는 설정값) - 28자리 끝 반올림 차례만 다른 합 비교 시험을 허용 오차로 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
"""조종원 시간당 노임 — 식 좌→우 순차 + 원 미만 절사 (2026-09-13, PLAN 6장 · 명세 7장).
|
||
|
||
기준점 — STmate 분석 18번 §2.1: 6건 × 3직종 18/18 이 좌→우일 때만 일치(계수 선계산 14/18).
|
||
건설기계운전사 267,360 → 55,700 · 화물차운전사 226,709 → 47,231 ·
|
||
일반기계운전사 161,142 → 33,571
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from decimal import Decimal
|
||
from functools import lru_cache
|
||
|
||
import pytest
|
||
|
||
from B09_Estimation.B09_Estimation_MachineCost import (
|
||
OPERATOR_ALLOWANCE_FACTOR,
|
||
OPERATOR_WAGE_FORMULA,
|
||
hourly_operator_wage,
|
||
)
|
||
from B09_Estimation.B09_Estimation_UnitPrice import HOURLY_WAGE_SUFFIX, build_unit_prices
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("daily", "hourly"),
|
||
[(267360, 55700), (226709, 47231), (161142, 33571)],
|
||
)
|
||
def test_좌에서_우로_차례로_풀고_원_미만_절사(daily: int, hourly: int) -> None:
|
||
assert OPERATOR_WAGE_FORMULA == "1/8*16/12*25/20"
|
||
assert hourly_operator_wage(Decimal(daily)) == Decimal(hourly)
|
||
|
||
|
||
def test_계수를_미리_접으면_1원_틀린다() -> None:
|
||
"""회귀 막이 — 접은 계수 곱은 55,699.99… 라 절사하면 55,699(STmate 55,700)."""
|
||
folded = (Decimal(267360) / 8 * OPERATOR_ALLOWANCE_FACTOR).to_integral_value(
|
||
rounding="ROUND_FLOOR"
|
||
)
|
||
assert folded == Decimal(55699) # 종전 코드 — 계수 1.666…6 이 끝자리에서 잘려 55,699.99…
|
||
assert hourly_operator_wage(Decimal(267360), digits=None) == Decimal(55700)
|
||
|
||
|
||
@lru_cache(maxsize=1)
|
||
def _build():
|
||
return build_unit_prices()
|
||
|
||
|
||
def test_중기_호표의_운전원은_시간당_노임_제목을_1시간분_부른다() -> None:
|
||
book = _build().book
|
||
hourly = [code for code in book.titles if code.endswith(HOURLY_WAGE_SUFFIX)]
|
||
assert hourly, "조종원 시간당 제목이 한 벌도 안 섬"
|
||
for code in hourly:
|
||
daily = (
|
||
book.titles[code.removesuffix(HOURLY_WAGE_SUFFIX)].adopted_price()
|
||
if code.removesuffix(HOURLY_WAGE_SUFFIX) in book.titles
|
||
else None
|
||
)
|
||
price = book.titles[code].adopted_price()
|
||
assert price == price.to_integral_value() # 원 미만 절사
|
||
if daily is not None:
|
||
assert price == hourly_operator_wage(daily)
|
||
# X 층 운전원 줄은 계수 접은 수량이 아니라 시간당 제목 × 인수.
|
||
operator_rows = [
|
||
detail
|
||
for details in book.details.values()
|
||
for detail in details
|
||
if detail.ref_code.endswith(HOURLY_WAGE_SUFFIX)
|
||
]
|
||
assert operator_rows and all(
|
||
d.quantity == d.quantity.to_integral_value() for d in operator_rows
|
||
)
|