feat(719-2): 공사 쪽 M02 단가산출(SK) — B09 대가 블록 · 산근 · [업데이트] · 내 자료집 SK
- Labor_Source: 굳힌 SK = 품셈 꼴 대가 블록(사본 단가.json 굳힌 키 줄 · M01 안 읽음) · 단가산출 속 일위대가 · SK 줄 = 그 대가 블록 부르는 줄 · 고정 줄 = fixed · 단가 고친 줄 = fixed(M02 셈 단가) · cover = P:+SK 이름표(SK 고쳐도 B09 덮개 그대로) · 줄 sk · poom(품셈 값) · 부르는 줄 수량 ÷ 굳힌 줄 배 · 사전조합 알약 = 굳힌 알약 · 서고 sangeun 사본 - Sangeun: SK 하위 로직 = 산근 · 단가산출 속 참조 걸음 · 결과단위 배 = 사본 줄에서(_unit(row) · unit_of(whole, code)) - Linked: 굳힌 SK 자원 키(굳힌 키 로직 줄 · 고정 이음) - Refresh: 미리 보기 단가산출(바뀌는 SK 줄) · 서고 사본 원본판 다르면 다시 굳힘 - Bill_Library: 종류 sangeun(내 자료집 SK 불러오기) · changed · refresh_copy(같은 폴더 · 값 그대로 · 원본판 없는 옛 사본 그대로) - Daega_Personal: 품셈 블록(굳힌 SK 포함) → 개인 층 SK(B09 품 · 단가 덮개 = SK 덮개 · 더한줄 수량 · 단가) - M02_Item_Sangeun: 굳힌 SK 정체(굳힌 줄 차례 → 원본 cid) - 화면: 일위대가 펼침 SK 줄 되돌림 [단가산출 값으로] · 곁 글 품셈 값 · 내 자료집 단가산출 글 · 업데이트 미리 보기 SK 줄 · 단가연결 내 것 단가산출(Labor_Api fetchLibrary 종류 넓힘) - 시험 test_719_2_sk_project 5 통과 + 1 건너뜀(715-1b 엔진 단가 · 값묶음 뒤 자동 실행) - test_155_5: _unit 인자 = 로직 줄로 바뀜(사본 줄에서 배 읽음) — 부르는 줄 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,14 @@ class Builder:
|
||||
|
||||
# ── 품셈(로직 하나) ─────────────────────────────────────────────────
|
||||
def poomsem(self, whole: Any, one: dict[str, Any], title: str = "") -> str | None:
|
||||
got = self._poomsem(whole, one, title)
|
||||
if got is None:
|
||||
return None
|
||||
got.pop("_engine"), got.pop("_items")
|
||||
return self.put(got)
|
||||
|
||||
def _poomsem(self, whole: Any, one: dict[str, Any], title: str = "") -> dict | None:
|
||||
"""품셈 블록 꼴(넣기 전) — 엔진 줄 · 줄마다 `_n`(엔진 차례 · 단가산출 블록이 씀)."""
|
||||
code = str(one.get("로직") or "")
|
||||
given, rate, picks = one.get("입력값") or {}, one.get("켜쌓기율"), one.get("고름")
|
||||
try:
|
||||
@@ -142,18 +150,73 @@ class Builder:
|
||||
f"{k} {st._word(whole, k, v) if isinstance(v, str) else v}"
|
||||
for k, v in sorted(given.items())
|
||||
)
|
||||
return self.put(
|
||||
{
|
||||
"kind": "품셈",
|
||||
"name": title or logic.get("이름") or code,
|
||||
"spec": shown,
|
||||
"spec_raw": spec,
|
||||
"unit": unit,
|
||||
"cite": logic.get("출처") or "",
|
||||
"origin": {"로직": code, "입력값": given, "켜쌓기율": rate, "고름": picks},
|
||||
"lines": lines,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"kind": "품셈",
|
||||
"name": title or logic.get("이름") or code,
|
||||
"spec": shown,
|
||||
"spec_raw": spec,
|
||||
"unit": unit,
|
||||
"cite": logic.get("출처") or "",
|
||||
"origin": {"로직": code, "입력값": given, "켜쌓기율": rate, "고름": picks},
|
||||
"lines": lines,
|
||||
"_engine": engine,
|
||||
"_items": items,
|
||||
}
|
||||
|
||||
# ── 단가산출(SK · 719-2) ────────────────────────────────────────────
|
||||
def sangeun(self, whole: Any, ref: dict, doc: dict, copies: dict, depth: int = 0) -> str | None:
|
||||
"""굳힌 SK(사본 `단가.json` 로직 줄 · 719-1) = 품셈 블록 꼴 대가 — 셈 = 굳힌 줄 그대로 ·
|
||||
단가산출 속 일위대가 · SK 줄(참조) = 그 대가 블록을 부르는 줄(펼침 · B09 차례 그대로) ·
|
||||
고정 줄 = `fixed` · 단가 고친 줄(덮개 · 더한줄 단가 · 값묶음) = `fixed`(줄 단가 = M02 셈) ·
|
||||
cover = `P:` + SK 이름표(SK 를 고쳐 [업데이트] 해도 B09 덮개 그대로) ·
|
||||
줄 `sk` · `poom`(M02 에서 고친 줄의 품셈 값 = 원본 덮개 `원값`)."""
|
||||
from M02_MasterTemplete import M02_Item_Sangeun as item_sk
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
from M02_MasterTemplete.M02_Labor_Expand import Expand
|
||||
|
||||
rid = refs.ref_id(ref)
|
||||
key = str((doc.get("바탕") or {}).get("로직") or "")
|
||||
if item_sk.ROW not in doc or not key:
|
||||
return None # 못 푼 SK(없는 바탕 · 순환) — 부르는 줄이 까닭
|
||||
|
||||
def read(r: dict) -> dict | None:
|
||||
return copies.get(refs.ref_id(r))
|
||||
|
||||
try:
|
||||
got = calc.resolve(doc, whole, read, (rid,))
|
||||
row = {**got["줄"], "키": key, "호표": [dict(x) for x in got["줄"]["호표"]]}
|
||||
if calc._bundles(row, whole, Expand(whole, read), (rid,)):
|
||||
return None
|
||||
except (sd.DocError, mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError):
|
||||
return None
|
||||
one = {"로직": key, "입력값": got["입력값"], "켜쌓기율": got["켜쌓기율"]}
|
||||
block = self._poomsem(calc._Over(whole, key, row), {**one, "고름": got["고름"]})
|
||||
if block is None:
|
||||
return None
|
||||
ids = list(doc.get("정체") or [])
|
||||
was = {c: v.get("원값") for c, v in ((doc.get("원본") or {}).get("덮개") or {}).items()}
|
||||
lines = []
|
||||
for n, (line, x) in enumerate(zip(block["lines"], block.pop("_engine"))):
|
||||
item = block["_items"][n] if n < len(block["_items"]) else {}
|
||||
qty = _f(x.get("수량"))
|
||||
head = {"name": line.get("name") or "", "spec": "", "unit": line.get("unit") or ""}
|
||||
if isinstance(item.get("참조"), dict) and depth < refs.DEPTH:
|
||||
line = {**self.line_of(whole, item, qty, 1, head["name"], copies, depth), **head}
|
||||
elif isinstance(item.get("고정"), dict):
|
||||
line = {**fixed_line(item["고정"], qty), **head}
|
||||
elif qty and ("단가" in item or item.get("값묶음")):
|
||||
own = {PART[k]: _f(v) / qty for k, v in (x.get("비목") or {}).items() if k in PART}
|
||||
line = {**head, "kind": "fixed", "qty": qty, "own": own, "link": {}}
|
||||
poom = was.get(ids[n]) if n < len(ids) else None
|
||||
lines.append({**line, "sk": True, **({"poom": poom} if poom else {})})
|
||||
block.pop("_items")
|
||||
name = doc.get("이름") or block["name"]
|
||||
origin = {**block["origin"], "단가산출": rid}
|
||||
cover = f"P:{_digest({'단가산출': rid})}"
|
||||
return self.put({**block, "name": name,
|
||||
"cite": f"단가산출 {ref.get('키')}", "origin": origin,
|
||||
"lines": lines}, cover) # fmt: skip
|
||||
|
||||
def _engine_line(
|
||||
self, item: dict | None, x: dict, logic: dict, extra: int, bases: dict, call: dict | None
|
||||
@@ -209,6 +272,17 @@ class Builder:
|
||||
"qty": each,
|
||||
"why": f"없는 참조 「{ref.get('키')}」",
|
||||
}
|
||||
if refs.kind_of(ref.get("키")) == "sangeun": # 단가산출(719-2) = 굳힌 줄 대가
|
||||
bid = self.sangeun(whole, ref, doc, copies, depth + 1)
|
||||
if bid is None:
|
||||
why = "단가산출을 못 셈(바탕 없음 · 순환)"
|
||||
return {"kind": "blank", "name": name, "qty": each, "why": why}
|
||||
from M02_MasterTemplete.M02_Sangeun_Calc import times_of
|
||||
|
||||
block = self.blocks[bid] # 블록 = 굳힌 줄 결과단위(배) 몫 · M02 참조 줄 = 1 단위
|
||||
per = float(times_of(whole.logic(doc["바탕"]["로직"])))
|
||||
return {"kind": "daega", "key": bid, "name": block["name"], "unit": block["unit"],
|
||||
"qty": None if each is None else each / per} # fmt: skip
|
||||
if refs.kind_of(ref.get("키")) == "promote": # 옛 단가승격 참조 = 일위대가화 = 대가
|
||||
first = (doc.get("담은로직") or [{}])[0]
|
||||
bid = self.poomsem(whole, first, doc.get("이름") or name)
|
||||
@@ -225,7 +299,7 @@ class Builder:
|
||||
return {"kind": "blank", "name": name or code, "qty": q, "why": "돈 아닌 로직"}
|
||||
key = sangeun_ref(code, one.get("입력값"), one.get("켜쌓기율"), one.get("고름"))
|
||||
return {"kind": "sangeun", "key": key, "name": name or code,
|
||||
"unit": sg.unit_of(code), "qty": q} # fmt: skip
|
||||
"unit": sg.unit_of(whole, code), "qty": q} # fmt: skip
|
||||
bid = self.poomsem(whole, one, str(one.get("별칭") or "") or name)
|
||||
if bid is None:
|
||||
return {
|
||||
@@ -243,9 +317,11 @@ class Builder:
|
||||
from M01_MasterData import M01_MasterData_Store_Combo as combo_store
|
||||
|
||||
inner = [one for one in doc.get("담은로직") or [] if isinstance(one, dict)]
|
||||
kept = doc.get("알약") # 굳힌 알약(142-2 · 지금 M01 안 읽음 · 719-2) · 없으면 셈
|
||||
pills = {
|
||||
code: pill
|
||||
for pill in combo_store.combo_pills({"담은로직": inner})["물량"]
|
||||
for pill in (kept if isinstance(kept, list) else None)
|
||||
or combo_store.combo_pills({"담은로직": inner})["물량"]
|
||||
for code in pill["로직"]
|
||||
}
|
||||
lines = []
|
||||
@@ -318,7 +394,7 @@ class Builder:
|
||||
|
||||
def library(self, folder: Path, manifest: dict[str, Any]) -> str | None:
|
||||
"""설계내역서 서고 사본(204-2) — 구조물조합 = 조합 블록 · 사전조합 = 겹 대가 블록 그대로."""
|
||||
if manifest.get("종류") != "precombo":
|
||||
if manifest.get("종류") not in ("precombo", "sangeun"):
|
||||
return self.combo(folder, f"L:{folder.name}")
|
||||
labor = read_json(folder / LABOR_FILE)
|
||||
ref = (manifest.get("원본") or {}).get("참조")
|
||||
@@ -327,6 +403,8 @@ class Builder:
|
||||
if not isinstance(doc, dict):
|
||||
return None
|
||||
whole = item_prices.load_prices(read_json(folder / PRICE_FILE) or {})
|
||||
if manifest["종류"] == "sangeun": # 내 자료집 단가산출(719-2)
|
||||
return self.sangeun(whole, ref, doc, copies, 1)
|
||||
return self.precombo(whole, ref, doc, copies, 1)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user