refactor(821-4): 820-1 기계 13 줄 · 파워트로웰 식 GC001195 지움 — 품셈에 없는 기계 = 내 기계

- 기계.json EQ000656 ~ 668(발전기 5.5 · 7 · 10㎾ · 대형 브레이커 0.18㎥ · 에어 호스 · 모노펌프 · 파워트로웰 · 콘크리트펌프용 파이프 여섯) 지움 · 로직 08장 GC001195 · GC000268 · GC000273 고르기에 끼운 「자체 …」 원문번호 뺌 · 수식 문서 08장 같이(820-1 바로 앞 판과 같음)
- _키대장 = 열넷 「폐기」 로 옮김(결번 · 다음 번호 EQ 669 · GC 1196 그대로 · 재사용 안 함)
- 둠: 덤프트럭(4륜구동) 1ton EQ000655 · GF000145 · 체인톱 시간당 식 GF000337 · 기계 별칭
- 기술문서 기계경비_산정 7 절 = 「품셈에 없는 기계 = 사용자 내 기계(요구서 796)」 한 단락
- 802-2 변환 대조 --check 같음(로직 1,514 · 저장본 · 목록 다시 쓸 것 없음)
- 낡은 시험 지움: test_820_1_small_machines(13 줄 값 · GC001195 계산 지킴 — 줄이 없어짐) · 빌더 build_기계_소형보조_820_1 — 지킴 뜻(빈칸 든 입력 이름 이음 글 왕복 · EST main 504 블록 금액 546)은 test_821_4 로 옮김
- 새 지킴 시험 test_821_4_import_my_machine(구성 → 내 기계 값 · 엑셀 · STmate 시트 · 직접 꼴 · 같은 이름 재사용 · 다시 불러와도 중복 없음 · 미리 보기 안 만듦 · 금액 그대로 · 13 줄 없음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 14:25:07 +09:00
co-authored by Claude Opus 5.5
parent 4e71ec515b
commit bc0a758ae0
8 changed files with 223 additions and 956 deletions
@@ -1,241 +0,0 @@
"""품셈에 없는 소형 · 보조 기계를 우리 기계표 줄로(PLAN 820-1 · 요구 785 ~ 787 · 방향 장부 Q1 · R).
EST 단가 구성 → 우리 줄 — 손료계수 · 연료 · 잡품 · 조종원 = 품셈 값(발전기 계열 2,362 · 대형 브레이커 6,601) ·
가격 = EST 씨앗(출처 「자체」 · 비고 「갱신 필요」) · 콘크리트펌프용 파이프 = 달러값 × 한국은행 환율(FX000001).
계산식:
손료만(브레이커 0.18 · 에어 호스 · 모노펌프 · 파이프 · 발전기 7 · 10㎾) = GC000273 고르기에 더함
발전기 5.5㎾(경유 · 잡품 · 일반기계운전사) = GC000268 고르기에 더함
→ 고르기 더하기는 `build_기계목록_빠진기계_814_1`(로직 · 수식 문서 목록 같이)
파워트로웰(휘발유 · 잡품 · 전력 · 조종원 없음) = 새 기계경비식 GC001195 — 기계 줄 값을 바로 읽음
(`M02_Machine_Hourly._own` 이 고름) · 잡품 = 주연료 줄의 % (전력 줄엔 안 붙임)
더하기만 — 다른 줄 · 서식 그대로 · 두 번 돌려도 같음.
쓰기: venv python resources/master_data/scripts/build_기계_소형보조_820_1.py
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
DATA = HERE.parent
ROOT = DATA.parents[1]
MACHINE = DATA / "기계.json"
BOOK = DATA / "_키대장.json"
LOGIC = DATA / "로직_건설품셈_08장_건설기계.json"
DOC = DATA / "수식문서" / "수식_건설품셈_08장_건설기계.json"
USD = 1414.9 # 원/USD — 한국은행 환율 FX000001(2026-08-14)
SEED = "EST 단가 구성 → 우리 줄 · 손료계수·연료 = 품셈 값 · 가격 = EST 씨앗(갱신 필요)"
GEN = {"상각비계수": 1125, "정비비계수": 563, "관리비계수": 674, "손료계수": 2362} # 발전기 계열
BRK = {"상각비계수": 3000, "정비비계수": 2833, "관리비계수": 768, "손료계수": 6601} # 0230-0002
UNIT = {
"가격": "천원", "상각비계수": "10^-7/시간", "정비비계수": "10^-7/시간",
"관리비계수": "10^-7/시간", "손료계수": "10^-7/시간", "주연료": "ℓ/hr", "주연료종류": "",
"잡재료율": "주연료의 %", "조종원": "인/일", "조종원직종": "", "전력": "㎾h/hr",
} # fmt: skip
FIX_SRC = {"발전기": "건설품셈 공통 8-3-8", "브레이커": "건설품셈 공통 8-3-1"}
WAGE = "건설품셈 공통 8-1-3 5 · 건설노임 1050"
SAB = "2016 사방사업표준품셈(안) 기계경비"
PIPE = {"150㎜×1m": 25, "150㎜×2m": 37, "150㎜×3m": 46,
"곡관 150㎜ 45°": 67, "곡관 150㎜ 60°": 84, "곡관 150㎜ 90°": 103} # fmt: skip
def _row(n, gubun, detail, name, spec, value, note, fix=None):
src = {k: (FIX_SRC[fix] if fix and k in GEN else "자체") for k in value}
if "조종원직종" in value:
src["조종원직종"] = WAGE
return {
"키": f"EQ{n:06d}",
"원문번호": f"자체 {name} {spec}",
"원문": "건설품셈",
"구분": gubun,
"상세구분": detail,
"이름": name,
"규격": spec,
"단위": {k: UNIT[k] for k in value},
"값": value,
"출처": src,
"비고": f"{SEED} · {note}",
}
def rows() -> list[dict]:
g, c, t = "[70]기타기계", "[40]콘크리트기계", "[00]토공기계"
fuel5 = {
"주연료": 0.625,
"주연료종류": "경유",
"잡재료율": 11,
"조종원": 1,
"조종원직종": "1050",
}
out = [
_row(656, g, "발전기", "발전기", "5.5㎾", {"가격": 1063, **GEN, **fuel5},
"EST 계 37,760 · 주연료 · 잡품 · 조종원(일반기계운전사) = " + SAB + "과 같음", "발전기"),
_row(657, g, "발전기", "발전기", "7㎾", {"가격": 1828.9, **GEN},
f"가격 = {SAB}(1,828,900 원) · 그 표 = 손료만(연료 · 조종원 없음)", "발전기"),
_row(658, g, "발전기", "발전기", "10㎾", {"가격": 3213, **GEN},
f"가격 = {SAB}(3,213,000 원) · 그 표 = 손료만(연료 · 조종원 없음)", "발전기"),
_row(659, t, "브레이커", "대형 브레이커", "0.18㎥", {"가격": 4559, **BRK},
"손료만(굴착기 부속) · 가격 = 0.2㎥(0230-0002) 값 · 계수 = 0.2㎥ 계수", "브레이커"),
_row(660, g, "에어 호스", "에어 호스", "1.91㎝", {"가격": 105, "손료계수": 5625},
"손료만 · EST 계 59"),
_row(661, g, "모노펌프", "모노펌프", "5-20㎥/hr", {"가격": 4900, "손료계수": 4346},
"손료만 · EST 계 2,129"),
_row(662, c, "파워트로웰", "파워트로웰", "3.73㎾",
{"가격": 2680, "손료계수": 5313, "주연료": 1.0, "주연료종류": "휘발유", "잡재료율": 10, "전력": 3.73},
"주연료 휘발유 · 잡품 · 전력 3.73 ㎾h · 조종원 없음 · EST 계 3,858 · 시간당 경비 = GC001195"),
] # fmt: skip
for i, (spec, usd) in enumerate(PIPE.items()):
out.append(
_row(663 + i, c, "콘크리트펌프용 파이프", "콘크리트펌프용 파이프", spec,
{"가격": round(usd * USD / 1000, 4), "손료계수": 4500},
f"압송관 · 손료만 · ${usd} 표기 · 환율 적용({USD:,} 원/USD · 한국은행 2026-08-14 FX000001) 또는 원화 갱신 필요")
) # fmt: skip
return out
EQ = "EQ000662"
LOGIC_ROW = {
"키": "GC001195",
"원문번호": "8",
"구분": "건설품셈 공통",
"상세구분": "08장 건설기계",
"이름": "파워트로웰 시간당 기계경비",
"결과단위": "원/시간",
"출처": "건설품셈 공통 8-1-5 · 8-1-6",
"부품": {"갈래": "기계경비식"},
"소유": "공용",
"비고": f"품셈에 없는 기계 — {SEED} · 기계표 {EQ} 파워트로웰 3.73㎾ 값을 읽음 · 손료 = 가격 × 손료계수 · 잡품 = 주연료 줄의 % · 전력 = 기계표 전력 칸 · 조종원 없음",
"입력": [
{
"이름": "지역",
"고르기": ["전국평균", "전국", "서울", "경기", "강원", "충북", "충남", "전북", "경북", "경남", "부산", "제주", "대구", "인천", "대전", "울산", "세종", "전남광주"],
"출처": "건설품셈 공통 8-1-7 5",
},
{
"이름": "전력 계약종별",
"고르기": ["주택용 저압", "일반용(갑)Ⅰ 저압"],
"출처": "유가전력 한전 전기요금표 2025-01-01",
"비고": "품셈·계약예규에 계약종별 규정 없음(전력비는 실비 계상) · 임시전력(갑)은 주택용 저압 요금 그대로(요금표 6.가)",
},
],
"중간": [
{"이름": "손료", "식": f"버림({EQ}.가격 * 1000 * {EQ}.손료계수 / 10000000, 0)", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4"},
],
"호표": [
{"종류": "재료", "요소": "MO:휘발유:{지역}", "이름": "주연료", "단위": "ℓ", "수량": f"{EQ}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1"},
{"종류": "재료", "요소": {"이름": "전력량요금", "계약종별": "입력"}, "이름": "전력", "단위": "㎾h", "수량": f"{EQ}.전력", "비목": "재료비", "출처": "자체"},
],
"덧줄": [
{"이름": "잡재료비", "식": f"줄.'주연료' * {EQ}.잡재료율 / 100", "비목": "재료비", "출처": "건설품셈 공통 8-4 주②"},
{"이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2"},
],
"끝수": "손료 원 미만 버림(건설품셈 공통 8-1-5 12) · 합계 원문 규정 없음",
"변수표": {
"지역": {"기호": "S", "아래": "지역"},
"전력 계약종별": {"기호": "S", "아래": "전력계약종별"},
"손료": {"기호": "C", "아래": "손료"},
},
} # fmt: skip
def _block(obj: dict, nl: str) -> str:
"""줄 하나 — 두 칸 들여쓴 JSON 을 넷 칸 자리에."""
text = json.dumps(obj, ensure_ascii=False, indent=2)
return nl.join(" " + line for line in text.split("\n"))
def _insert_tail(path: Path, objs: list[dict]) -> None:
"""「줄」 목록 끝(`]` 앞)에 더함 — 이미 있는 키는 건너뜀."""
text = path.read_text("utf-8")
have = {r["키"] for r in json.loads(text)["줄"]}
new = [o for o in objs if o["키"] not in have]
if not new:
return
nl = "\r\n" if "\r\n" in text else "\n"
close = text.rstrip().rindex("]")
head = text[:close].rstrip()
body = "".join(f",{nl}{_block(o, nl)}" for o in new)
out = head + body + nl + " " + text[close:]
json.loads(out)
path.write_text(out, "utf-8", newline="")
def _insert_after(path: Path, after_key: str, obj: dict) -> None:
"""키 `after_key` 블록 뒤에 줄 하나 — 이미 있으면 건너뜀."""
text = path.read_text("utf-8")
if f'"키": "{obj["키"]}"' in text:
return
nl = "\r\n" if "\r\n" in text else "\n"
data = json.loads(text)
at = text.index(f'"키": "{after_key}"')
nxt = [r["키"] for r in data["줄"]]
follow = nxt[nxt.index(after_key) + 1]
cut = text.rindex("{", 0, text.index(f'"키": "{follow}"'))
cut = text.rindex(",", at, cut) + 1
out = text[:cut] + nl + _block(obj, nl) + "," + text[cut:]
json.loads(out)
path.write_text(out, "utf-8", newline="")
def _book(machines: list[dict]) -> None:
text = BOOK.read_text("utf-8")
nl = "\r\n" if "\r\n" in text else "\n"
adds = [(r["키"], r["원문번호"], "기계.json", "EQ000655") for r in machines]
adds.append(("GC001195", "공통 8 파워트로웰 시간당 경비", LOGIC.name, "GC001194"))
for key, number, file, after in adds:
if f'"{key}":' in text:
continue
line = f' "{key}": {json.dumps({"원문번호": number, "파일": file}, ensure_ascii=False)},'
prev = max((k for k, *_ in adds if k[:2] == key[:2] and f'"{k}":' in text), default=after)
at = text.index(f' "{prev}":')
end = text.index(nl, at) + len(nl)
text = text[:end] + line + nl + text[end:]
book = json.loads(text)
nums = {"EQ": 669, "GC": 1196}
for tid, n in nums.items():
old = book["다음"][tid]
if old < n:
text = text.replace(f'"{tid}": {old},', f'"{tid}": {n},', 1)
json.loads(text)
BOOK.write_text(text, "utf-8", newline="")
def _doc() -> None:
"""GC001195 수식 문서 — 옛 로직 변환(`master_doc_convert`) · 파일 끝에 더함."""
for p in (str(ROOT), str(HERE)):
if p not in sys.path:
sys.path.insert(0, p)
import master_doc_convert as cv
import master_doc_link as ml
from M01_MasterData import M01_MasterData_Store as m01
data = json.loads(DOC.read_text("utf-8"))
if any(d.get("키") == "GC001195" for d in data["줄"]):
return
files, whole = m01.loaded()
doc, notes = cv.convert(whole, LOGIC_ROW)
if notes:
print("변환 알림:", notes)
data["줄"].append(doc)
DOC.write_text(ml.dump(data), "utf-8", newline="")
def main() -> None:
machines = rows()
_insert_tail(MACHINE, machines)
_book(machines)
_insert_after(LOGIC, "GC000276", LOGIC_ROW)
_doc()
import build_기계목록_빠진기계_814_1 as b814
b814.main()
if __name__ == "__main__":
sys.path.insert(0, str(HERE))
main()