diff --git a/resources/master_data/scripts/master_machine_crew.py b/resources/master_data/scripts/master_machine_crew.py new file mode 100644 index 000000000..d1c6d1264 --- /dev/null +++ b/resources/master_data/scripts/master_machine_crew.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +"""기계 한 대 인력 줄 · 잡품 줄 (PLAN 842-3 · 요구 849 · 853 · 867 · 868 · 870) — 기계 줄 값 칸 꼴 · 읽는 법. + +꼴(기계 줄 `값` — M01 `기계/EQ*.json` · 내 기계 `내기계/<키>.json` · 단가산출 사본 EQ 줄): + 인력 = [{키: 'LB:<인력 원문번호>'(빈 글 = 직종 고르기 — 식 입력 직종), 인: 수, 이름?}] 단위 「인/일」 + 잡품 = [{줄: '<식 재료 줄 이름 — 주연료 · 전력>' | 키: '<재료 요소 키>', 이름?, 수량?, 율: %}] 단위 「%」 + 줄마다 금액 = 인력: 노임 × 인 × 시간환산 · 잡품: (줄 = 그 재료 줄 수량 · 단가 | 키 = 수량(기본 1) × 재료 단가) × 율 ÷ 100 + = 단가산출 `기계덮음` 조종원 · 잡품 줄과 같은 꼴(`master_doc_cover`) · 옛 칸(조종원 · 조종원직종 · 잡재료율) 없음. +읽기(`view`) = 식이 읽는 조종원 수(인 합) · 직종(첫 직종) · 잡재료율(첫 줄 율)을 목록에서 셈(저장 안 함) · + 옛 꼴 줄(옛 칸 있음 — 남의 내 기계 · 옛 사본) = 그대로. +목록이 식 한 줄로 안 풀리면(직종 여럿 · 재료 키 줄 · 수량 · 식에 그 줄 없음) 셈이 목록을 덮음으로 얹음 + (`master_doc_cover.row_entry` — 단가산출 `기계덮음` 은 그 위에 부분별로 덮음). +파이썬 · TS 짝 — 짝 = `common_util/common_util_machine_crew.ts` · 거울 시험 `resources/tester/test_842_3_machine_crew.py`. +""" + +from __future__ import annotations + +from numbers import Number + +CREW, MISC = "인력", "잡품" +OLD = ("조종원", "조종원직종", "잡재료율") +BASE = ("주연료", "전력") # 잡품 줄 기본 = 이 재료 줄(값 > 0) + + +def code(key) -> str: + """노임 키 → 인력 원문번호(`LB:1048` → 1048 · 빈 글 = 직종 고르기).""" + t = str(key or "") + return t[3:] if t.startswith("LB:") else t + + +def is_new(vals) -> bool: + """새 꼴(옛 칸 없음) 기계 값.""" + return isinstance(vals, dict) and not any(k in vals for k in OLD) + + +def view(vals): + """식이 읽는 값 — 새 꼴이면 옛 칸 셋을 목록에서 셈해 더한 새 묶음 · 그 밖 = 그대로.""" + if not is_new(vals): + return vals + crew = vals.get(CREW) + misc = vals.get(MISC) + jobs = [code(r.get("키")) for r in crew or [] if isinstance(r, dict) and code(r.get("키"))] + rate = next((r.get("율") for r in misc or [] if isinstance(r, dict) and r.get("줄")), None) + return { + **vals, + "조종원": None if crew is None else sum((r.get("인") or 0) for r in crew), + "조종원직종": jobs[0] if jobs else None, + "잡재료율": None if misc is None else (rate if rate is not None else 0), + } + + +def from_old(vals: dict, base: list[str] | None = None) -> dict: + """옛 꼴 값 → 새 꼴(옛 칸 셋 → 인력 · 잡품) — 옛 칸 없음 = 그대로. + 인력 = 조종원 > 0 이면 한 줄(직종 없음 = 빈 키) · 0 = 빈 목록 · 없음 = 칸 없음. + 잡품 = 잡재료율 > 0 이면 밑 재료 줄(`base` · 없으면 주연료 · 전력 가운데 값 > 0)마다 그 율 · 0 = 빈 목록.""" + if not isinstance(vals, dict) or is_new(vals): + return vals + out = {k: v for k, v in vals.items() if k not in OLD} + n, job, rate = vals.get("조종원"), vals.get("조종원직종"), vals.get("잡재료율") + if _isnum(n): + out[CREW] = [{"키": f"LB:{job}" if job else "", "인": n}] if n > 0 else [] + if _isnum(rate): + if base is None: + base = [b for b in BASE if _pos(vals.get(b))] + out[MISC] = [{"줄": b, "율": rate} for b in base] if rate > 0 and base else [] + return out + + +def _isnum(v) -> bool: + return isinstance(v, Number) and not isinstance(v, bool) + + +def _pos(v) -> bool: + try: + return float(v) > 0 + except (TypeError, ValueError): + return False + + +def summary(vals) -> str: + """목록 요약 글(M01 기계 목록 열 · 비고) — 「1048 1인 · 잡품 주연료 22%」.""" + if not isinstance(vals, dict): + return "" + crew = [f"{code(r.get('키')) or '직종 고르기'} {r.get('인')}인" for r in vals.get(CREW) or []] + misc = [ + f"{r.get('이름') or r.get('줄') or r.get('키')} {r.get('율')}%" + for r in vals.get(MISC) or [] + ] + return " · ".join([*crew, *(["잡품 " + ", ".join(misc)] if misc else [])])