Files
Aislo/M02_MasterTemplete/M02_Hopyo_SkLink.py
T
eomsangdonandClaude Opus 5.5 eaa8064f1a feat(777-1): 호표가 다른 호표를 부를 때 = 일위대가 둘 — 하위 함께 만들기 · 같은 SC 재사용 · 결과 글 · 납작 길 막음
- 부모 저장 때 확인 안 된 하위 호표(깊이 끝까지)를 하위 먼저 그 체크대로 함께 만듦(409 「먼저 확인」 걷음)
  · 서버 = 체크 제안 몸(M02_Hopyo_Kids.proposal ↔ 화면 bodyFor 짝) · 화면 = 확인 창 캐시 고름(Hopyo_Kids.ts)
  · 하위 실패 = 409 「하위 제N호표 실패 — …」 · 하위 · 하위남은줄 → 결과 표 [고르기]가 그 하위를 엶
  · [모두 고정] 연쇄 실패 까닭 따로 · 미리 보기는 하위 호표 줄을 이음으로 셈
- 같은 엑셀 다시 불러오기 = 같은 이름 · 같은 내용(수량까지) SC 재사용 — 본뜬.또(SK 꼴) · 끝내기는 그 몫만 뺌
- 결과 표 글 「SC-12 · 하위 제4호표 SC-11(사본 담음)」 · 확인 창 호표 줄 글 「하위 일위대가 SC-11 사본」
- [구조물 조합으로] 도 하위 먼저(못 만들면 지금처럼 고정 줄) · 붙여넣기 · B09 통째 = 이미 하위 먼저(안 고침)
- 엑셀 대조(CUTS · _excel_round · excel_given · compare) → 새 M02_Hopyo_Compare.py(Build 608 → 540)
- M02_Labor_Router 병행 흔적 가드 걷음(_later · inspect · 몸 칸 새로)
- STmate [엑셀 보기] 단가산출근거로 감(형식별 블록 시트 이름)
- 낡은 시험 고침: 「제46호표 먼저 확인」 409 · 글 → 하위 함께(777-1 정함) · 718-1 실패 = 남은줄 또는 하위 ·
  화면 시험 StaleError 안내 → unmadeKids · leftAt · 새 지킴 시험 test_777_1_hopyo_kids

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 09:24:48 +09:00

132 lines
6.1 KiB
Python

