feat(785-5): 만들기 · 길 — 확인한 고른 것 → SC · SK · 장부 · 새 M02_Hopyo_Router · 판정 · 검산 · 옮기기

- 새 M02_Hopyo_Router — 옛 hopyo 길 모두 M02_Labor_Router 에서 옮김(옛 화면 785-7 까지 돎) · 새 길 hopyo-judge 목록 · 호표 · 블록 · hopyo/{키}/check · hopyo/{키}/move · main 등록 · 시동 warm · Fit.ensure 걷음 · 올림 때 엑셀 사전(Row.lexicon) · 블록 풀이 뒤 셈 · 끝내기 때 저장본 지움
- 새 M02_Hopyo_Judge — 1차 줄 판(Row) + 단산 줄 2차 풀이(Clause → Solve) · 엑셀 칸(이름 · 규격 · 단위 · 단가) · 블록 금액 · 절 몫 · 블록 풀이 파생 저장본(HP 폴더 _derived · 입력 지문 · 지문 다를 때만) · judged(folder, key) · 검산(입력값 = 엔진이 쓴 칸 모두 · 지역 · 자재지역 · 조종원직종 비움 · 맡은 절 단위 줄 · 몫과 품 대조 · 금액 = 일위대가 로직 줄과 같은 셈)
- 새 M02_Hopyo_Make — 고른 것 → SC 줄(같은 별칭 · 줄 본뜬 {호표, 차례, 행} · 수량 = 엑셀 × 몫 × 배 · 같은 기초 둘째 = 한 줄 SC 사본 · 별도계상 자재 · 이음 · 율 · 고정 · 참조) · SK 더한줄(바탕 없음 · SkUnit.adds · Sangeun.Maker.write) · 하위 먼저 · 입력 필요 · 단위 환산 못 정함 = 그 줄(블록)만 막힘 · 장부 고른 것 꼴 짝 · 이미 옮긴 것 = 덮음 없으면 409(남의 SC 덮지 않음) · 확인한 줄 몸(Lines) = Build 에서 옮김
- Build 나눔 — 모두 고정 길 · Builder · _order · _origins · build_all 그대로(B09 빌림) · 확인한 줄 몸 → Make.Lines
- SkUnit · Sangeun — SkPatch · Fit · Basis 부름 걷음(기계경비 기초 차례 SkUnit 으로 · 근거 꼬리표 = Clause.basis · 맞춤 셈 409 없앰 · 재료 고름 MO 됨) · Kids 가 _same_mark 가짐
- 시험 새 test_642_5_make(판정 · 검산 · 옮기기 · 예시 1 · 2 · 3 · 덮음 409 · 막는 길) · test_m02_hopyo_excel 마당에 새 Router 더함 · HOPYO_MAX_BYTES 자리(길 옮긴 까닭)
- 낡은 시험 고침: test_718_1 · test_729 「맞춤 셈 전 409」 뺌(맞춤 뒤 셈 안 기다림) · test_628 맞춤셈 캐시 넣기 뺌(근거 = 조항 색인) · test_729 근거 없음 꼴 · 고정 이음 유가 칸(785-3 LINKS 늘어난 까닭)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-04 23:13:53 +09:00
co-authored by Claude Opus 5.5
parent b2c2bb7ad3
commit a22af84b62
14 changed files with 1630 additions and 556 deletions
+35 -6
View File
@@ -8,7 +8,8 @@
단위 SK 더한줄(바탕 없음 · 778-4 꼴) = 인력 `{요소 LB, 수량 품}` · 기계경비 로직 `{로직 GC, 입력값 {기계},
수량 시간 × 할증}` · 재료 `{요소 MT, 수량(손료율 든), 별도계상?}`(못 찾은 별도계상 = 0 원 고정 줄) ·
율 `{고정 {비율 {기준, 비목}}, 수량 = 율 %}`(줄 목록 = 그 행 줄 `add:#k`) · 하위 `{참조 SK · SC}` · 값 고정 줄.
근거 꼬리표 = 맞춤셈 1위 기초(본뜬 칸 · 참고). 서버 단독 · 쓰기 = `M02_Hopyo_Sangeun`.
근거 꼬리표 = 조항 색인(`Sangeun.Maker.basis` · 참고) · 고른 것(785-5 `M02_Hopyo_Make`)도 `adds` 를 부름 ·
기계경비 기초 차례(`_mach_logics`) = 옛 SkPatch 에서 옮김. 서버 단독 · 쓰기 = `M02_Hopyo_Sangeun`.
"""
from __future__ import annotations
@@ -16,7 +17,6 @@ from __future__ import annotations
from typing import Any
from M02_MasterTemplete import M02_Hopyo_Fixed as hfix
from M02_MasterTemplete import M02_Hopyo_SkPatch as skpatch
from M02_MasterTemplete import M02_Hopyo_UnitLine as unit_line
from M02_MasterTemplete import M02_Hopyo_Units as units
from M02_MasterTemplete import M02_Sangeun_Calc as calc
@@ -29,13 +29,41 @@ OUT, DROP = "밖", "뺌"
UNIT = "단위" # 본뜬 갈래
MINE = "내가 고름"
WORD = {"L": "품", "X": "시간", "M": "수량", "R": "율", "S": "수량"}
PICKS = {"L": ("노임", "LB"), "X": ("기계", "EQ"), "M": ("자재", "MT")}
PICKS = {"L": ("노임", "LB"), "X": ("기계", "EQ"), "M": ("자재", ("MT", "MO"))}
MACH_IN = "기계" # 기계경비 기초 기계 입력 칸
MACHINE = {"굴착기": "GC000267"} # 굴착기(무한궤도) 시간당 기계경비 · 그 밖 장비 = GC000268
MACHINE_ANY = "GC000268"
MACHINE_SET = {"GC000267", "GC000268", "GC000269", "GC000275"}
# (주연료, 조종원) 없는 기종 → 그 기계경비 기초(M01 로직이 부르는 것과 같음 · 778-2)
SOLO = {(False, False): "GC000273", (True, False): "GC000272", (False, True): "GC000276"}
class PickError(ValueError):
"""고름 틀림 — 부르는 쪽이 400."""
def _mach_logics(whole: Any, kinds: set, eq: dict | None = None) -> list[str]:
"""기계경비 기초 차례 — 갈래 첫 기초(굴착기 = 무한궤도) › 그 밖 · `eq` 가 있으면
연료 · 조종원 없는 기종 = 그 기초 하나 · 아니면 그 기계(또는 같은 이름 기계)를 고르기에 둔 기초가 앞."""
first = next((c for k, c in MACHINE.items() if k in kinds), MACHINE_ANY)
order = [first, *sorted(MACHINE_SET - {first})]
if not eq:
return order
has = eq.get("값") if isinstance(eq.get("값"), dict) else {}
if solo := SOLO.get((bool(has.get("주연료")), bool(has.get("조종원")))):
return [solo]
eqs = whole.numbers.get("EQ", {})
def takes(code: str) -> bool:
ins = whole.logic(code).get("입력") or []
opts = next((s.get("고르기") or [] for s in ins if s.get("이름") == MACH_IN), [])
same = any((eqs.get(o) or {}).get("이름") == eq.get("이름") for o in opts)
return eq.get("원문번호") in opts or same
hit = [c for c in order if takes(c)]
return hit + [c for c in order if c not in hit]
def _picked(one: dict[str, Any], pick: Any, us: units.Units) -> dict[str, Any]:
"""사용자 고름 → 이어짐 단위 — 노임 LB · 기계 EQ(→ 기계경비 로직) · 자재 MT · 율 기준 · 뺌."""
if not isinstance(pick, dict):
@@ -50,7 +78,8 @@ def _picked(one: dict[str, Any], pick: Any, us: units.Units) -> dict[str, Any]:
field, head = PICKS.get(kind, ("", "?"))
code = str(pick.get(field) or "")
if not field or not code.startswith(head):
raise PickError(f"{units.HOW.get(kind, kind)} 줄 · {field or '뺌'} 키({head}…)로 고름")
shown = head if isinstance(head, str) else head[0]
raise PickError(f"{units.HOW.get(kind, kind)} 줄 · {field or '뺌'} 키({shown}…)로 고름")
try:
row = us.whole.get(code)
except Exception as e: # noqa: BLE001 — 없는 키
@@ -58,7 +87,7 @@ def _picked(one: dict[str, Any], pick: Any, us: units.Units) -> dict[str, Any]:
unit: dict[str, Any] = {field: code}
if kind == "X":
kinds = mtypes(f"{row.get('상세구분')} {row.get('이름')}")
unit["로직"] = skpatch._mach_logics(us.whole, kinds, row)[0]
unit["로직"] = _mach_logics(us.whole, kinds, row)[0]
unit |= {"할증": one["할증"]} if one.get("할증") else {}
if kind == "M" and (one.get("어떻게") == "별도" or pick.get("별도계상")):
unit["별도계상"] = True
@@ -110,7 +139,7 @@ def adds(block: dict, rows: list[dict], us: units.Units, ref_of: Any) -> tuple[l
add = {"종류": "인력", "요소": unit["노임"], "이름": name, "단위": "인", "수량": q}
elif kind == "X":
eq = us.whole.get(unit["기계"]) or {}
given = {skpatch.MACH_IN: eq.get("원문번호")}
given = {MACH_IN: eq.get("원문번호")}
q *= float(unit.get("할증") or 1)
add = {
"종류": "로직",