- M02_MyMachine — 층 뿌리 내기계.json · 줄 = M01 기계 줄 꼴 · 키 EQP{사용자}-#### · 원문번호 「개인 {키}」 · 바로 · 품셈(계수 짝) · 직접 세 꼴 · 같은 이름 알림 · 쓰는 곳
- 셈 색인 = M01 + 지금 층 내 기계(skc.Copy) — 단가산출 셈 · 저장 · 사본 찍기 · 단가 갱신 · 새값 표지 · 기계 찾기 · 시간당 · 사본 모으기가 읽음 · 층 모르는 길 = M01 만
- 시간당 식 = 채운 칸으로 고른 GC 식(268 · 272 · 276 · 273) · M01 기계 619 대 셈 방식 대조 차이 0
- 길 /api/m02/layers/personal/my-machines(목록 · 하나 · 만들기 · 고치기 · 지우기 · 본뜬)
- 지킴 시험 test_821_1_my_machine
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
152 lines
7.3 KiB
Python
152 lines
7.3 KiB
Python
"""M02 기계(M01 EQ) 한 시간 값 — [항목 추가] 창에서 기계를 고르면 고정 줄 단가(PLAN 736-3).
|
|
|
|
기계 줄 = 일위대가 · 단가산출 더한줄의 고정 줄(이음 기계 EQ · 수량 1) — 단가 = 이 값(노 · 재 · 경).
|
|
셈 = M01 「건설기계」 장 기계경비 로직 가운데 그 기종을 받는 짝 차례 첫째
|
|
(`로직(GC, 기계='원문번호')` · 옵션은 기본값). 짝 차례 · 셈은
|
|
`DataFlow/M02_DF_Values._machine_hourly` 와 같은 규칙(그 폴더는 시험 길이라 따로 둠).
|
|
마스터 파일은 읽기만 · 로그인 사용자 모두.
|
|
기계표 = M01 + 지금 층 내 기계(821-1 · `M02_MyMachine` — 식은 채운 칸으로 고른 같은 GC 식 · 직접 = 그 값) ·
|
|
층을 모르는 길(`/api/m02/logic-lib/machine-hourly`)은 M01 만.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from M01_MasterData import M01_MasterData_Store as store
|
|
from M02_MasterTemplete import M02_MyMachine as mym
|
|
|
|
cm = store.cm
|
|
_PLAIN = {"기계", "지역", "보정작업", "조종원직종"} # 이 밖의 입력 = 부착 장비 전용
|
|
|
|
|
|
def _family() -> list[dict]:
|
|
"""기계 시간당 경비 로직 — 「건설기계」 장 파일 안 · 입력 「기계」 고르기가 있는 돈 로직."""
|
|
return [
|
|
row
|
|
for name, data in store.loaded()[0].items()
|
|
if data.get("그룹") == "로직" and "건설기계" in name
|
|
for row in data.get("줄") or []
|
|
if str(row.get("결과단위") or "").startswith("원")
|
|
and any(i.get("이름") == "기계" and i.get("고르기") for i in row.get("입력") or [])
|
|
]
|
|
|
|
|
|
def _codes(row: dict) -> list:
|
|
return next(i["고르기"] for i in row["입력"] if i.get("이름") == "기계")
|
|
|
|
|
|
def _rank(row: dict) -> tuple:
|
|
"""부착 전용 · 손료만 · 보정 · 기종 수 · 키 — 작을수록 먼저."""
|
|
names = [i.get("이름") for i in row.get("입력") or []]
|
|
attach = any(n not in _PLAIN for n in names)
|
|
loss = "지역" not in names
|
|
return (attach, loss, loss and "보정작업" in names, len(_codes(row)), str(row.get("키")))
|
|
|
|
|
|
def _operated(code: str) -> bool:
|
|
"""기계표(원문 8-4) 조종원 > 0 — 그 원문번호 기계 가운데 하나라도."""
|
|
rows = mym.whole().index.get("EQ", {}).values()
|
|
return any(
|
|
str(r.get("원문번호")) == code and float((r.get("값") or {}).get("조종원") or 0) > 0
|
|
for r in rows
|
|
)
|
|
|
|
|
|
def logic_of(code: str) -> dict | None:
|
|
"""기계 원문번호 → 그 기계 시간당 경비 로직(짝 차례 첫째) · 없으면 None(813-2 M01 화면 ·
|
|
불러오기 `M02_Doc_Import.hourly_link` 도 씀). 기계표 조종원 > 0 = 조종원 호표 있는 식 먼저
|
|
(「조종원 없는 기종」 목록에 겹친 기계 — 진동롤러(핸드가이드식) · 고소작업차 …)."""
|
|
fit = [r for r in _family() if code in [str(c) for c in _codes(r)]]
|
|
if not fit:
|
|
return _own(code) or mym.logic_for(code)
|
|
op = _operated(code)
|
|
|
|
def order(row: dict) -> tuple:
|
|
has = any(x.get("이름") == "조종원" for x in row.get("호표") or [])
|
|
rank = _rank(row)
|
|
return (*rank[:3], op and not has, *rank[3:])
|
|
|
|
return min(fit, key=order)
|
|
|
|
|
|
def _own(code: str) -> dict | None:
|
|
"""그 기계만의 시간당 경비 식(816-1 · 4륜구동 1톤 덤프트럭 = GF000145 · 816-3 체인톱 = GF000337) —
|
|
「기계」 입력 없는 기계경비식이 그 원문번호 기계 줄 값(`EQ….가격` · `EQ….손료계수`)을 읽으면 그 식."""
|
|
rows = mym.whole().index.get("EQ", {}).values()
|
|
keys = [f"{r.get('키')}." for r in rows if code and str(r.get("원문번호")) == code]
|
|
for data in store.loaded()[0].values():
|
|
for row in data.get("줄") or [] if data.get("그룹") == "로직" else []:
|
|
if (row.get("부품") or {}).get("갈래") != "기계경비식" or takes_machine(row):
|
|
continue
|
|
if any(k in str(row.get("중간")) + str(row.get("호표")) for k in keys):
|
|
return row
|
|
return None
|
|
|
|
|
|
def takes_machine(logic: dict) -> bool:
|
|
"""식이 「기계」 입력(원문번호 고르기)을 받는지 — 안 받는 식(GF000145)엔 기계='…' 를 안 넘김."""
|
|
return any(i.get("이름") == "기계" for i in logic.get("입력") or [])
|
|
|
|
|
|
def hourly(key: str) -> dict[str, Any]:
|
|
"""`{ok, 이름, 규격, 로직, 원문번호, 노무비, 재료비, 경비, 계}` · 못 셈 = `{ok: False, reason}`."""
|
|
if not key.startswith("EQ"):
|
|
return {"ok": False, "reason": f"「{key}」 는 기계 아님"}
|
|
try:
|
|
row = (whole := mym.whole()).get(key)
|
|
except cm.mf.FormulaError as e:
|
|
return {"ok": False, "reason": str(e)}
|
|
if mym.mine(key):
|
|
return mym.hourly(row, whole)
|
|
code = str(row.get("원문번호") or "")
|
|
logic = logic_of(code)
|
|
if not logic:
|
|
return {"ok": False, "reason": f"「{row.get('이름')}」 은 시간당 경비를 셀 자료가 없음"}
|
|
args = "".join( # 빈칸 든 입력 이름(「전력 계약종별」 · 820-1 파워트로웰)은 따옴표로
|
|
f", {n if (n := i['이름']).isidentifier() else repr(n)}='{(i.get('고르기') or [''])[0]}'"
|
|
for i in logic.get("입력") or []
|
|
if i.get("이름") != "기계"
|
|
)
|
|
mine = f", 기계='{code}'" if takes_machine(logic) else ""
|
|
call = f"로직({logic['키']}{mine}{args})"
|
|
line = {"종류": "로직", "요소": call, "이름": "기계", "단위": "시간", "수량": "1"}
|
|
holder = {
|
|
"키": "", "원문번호": "", "이름": "기계 시간", "결과단위": "원/식", "출처": "자체",
|
|
"입력": [], "중간": [], "호표": [line], "덧줄": [], "끝수": None,
|
|
} # fmt: skip
|
|
got = store.calc("", {}, holder, None)
|
|
if not got.get("ok") or "sums" not in got:
|
|
return {"ok": False, "reason": got.get("reason") or "셈 못 함"}
|
|
sums = {k: float(got["sums"][k]) for k in (*cm.mf.COST_ITEMS, "계")}
|
|
# 804-3 줄 쌓기 기계 줄 = 이 로직 + 기계='원문번호'
|
|
return {
|
|
"ok": True,
|
|
"이름": row.get("이름"),
|
|
"규격": row.get("규격"),
|
|
"로직": logic["키"],
|
|
"원문번호": code,
|
|
**sums,
|
|
}
|
|
|
|
|
|
def combo(main: str, part: str) -> tuple[str, str] | None:
|
|
"""조합 기계(굴착기 + 대형 브레이커 · 부착용 집게 · 814-1) → 우리 조합 식 (키, 부름 글)
|
|
`로직(GC000271, 기계='…', 브레이커='…', 지역='전국평균', …)` — 두 기계가 그 식 고르기에 있을 때만."""
|
|
try:
|
|
codes = [str(store.loaded()[1].get(k).get("원문번호") or "") for k in (main, part)]
|
|
except cm.mf.FormulaError:
|
|
return None
|
|
for row in sorted(_family(), key=lambda r: str(r.get("키"))):
|
|
ins = row.get("입력") or []
|
|
attach = [i for i in ins if i.get("이름") not in _PLAIN and i.get("고르기")]
|
|
if len(attach) != 1 or codes[0] not in [str(c) for c in _codes(row)]:
|
|
continue
|
|
if codes[1] not in [str(c) for c in attach[0]["고르기"]]:
|
|
continue
|
|
given = {"기계": codes[0], attach[0]["이름"]: codes[1]}
|
|
given |= {i["이름"]: (i.get("고르기") or [""])[0] for i in ins if i["이름"] not in given}
|
|
args = "".join(f", {k}='{v}'" for k, v in given.items())
|
|
return str(row["키"]), f"로직({row['키']}{args})"
|
|
return None
|