feat(z01): 기초단가 열 이름을 이름표에서만 읽음 — 합친 표 열 key 를 이름표 merged_tables 에 맞춤(노임 source · 유가 source·region_code·region_name·price_krw_per_l · 기계 fuel_type·fuel_rate_l_per_hour·operator_person_per_day·hourly_loss_krw·hourly_fuel_krw·hourly_operator_krw·hourly_total_krw) · 표 수준 알림을 서버가 한 벌로(새로 만드는 프로젝트부터 쓰임 · 요율은 법정값 경고까지) · 출처 받는 자리 빈 곳은 「없음」(브레인 ①②③)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
2026-09-16 08:50:45 +09:00
co-authored by Claude Opus 5
parent 909b055aa9
commit 05bab3b61f
3 changed files with 100 additions and 62 deletions
@@ -25,17 +25,17 @@ from Z01_MasterData.Z01_MasterData_Tables import RESOURCES
#: B09 셈이 여는 원본 셋 — 바뀌면 캐시를 새로 읽음
_INPUT_FILES = ("mach_base_2026.json", "pum_const_2026.json", "labor_const_2026-01-01.json")
EDITABLE = ["price_thousand_krw", "loss_coefficient_per_hour"]
COMPUTED = ("loss_krw_per_hour", "fuel_krw_per_hour", "operator_krw_per_hour", "hourly_krw")
COMPUTED = ("hourly_loss_krw", "hourly_fuel_krw", "hourly_operator_krw", "hourly_total_krw")
#: 조종원 식 글자 — B09 식 문자열(좌→우 차례)을 사람이 읽게만 바꿈
_WAGE_TEXT = OPERATOR_WAGE_FORMULA.replace("*", " × ").replace("/", " ÷ ")
_FORMULA = {
"loss_krw_per_hour": "손료 = 취득가(천원) × 1,000 × 시간당 손료계수 (건설품셈 8-1-6)",
"fuel_krw_per_hour": "연료 = 주연료(ℓ/hr) × (1 + 잡재료%) × 전국평균 유가 (건설품셈 8-4 · 8-1-7)",
"operator_krw_per_hour": (
"hourly_loss_krw": "손료 = 취득가(천원) × 1,000 × 시간당 손료계수 (건설품셈 8-1-6)",
"hourly_fuel_krw": "연료 = 주연료(ℓ/hr) × (1 + 잡재료%) × 전국평균 유가 (건설품셈 8-4 · 8-1-7)",
"hourly_operator_krw": (
f"조종원 = 일 노임 × 인원 × {_WAGE_TEXT}"
" · 원 미만 절사 (건설품셈 8-1-2 · 건협 임금적용요령 4-나)"
),
"hourly_krw": "시간당 사용료 = 손료 + 연료 + 조종원",
"hourly_total_krw": "시간당 사용료 = 손료 + 연료 + 조종원",
}
_LOCKED_COMPUTED = "계산값 — 밑값(취득가·손료계수는 이 표 · 노임·유가는 그 표)을 고치면 따라 바뀜"
_LOCKED_OPERATING = "건설품셈 8-4 운전경비표(로직)에서 옴 — 기초단가가 아님"
@@ -45,11 +45,11 @@ _LOCKED = {
"specification": "공표 원문 칸 — 원본 갱신으로만 바뀜",
"economic_life_hours": "공표 원문 칸 — 원본 갱신으로만 바뀜",
"annual_standard_hours": "공표 원문 칸 — 원본 갱신으로만 바뀜",
"fuel_kind": _LOCKED_OPERATING,
"fuel_liters_per_hour": _LOCKED_OPERATING,
"fuel_type": _LOCKED_OPERATING,
"fuel_rate_l_per_hour": _LOCKED_OPERATING,
"misc_material_percent": _LOCKED_OPERATING,
"operator_occupation_code": _LOCKED_OPERATING,
"operator_person_days": _LOCKED_OPERATING,
"operator_person_per_day": _LOCKED_OPERATING,
"fuel_price_krw_per_l": "유가 표에서 고침 — 여기는 덮개 얹은 전국평균을 끌어 씀",
"operator_daily_wage_krw": "노임 표에서 고침 — 여기는 덮개 얹은 건설 노임을 끌어 씀",
"hourly_note": "계산이 왜 안 섰는지 — 밑값이 비면 섬",
@@ -112,11 +112,11 @@ def base_rows(source_name: str, effective_date: str | None) -> list[dict[str, An
"loss_coefficient_per_hour": _num(m.loss_coefficient_per_hour),
"economic_life_hours": m.economic_life_hours,
"annual_standard_hours": m.annual_standard_hours,
"fuel_kind": record.fuel_kind if record else None,
"fuel_liters_per_hour": _num(record.fuel_liters_per_hour) if record else None,
"fuel_type": record.fuel_kind if record else None,
"fuel_rate_l_per_hour": _num(record.fuel_liters_per_hour) if record else None,
"misc_material_percent": _num(record.misc_material_percent) if record else None,
"operator_occupation_code": record.operator_occupation_code if record else None,
"operator_person_days": _num(record.operator_person_days) if record else None,
"operator_person_per_day": _num(record.operator_person_days) if record else None,
}
)
return rows
@@ -167,24 +167,24 @@ def compute(row: dict[str, Any], inputs: Inputs) -> dict[str, Any]:
)
money = cost.money
out["fuel_price_krw_per_l"] = _num(fuel_price)
out["loss_krw_per_hour"] = _num(money.expense)
out["hourly_loss_krw"] = _num(money.expense)
formula = {
"loss_krw_per_hour": (
"hourly_loss_krw": (
f"{_fmt(money.expense)} = {_fmt(spec.price_thousand_krw)}천원 × 1,000 × {spec.loss_coefficient_per_hour}"
)
}
if fuel_price is not None:
out["fuel_krw_per_hour"] = _num(money.material)
out["hourly_fuel_krw"] = _num(money.material)
misc = record.misc_material_percent if record else None
formula["fuel_krw_per_hour"] = (
formula["hourly_fuel_krw"] = (
f"{_fmt(money.material)} = {_fmt(record.fuel_liters_per_hour)}ℓ"
+ (f" × (1 + {_fmt(misc)}%)" if misc is not None else "")
+ f" × {_fmt(fuel_price)}원/ℓ"
)
if wage is not None:
out["operator_krw_per_hour"] = _num(money.labor)
out["hourly_operator_krw"] = _num(money.labor)
persons = record.operator_person_days
formula["operator_krw_per_hour"] = (
formula["hourly_operator_krw"] = (
f"{_fmt(money.labor)} = {_fmt(out['operator_daily_wage_krw'])}원"
+ (f" × {_fmt(persons)}인" if persons is not None else "")
+ f" × {_WAGE_TEXT} (원 미만 절사)"
@@ -192,9 +192,9 @@ def compute(row: dict[str, Any], inputs: Inputs) -> dict[str, Any]:
if cost.gaps:
out["hourly_note"] = " · ".join(cost.gaps)
else:
out["hourly_krw"] = _num(money.total)
out["hourly_total_krw"] = _num(money.total)
out["hourly_note"] = None
formula["hourly_krw"] = (
formula["hourly_total_krw"] = (
f"{_fmt(money.total)} = {_fmt(money.expense)} + {_fmt(money.material)} + {_fmt(money.labor)}"
)
out["@formula"] = formula