"""M02 일위대가 만들기 — 단가산출(SK) 이음 · 남은 줄(PLAN 729-2 · 요구서 내역19 1장 8).
`M02_Hopyo_Build.Builder` 가 부름(`b` = 그 만들기) —
확인 안 한 단가산출 줄 = 그 블록 SK 참조(이 층 · 같은 원본 · 없으면 `M02_Hopyo_Sangeun.Maker` 로 그 블록만
만듦 · 미리 보기 = 키만 · 맞춤 셈 전 · 고름 대기 · 단위 다름 = 못 이음) ·
확인한 로직 줄이 차이 SK 의 바탕 = 그 SK 참조(엑셀 차이 보존 · 별칭 짝 = 기초까지만 · 못옮김 남으면 남은 줄 · 729-5) ·
남은 줄 = 엑셀 줄 · 갈래 · 까닭 · 후보(단가산출 고름 대기) · 못옮김 남은 SK = 못 이음(`단산` 번 · `못옮김` ·
잇기 = [단가산출 만들기] `바탕[번].잇기` · 729-4).
"""
from __future__ import annotations
import json
from decimal import Decimal
from typing import Any
from M01_MasterData import M01_MasterData_Store as m01
from M02_MasterTemplete import M02_Hopyo_Elem as elem
from M02_MasterTemplete import M02_Hopyo_Finger as finger
from M02_MasterTemplete import M02_Hopyo_Sangeun as sangeun
from M02_MasterTemplete import M02_Labor_Doc as labor
from M02_MasterTemplete import M02_Labor_Ref as refs
mm_unit = m01.cm.mf.mp.mm.plain_unit
LEFT = "안 이은 줄이 남음 — 줄마다 이을 것을 고름"
WHY = {
"단가산출": "맞는 기초 없음 — 고름 대기",
"자재": "자재 후보 여럿 · 없음",
"별도계상": "자재 후보 여럿 · 없음",
"노임": "같은 이름 노임 없음",
"기계": "같은 기계(EQ) 없음 · 여럿",
"호표": "하위 호표 단위 다름 · 같은 하위 둘째",
"%": "밑수가 다른 줄 금액 합 아님",
}
def _num(value: Decimal) -> int | float:
value = value.normalize()
return int(value) if value == value.to_integral_value() else float(value)
def sks(b: Any) -> dict[str, str]:
"""이 층 그 HP 의 SK `{본뜬 키: SK 키}`(한 번 읽어 둠)."""
if b._sk is None:
b._sk = sangeun.made(b.folder, b.key, b.source)
return b._sk
def sk_doc(b: Any, key: str) -> dict[str, Any]:
try:
got = json.loads((b.folder / "sangeun" / f"{key}.json").read_bytes())
except (OSError, ValueError):
return {}
return got if isinstance(got, dict) else {}
def _ref(key: str, line: dict, alias: Any, memo: str, qty: Decimal) -> dict[str, Any]:
head = {"층": refs.here()[0], "주인": refs.here()[1], "키": key}
return {"참조": head, "별칭": alias, "메모": memo, "수량": {"손": _num(qty)}}
def sk_line(b: Any, line: dict[str, Any], create: bool) -> tuple[dict | None, dict | None]:
"""단가산출 줄 → (그 블록 SK 참조 줄, 못 이은 까닭 {까닭, 후보?}) — `create` = 없으면 만듦."""
ref = line.get("참조") or {}
if ref.get("종류") != "단산":
return None, None
no = int(ref.get("번") or 0)
key = sks(b).get(sangeun.mark(b.key, no))
lost = None
if key is None and create:
if b._maker is None:
b._maker = sangeun.Maker(b.src, b.key, b.who)
if no not in b._maker.first:
return None, None
if not b._maker.done:
return None, {"까닭": "맞춤 셈 전 — 단가산출 못 만듦"}
plan = b._maker.plan(no)
if plan["갈래"] == sangeun.skpatch.WAIT:
return None, {"까닭": plan["까닭"], "후보": plan["후보"]}
lost = plan["못옮김"]
key = f"SK-미리{no}" if b.dry else b._maker.write(no, plan, None)
sks(b)[sangeun.mark(b.key, no)] = key
if key is None:
return None, None
doc = sk_doc(b, key)
if lost is None: # 출처마다(733-1 · 같은 내용 SK 를 여러 블록이 씀)
lost = sangeun.origin_of(doc, sangeun.mark(b.key, no), b.source).get("못옮김")
if lost: # 연결 안 되면 못 만듦(내역 20-4)
return None, _lost(no, key, lost)
unit = doc.get("단위") or line.get("단위")
if mm_unit(unit) != mm_unit(line.get("단위")):
return None, {"까닭": f"단가산출 단위 「{unit}」 다름"}
qty = Decimal(str(line.get("수량") or 0))
alias = str(line.get("이름") or "")[: labor.ALIAS_MAX].strip() or None
return _ref(key, line, alias, f"실무 {qty}{line.get('단위') or ''} · 단산{no}", qty), None
def _lost(no: int, key: str, lost: list) -> dict[str, Any]:
why = f"단가산출 못옮김 {len(lost)} — [단가산출 만들기]에서 이음"
return {"까닭": why, "단산": no, "SK": key, "못옮김": lost}
def kept(
b: Any, line: dict, one: dict, qty: Decimal, alias: Any, memo: str
) -> tuple[dict | None, dict | None]:
"""확인한 로직 줄이 그 블록 차이 SK 의 바탕 = (그 SK 참조, 못옮김 남으면 까닭) · 아니면 (None, None)."""
ref = line.get("참조") or {}
if ref.get("종류") != "단산" or one.get("몫") is not None:
return None, None
no = int(ref.get("번") or 0)
key = sks(b).get(sangeun.mark(b.key, no))
doc = sk_doc(b, key) if key else {}
base = (doc.get("바탕") or {}).get("로직")
mine = sangeun.origin_of(doc, sangeun.mark(b.key, no), b.source) if doc else {}
if mine.get("갈래") != sangeun.skpatch.SK or base != one["로직"]:
return None, None
lost = mine.get("못옮김") # sk_line 과 같은 규칙 — 차이 잃지 않음(내역 20-4)
return _ref(key, line, alias, f"{memo} · 차이 SK", qty), _lost(no, key, lost) if lost else None
def left(line: dict[str, Any], why: dict | None) -> dict[str, Any]:
"""남은 줄 한 줄 — 엑셀 줄 · 갈래 · 까닭 · 후보(기계 = M01 EQ 셋 · 746-1)."""
kind = finger.kind_of(line)
keep = ("차례", "이름", "규격", "단위", "수량")
out = {**{k: line.get(k) for k in keep}, "갈래": kind}
out["까닭"] = (why or {}).get("까닭") or WHY.get(kind, "이을 것 없음")
more = {k: v for k, v in (why or {}).items() if k not in ("까닭", "후보")}
cands = (why or {}).get("후보") or []
if not cands and kind == "기계":
cands = elem.machines(f"{line.get('이름') or ''} {line.get('규격') or ''}")
return {**out, "후보": cands, **more}