feat(822-2): 기계 시간당 식 자료 — 새 식 넷 · 33 대 옮김 · 직종 13 대 · 전력 칸 · POWER TROWEL
- 08장 로직: GC001196 전력 기종 · GC001197 전력 + 조종원 · GC001198 손료계수 한 칸 연료 · GC001199 직접 값 - 손료만 식(GC000273) 목록에서 연료 · 조종원 · 전력 있는 기계 뺌 → 276 · 268 · 새 식 목록(보정 식 274 그대로) - 기계표: 주연료 칸 ㎾ 글 10 대 → 전력(㎾) 숫자 칸(값원문 보존) · 원문 잡재료 「-」 = 0 · 조종원직종 13 대(요구 799) - EQ000534 이름 POWER TLOWEL → POWER TROWEL(옛이름 보존) - 가격 없는 기계(스프레어 둘 · TBM 4.5m) · 중유 믹싱플랜트 · 73.7+ 표층재생기는 목록 밖(꼴로 고름 → 까닭 글) - 수식 문서 802-2 다시(1,518 저장 · 기본 다름 0) · 기존 M01 문서 1,514 금액 전후 같음 - M01 기계 저장 손질: 보낸 단위 · 출처 칸이 있으면 그 글 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
"""기계 시간당 경비 식 완성(PLAN 822-2 · 요구 797 · 798 · 799) — 기계표 · 08장 로직 고침(정본 저장 길).
|
||||
|
||||
한 번만 돎(두 번째는 바뀐 것 없음):
|
||||
기계.json
|
||||
· 주연료 칸에 ㎾ 글이 든 전동 기계 → 숫자 칸 `전력`(㎾ · 전동기 부하율 규정 없음 → 그대로) · 주연료 비움
|
||||
(원문 글은 `값원문` 그대로 · 비고에 한 줄) · 원문 잡재료 「-」 = 잡재료율 0
|
||||
· 조종원직종 13 대 = 사용자 확정(요구 799) · 아스팔트 믹싱플랜트 잡재료 「-」 = 0
|
||||
· EQ000534 이름 오자 「POWER TLOWEL」 → 「POWER TROWEL」(원문 8-4-8 표기 · `옛이름` 보존)
|
||||
로직_건설품셈_08장_건설기계.json
|
||||
· 새 식 넷 — 전력 기종 · 전력 + 조종원 · 손료계수 한 칸 연료 기종 · 시간당 값 직접
|
||||
· 손료만 식(GC000273) 목록에서 옮긴 기계 뺌 → 맞는 식 목록에 더함(보정 식 GC000274 는 그대로)
|
||||
저장 = `M01_MasterData_Store_Make.save`(검사 · 키 대장 · 줄 모양 그대로 · 개정확인).
|
||||
수식 문서는 뒤에 `build_수식문서_802_2.py` 로 다시.
|
||||
|
||||
venv python resources/master_data/scripts/build_기계시간당_822_2.py [--check]
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as store # noqa: E402
|
||||
from M01_MasterData import M01_MasterData_Store_Make as make # noqa: E402
|
||||
|
||||
MACHINE = "기계.json"
|
||||
LOGIC = "로직_건설품셈_08장_건설기계.json"
|
||||
JOB_SRC = "건설품셈 공통 8-1-3 5 · 건설노임 {}"
|
||||
JOBS = { # 요구 799 — (직종, 까닭)
|
||||
**{f"EQ00016{i}": ("1048", "건설기계관리법상 건설기계") for i in range(5, 10)},
|
||||
**{
|
||||
k: ("1048", "원문 8-1-3 5 표의 「쇄석기」")
|
||||
for k in ("EQ000249", "EQ000250", "EQ000251", "EQ000252")
|
||||
},
|
||||
"EQ000347": ("1048", "원문 8-1-3 5 표의 「천공기」"),
|
||||
"EQ000348": ("1048", "원문 8-1-3 5 표의 「천공기」"),
|
||||
"EQ000309": ("1050", "두 법에 없는 기계"),
|
||||
"EQ000433": ("1050", "두 법에 없는 기계"),
|
||||
}
|
||||
POWER = { # 주연료 칸 ㎾ 글 → 전력(원문 값 그대로)
|
||||
"EQ000345": 330, "EQ000347": 135, "EQ000348": 239, "EQ000426": 15.1, "EQ000427": 15.1,
|
||||
"EQ000529": 3.73, "EQ000530": 7.46, "EQ000531": 37, "EQ000533": 1.49, "EQ000532": 1.87,
|
||||
} # fmt: skip
|
||||
MISC_DASH = ("EQ000426", "EQ000427", "EQ000529", "EQ000530", "EQ000531", "EQ000533",
|
||||
*(f"EQ0001{n}" for n in range(87, 92))) # fmt: skip
|
||||
TYPO = ("EQ000534", "POWER TLOWEL", "POWER TROWEL")
|
||||
|
||||
P1, P2, N3, DIRECT = "전력", "전력조종원", "손료한칸연료", "직접"
|
||||
MOVE = { # 식 → 더할 원문번호(손료만 식 GC000273 에서는 뺌)
|
||||
"GC000276": ["2208-5008", "2208-5010", "2208-5012", "2208-5016", "2208-5020", "5105-0050",
|
||||
"5105-0100", "5105-0150", "5105-0200", "5119-0625", "7205-0125"],
|
||||
"GC000268": ["3108-0060", "3108-0080", "3108-0100", "3108-0120", "9010-0120",
|
||||
"9010-0200"],
|
||||
P1: ["7991-0050", "7202-0008", "7202-0010", "7991-0100", "7991-0500",
|
||||
"7992-0001", "7993-0020"],
|
||||
P2: ["5805-0002", "5805-0003"],
|
||||
N3: ["7994-0050"],
|
||||
} # fmt: skip
|
||||
LOSS_ONLY = "GC000273"
|
||||
#: 손료만 식에서 빼기만 — 주연료 「73.7+ 휘발유54.5」 를 풀지 않음(꼴로 고름 → 까닭 글)
|
||||
DROP_ONLY = ["3450-0642"]
|
||||
#: 목록 밖(꼴로 고름 → 값 모자람 까닭 글) — 가격 없는 기계를 목록에 두면 식 기본 · 옵션 대조가 갈림(802-2):
|
||||
#: 아스팔트 스프레어 둘(가격 · 손료 없음 → 직종 입력 식 꼴) · TBM 4.5m(가격 없음 → 전력 식 꼴)
|
||||
#: 3108-0040 = 주연료 중유 — 유가 표에 중유 없음(검사가 막음) → 목록 밖 · 꼴로 고름 → 까닭 글
|
||||
LABEL = {P1: "전력", P2: "전력 조종원", N3: "손료계수 한 칸", DIRECT: "직접 값"}
|
||||
|
||||
_HEAD = {
|
||||
"원문번호": "8",
|
||||
"구분": "건설품셈 공통",
|
||||
"상세구분": "08장 건설기계",
|
||||
"결과단위": "원/시간",
|
||||
}
|
||||
_SRC = "건설품셈 공통 8-1-5 · 8-1-6"
|
||||
_LOSS_ONE = {
|
||||
"이름": "손료",
|
||||
"식": "버림(EQ:{기계}.가격 * 1000 * EQ:{기계}.손료계수 / 10000000, 0)",
|
||||
"출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4",
|
||||
}
|
||||
_HOUR = {
|
||||
"이름": "시간환산",
|
||||
"식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모",
|
||||
"출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)",
|
||||
}
|
||||
_JOB = {"이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5"}
|
||||
_CREW = {
|
||||
"종류": "인력",
|
||||
"요소": "LB:{직종}",
|
||||
"이름": "조종원",
|
||||
"단위": "인",
|
||||
"수량": "EQ:{기계}.조종원 * 시간환산",
|
||||
"비목": "노무비",
|
||||
"출처": "건설품셈 공통 8-1-3 6",
|
||||
"비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2)",
|
||||
}
|
||||
_ELEC_IN = {
|
||||
"이름": "전력 계약종별",
|
||||
"고르기": ["일반용(갑)Ⅰ 저압", "주택용 저압"],
|
||||
"출처": "유가전력 한전 전기요금표 2025-01-01",
|
||||
"비고": "품셈·계약예규에 계약종별 규정 없음(전력비는 실비 계상) — 기본 = 불러오기 기본값",
|
||||
}
|
||||
_ELEC = {
|
||||
"종류": "재료",
|
||||
"요소": {"이름": "전력량요금", "계약종별": "입력"},
|
||||
"이름": "전력",
|
||||
"단위": "㎾h",
|
||||
"수량": "EQ:{기계}.전력",
|
||||
"비목": "재료비",
|
||||
"출처": "건설품셈 공통 8-1-6 3 가",
|
||||
"비고": "기계 줄 전력(㎾) 그대로 — 전동기 부하율 규정 없음",
|
||||
}
|
||||
_FUEL = {"이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰"}
|
||||
_FUEL_LINE = {
|
||||
"종류": "재료",
|
||||
"요소": "MO:{연료}:{지역}",
|
||||
"이름": "주연료",
|
||||
"단위": "ℓ",
|
||||
"수량": "EQ:{기계}.주연료",
|
||||
"비목": "재료비",
|
||||
"출처": "건설품셈 공통 8-4-8",
|
||||
}
|
||||
_REGION = {"이름": "지역", "고르기": None, "출처": "건설품셈 공통 8-1-7 5"}
|
||||
_MISC = {
|
||||
"이름": "잡재료비",
|
||||
"식": "재료비 * EQ:{기계}.잡재료율 / 100",
|
||||
"비목": "재료비",
|
||||
"출처": "건설품셈 공통 8-4 주②",
|
||||
}
|
||||
_LOSS_ADD = {"이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2"}
|
||||
_CUT = "손료 원 미만 버림(건설품셈 공통 8-1-5 12) · 합계 원문 규정 없음"
|
||||
_S = {"기호": "S"}
|
||||
|
||||
|
||||
def _vars(*names: str) -> dict:
|
||||
sign = {"손료": "C", "시간환산": "Q"}
|
||||
return {
|
||||
n: {"기호": sign.get(n, "S"), "아래": n.replace("전력 계약종별", "전력계약종별")}
|
||||
for n in names
|
||||
}
|
||||
|
||||
|
||||
def _machine_in(codes: list[str]) -> dict:
|
||||
return {"이름": "기계", "고르기": codes}
|
||||
|
||||
|
||||
def new_rows(regions: list) -> dict[str, dict]:
|
||||
"""새 식 넷(키 없음 — 저장이 대장 다음 번호)."""
|
||||
region = {**_REGION, "고르기": regions}
|
||||
return {
|
||||
P1: {
|
||||
**_HEAD,
|
||||
"이름": "기계 시간당 기계경비(전력 기종)",
|
||||
"출처": f"{_SRC} · 8-1-6 3 가",
|
||||
"부품": {"갈래": "기계경비식"},
|
||||
"소유": "공용",
|
||||
"비고": "원문 운전경비 표 주연료 칸에 ㎾ 가 찍힌 전동 기종 — 전력비 = 전력(㎾) × 전력량요금 · 잡재료 = 전력비의 %(8-4 주②)",
|
||||
"입력": [_machine_in(MOVE[P1]), _ELEC_IN],
|
||||
"중간": [_LOSS_ONE],
|
||||
"호표": [_ELEC],
|
||||
"덧줄": [_MISC, _LOSS_ADD],
|
||||
"끝수": _CUT,
|
||||
"변수표": _vars("기계", "전력 계약종별", "손료"),
|
||||
},
|
||||
P2: {
|
||||
**_HEAD,
|
||||
"이름": "기계 시간당 기계경비(전력 · 조종원 기종)",
|
||||
"출처": f"{_SRC} · 8-1-6 3 가",
|
||||
"부품": {"갈래": "기계경비식"},
|
||||
"소유": "공용",
|
||||
"비고": "원문 운전경비 표 주연료 칸에 ㎾ 가 찍히고 조종원이 있는 전동 기종(점보드릴) — 전력비 + 잡재료(전력비의 %) + 조종원",
|
||||
"입력": [_machine_in(MOVE[P2]), _ELEC_IN],
|
||||
"중간": [_LOSS_ONE, _JOB, _HOUR],
|
||||
"호표": [_ELEC, _CREW],
|
||||
"덧줄": [_MISC, _LOSS_ADD],
|
||||
"끝수": _CUT,
|
||||
"변수표": _vars("기계", "전력 계약종별", "손료", "직종", "시간환산"),
|
||||
},
|
||||
N3: {
|
||||
**_HEAD,
|
||||
"이름": "기계 시간당 기계경비(조종원 없는 기종 · 손료계수 한 칸)",
|
||||
"출처": _SRC,
|
||||
"부품": {"갈래": "기계경비식"},
|
||||
"소유": "공용",
|
||||
"비고": "원문 손료 표가 시간당 손료계수 하나만 준 기종(8-3-8 기타기계 등) 가운데 주연료가 있고 조종원이 없는 것",
|
||||
"입력": [_machine_in(MOVE[N3]), region],
|
||||
"중간": [_LOSS_ONE, _FUEL],
|
||||
"호표": [_FUEL_LINE],
|
||||
"덧줄": [_MISC, _LOSS_ADD],
|
||||
"끝수": _CUT,
|
||||
"변수표": _vars("기계", "지역", "손료", "연료"),
|
||||
},
|
||||
DIRECT: {
|
||||
**_HEAD,
|
||||
"이름": "기계 시간당 기계경비(직접 값)",
|
||||
"출처": "자체",
|
||||
"부품": {"갈래": "기계경비식"},
|
||||
"소유": "공용",
|
||||
"비고": "자체 — 구성 없는 기계 · 구성(손료 · 연료 · 조종원) 없이 시간당 노무비 · 재료비 · 경비를 바로 둔 기계(내 기계 「시간당 값 직접」) — 기계 줄 값 그대로",
|
||||
"입력": [{"이름": "기계", "고르기": []}],
|
||||
"중간": [],
|
||||
"호표": [],
|
||||
"덧줄": [
|
||||
{
|
||||
"이름": "시간당 노무비",
|
||||
"식": "EQ:{기계}.노무비",
|
||||
"비목": "노무비",
|
||||
"출처": "자체",
|
||||
},
|
||||
{
|
||||
"이름": "시간당 재료비",
|
||||
"식": "EQ:{기계}.재료비",
|
||||
"비목": "재료비",
|
||||
"출처": "자체",
|
||||
},
|
||||
{"이름": "시간당 경비", "식": "EQ:{기계}.경비", "비목": "경비", "출처": "자체"},
|
||||
], # fmt: skip
|
||||
"끝수": None,
|
||||
"변수표": _vars("기계"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _note(row: dict, text: str) -> None:
|
||||
was = str(row.get("비고") or "")
|
||||
if text not in was:
|
||||
row["비고"] = f"{was} · {text}" if was else text
|
||||
|
||||
|
||||
def machine_changes() -> list[dict]:
|
||||
rows = {r["키"]: r for r in store.read(MACHINE)[0]["줄"]}
|
||||
out = []
|
||||
for key, row in rows.items():
|
||||
new = copy.deepcopy(row)
|
||||
vals, unit, src = new["값"], new.setdefault("단위", {}), new.setdefault("출처", {})
|
||||
if key in POWER and vals.get("전력") is None:
|
||||
text = str(vals.get("주연료"))
|
||||
text += "㎾" if unit.get("주연료") == "㎾" else "" # 모르타르 믹서 = 단위 칸에 ㎾
|
||||
got = re.search(r"([\d.]+)\s*㎾", text)
|
||||
assert got and float(got.group(1)) == POWER[key], (key, text)
|
||||
vals["주연료"], vals["전력"] = None, POWER[key]
|
||||
unit["주연료"], unit["전력"] = "ℓ/hr", "kWh/hr"
|
||||
src["전력"] = src.get("주연료")
|
||||
_note(new, f"전력 = 원문 주연료 칸 「{text}」(㎾ · 전동기 부하율 규정 없음 → 그대로)")
|
||||
if key == "EQ000532":
|
||||
_note(new, "원문 「휘발유1.3」 갈래는 주연료2 칸 · 시간당 식은 전력")
|
||||
if key in MISC_DASH and vals.get("잡재료율") is None:
|
||||
vals["잡재료율"] = 0
|
||||
_note(new, "잡재료율 0 = 원문 잡재료 「-」")
|
||||
if key in JOBS and not vals.get("조종원직종"):
|
||||
job, why = JOBS[key]
|
||||
vals["조종원직종"] = job
|
||||
src["조종원직종"] = JOB_SRC.format(job)
|
||||
_note(new, f"직종 = 사용자 확정(요구 799) — {why}")
|
||||
if key == TYPO[0] and new.get("이름") == TYPO[1]:
|
||||
items = list(new.items())
|
||||
at = [k for k, _ in items].index("이름") + 1
|
||||
items.insert(at, ("옛이름", TYPO[1]))
|
||||
new = dict(items)
|
||||
new["이름"] = TYPO[2]
|
||||
if new.get("상세구분") == TYPO[1]:
|
||||
new["상세구분"] = TYPO[2]
|
||||
if new != row:
|
||||
out.append({"op": "edit", "key": key, "row": new})
|
||||
return out
|
||||
|
||||
|
||||
def logic_changes() -> list[dict]:
|
||||
data = store.read(LOGIC)[0]
|
||||
rows = {r["키"]: r for r in data["줄"]}
|
||||
if any(r.get("이름") == "기계 시간당 기계경비(직접 값)" for r in rows.values()):
|
||||
return []
|
||||
moved = {c for codes in MOVE.values() for c in codes} | set(DROP_ONLY)
|
||||
out = []
|
||||
for key in (LOSS_ONLY, *[k for k in MOVE if k.startswith("GC")]):
|
||||
new = copy.deepcopy(rows[key])
|
||||
pick = next(i for i in new["입력"] if i.get("이름") == "기계")
|
||||
if key == LOSS_ONLY:
|
||||
pick["고르기"] = [c for c in pick["고르기"] if str(c) not in moved]
|
||||
else:
|
||||
pick["고르기"] += [c for c in MOVE[key] if c not in pick["고르기"]]
|
||||
out.append({"op": "edit", "key": key, "row": new})
|
||||
regions = next(i["고르기"] for i in rows["GC000268"]["입력"] if i["이름"] == "지역")
|
||||
out += [{"op": "add", "row": row} for row in new_rows(regions).values()]
|
||||
return out
|
||||
|
||||
|
||||
def main(check: bool) -> None:
|
||||
parts = []
|
||||
for file, changes in ((MACHINE, machine_changes()), (LOGIC, logic_changes())):
|
||||
print(file, len(changes), [c.get("key") or c["row"]["이름"] for c in changes])
|
||||
if changes:
|
||||
parts.append({"file": file, "version": store.read(file)[1], "changes": changes})
|
||||
if check or not parts:
|
||||
return
|
||||
# 대장 열쇠 = 원문번호 — 넷 다 「8」 이면 한 키로 겹침 → 옛 기계경비식처럼 이름 붙은 열쇠(줄은 「8」)
|
||||
labels = {r["이름"]: f"공통 8 기계 시간당 경비({LABEL[t]})" for t, r in new_rows([]).items()}
|
||||
plain = store.ledger_label
|
||||
store.ledger_label = lambda book, tid, no, row: (
|
||||
labels.get(row.get("이름")) or plain(book, tid, no, row)
|
||||
)
|
||||
got = make.save(parts, confirmed=True)
|
||||
print("저장", got["added"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main("--check" in sys.argv)
|
||||
Reference in New Issue
Block a user