feat(b09): 호표 갈래 뒤집기 — 일위대가화 줄 = 대가 · 단가산출 줄 = 산근(155-3 · 155-4)
- 일위대가목록 = 구조물 조합 + 사전조합 + 일위대가화(승격 · 옛 SP) 줄 · 단가산출 줄 = 산근 줄 - 산근 줄 수량 = 수량 그대로(배 ≠ 1 로직 금액 1/배 흠) · 대가 줄만 ÷ 배 - 품셈 블록 로직 줄 판정 = 산근 풀이와 같은 call_of(기계경비 = 중기 · 그 밖 = 하위 산근) - 비고 「산근 n호표」(Calc_Labor 파이썬 · TS 짝) · 산근 줄 누름 = 단가산출근거 탭 - 산근목록 원천 = 단가산출 줄 + 사전조합 안 줄 + 일위대가화 줄 하위 로직(노무 · 자재 · 중기 줄 제외) - 산근 자재 자리표시 키(MO:경유:…) = 자재단가대비표 키로 - 시험: 145-1 다섯 · 145-2 셋 새 갈래로(머리에 근거 한 줄) · 새 test_155_b09_sources Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
@@ -5,11 +5,14 @@
|
||||
|
||||
조합 구조물 조합 사본 하나 = 블록(엑셀 「조합」 꼴) · 줄 = 담은 로직 줄 · 자재 줄
|
||||
사전조합 참조 SC = 겹 대가(굳힌 `참조사본`) · 줄 = 그 안 로직 · 참조 줄(수량 = 손)
|
||||
품셈 승격 아닌 로직 줄 = 로직마다 따로(노무 · 재료 · 중기 · % 줄) — 조합이 수량만큼 부름
|
||||
산근 승격 줄 · 옛 참조 SP = 산근 호표 참조(키 = 145-1 `sangeun_key` · 없으면 자리만)
|
||||
품셈 일위대가화(승격) 줄 · 옛 참조 SP = 로직마다 따로(노무 · 재료 · 중기 · 산근 · % 줄)
|
||||
— 조합이 수량만큼 부름 · 기계 = 기계경비 로직 줄 · 다른 돈 로직 줄 = 하위 산근
|
||||
산근 단가산출 줄(일위대가화 아닌 로직 줄) = 산근 호표 참조(키 = 145-1 `sangeun_key`)
|
||||
(155-3 · 사용자 뜻 = 일위대가화 줄 = 대가 · 단가산출 줄 = 산근 · 145 정함 뒤집음)
|
||||
|
||||
같은 블록 = 같은 꼴 → 한 호표(id = 내용 해시 · 같은 원본 + 같은 셈 단가 = 하나) ·
|
||||
줄 수량 = 사본 셈 수량(손 · 면적 알약 칸) ÷ 배 = 부르는 호표 1 단위 몫.
|
||||
줄 수량 = 사본 셈 수량(손 · 면적 알약 칸) — 대가 줄만 ÷ 배(부르는 호표 1 단위 몫 ·
|
||||
산근 단가는 이미 1 단위).
|
||||
단가 · 금액 · 끝수는 셈 짝(`B09_Estimation_Calc_Labor`)이 B09 프로젝트 한 벌 단가로 냄.
|
||||
"""
|
||||
|
||||
@@ -22,6 +25,7 @@ from decimal import Decimal
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sg
|
||||
from M02_MasterTemplete import M02_Item_Prices as item_prices
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor_doc
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
@@ -38,8 +42,6 @@ mf = item_prices.mf
|
||||
|
||||
#: 엔진 비목 → 블록 비목
|
||||
PART = {"노무비": "labor", "재료비": "material", "경비": "expense"}
|
||||
#: 기계 로직 줄 요소 — `로직(GC…, 기계='0201-0070', …)` 의 원문번호
|
||||
MACHINE = re.compile(r"기계\s*=\s*['\"]([^'\"]+)['\"]")
|
||||
#: % 덧줄 밑수 — `노무비 * …` · `줄.'이름' * …` · `(석공 * LB… + …) * …`(인력 줄)
|
||||
BASE_PART = re.compile(r"^\s*(노무비|재료비|경비)\s*\*")
|
||||
BASE_LINE = re.compile(r"^\s*줄\.'([^']+)'\s*\*")
|
||||
@@ -62,12 +64,8 @@ def _digest(value: Any) -> str:
|
||||
|
||||
|
||||
def sangeun_ref(code: str, given: Any, rate: Any, picks: Any) -> str:
|
||||
"""산근 호표 키 — 145-1(`B09_Estimation_Sangeun.sangeun_key`)이 들어오면 그 키 · 전엔 자리만."""
|
||||
try:
|
||||
from B09_Estimation.B09_Estimation_Sangeun import sangeun_key
|
||||
except ImportError:
|
||||
return f"SG:{code}:{_digest([given or {}, rate, picks or {}])}"
|
||||
return sangeun_key(code, given or {}, rate, picks or {})
|
||||
"""산근 호표 키 — 145-1 `B09_Estimation_Sangeun.sangeun_key`."""
|
||||
return sg.sangeun_key(code, given or {}, rate, picks or {})
|
||||
|
||||
|
||||
class Builder:
|
||||
@@ -84,7 +82,7 @@ class Builder:
|
||||
return bid
|
||||
|
||||
# ── 품셈(로직 하나) ─────────────────────────────────────────────────
|
||||
def poomsem(self, whole: Any, one: dict[str, Any]) -> str | None:
|
||||
def poomsem(self, whole: Any, one: dict[str, Any], title: str = "") -> str | None:
|
||||
code = str(one.get("로직") or "")
|
||||
given, rate, picks = one.get("입력값") or {}, one.get("켜쌓기율"), one.get("고름")
|
||||
try:
|
||||
@@ -98,15 +96,20 @@ class Builder:
|
||||
lines = []
|
||||
engine = got.get("줄") or []
|
||||
bases = _engine_bases(engine[: len(items)], items)
|
||||
calls = sg.line_calls(whole, code, given, picks)
|
||||
for n, x in enumerate(engine):
|
||||
item = items[n] if n < len(items) else None
|
||||
lines.append(self._engine_line(whole, item, x, logic, n - len(items), bases))
|
||||
lines.append(self._engine_line(item, x, logic, n - len(items), bases, calls.get(n)))
|
||||
eq = {str(r.get("키")): r for r in (whole.numbers.get("EQ") or {}).values()}
|
||||
for line in lines:
|
||||
if line["kind"] == "machine":
|
||||
line["spec"] = (eq.get(line["key"]) or {}).get("규격") or ""
|
||||
unit = str(logic.get("결과단위") or "").replace("원/", "")
|
||||
spec = " · ".join(f"{k} {v}" for k, v in sorted(given.items()))
|
||||
return self.put(
|
||||
{
|
||||
"kind": "품셈",
|
||||
"name": logic.get("이름") or code,
|
||||
"name": title or logic.get("이름") or code,
|
||||
"spec": spec,
|
||||
"unit": unit,
|
||||
"cite": logic.get("출처") or "",
|
||||
@@ -116,7 +119,7 @@ class Builder:
|
||||
)
|
||||
|
||||
def _engine_line(
|
||||
self, whole: Any, item: dict | None, x: dict, logic: dict, extra: int, bases: dict
|
||||
self, item: dict | None, x: dict, logic: dict, extra: int, bases: dict, call: dict | None
|
||||
) -> dict[str, Any]:
|
||||
head = {"name": x.get("이름") or "", "spec": "", "unit": x.get("단위") or ""}
|
||||
qty = _f(x.get("수량"))
|
||||
@@ -140,24 +143,20 @@ class Builder:
|
||||
return {**head, "kind": "blank", "qty": qty, "why": x.get("까닭") or "품목 없음"}
|
||||
return {**head, "kind": "material", "key": goods["키"], "spec": goods.get("규격") or "",
|
||||
"qty": qty} # fmt: skip
|
||||
hit = MACHINE.search(str(item.get("요소") or ""))
|
||||
row = whole.numbers.get("EQ", {}).get(hit.group(1)) if hit else None
|
||||
if row is None:
|
||||
# ponytail: 기계 아닌 하위 로직 줄은 B09 단가로 못 펼침 — 줄만 두고 빈칸
|
||||
return {
|
||||
**head,
|
||||
"kind": "blank",
|
||||
"qty": qty,
|
||||
"why": "하위 로직 줄 — 대가 단가로 못 펼침",
|
||||
}
|
||||
return {**head, "kind": "machine", "key": row["키"], "spec": row.get("규격") or "",
|
||||
"qty": qty} # fmt: skip
|
||||
# 로직 줄 — `Sangeun.call_of` 판정: 기계경비 로직 = 중기 · 그 밖 = 하위 산근
|
||||
if call and call.get("machine"):
|
||||
return {**head, "kind": "machine", "key": call["machine"], "qty": qty}
|
||||
if call and call.get("code"):
|
||||
key = sg.sangeun_key(call["code"], call["given"])
|
||||
return {**head, "kind": "sangeun", "key": key, "qty": qty}
|
||||
return {**head, "kind": "blank", "qty": qty, "why": "하위 로직 줄 — 못 펼침"}
|
||||
|
||||
# ── 조합 · 사전조합 줄 ──────────────────────────────────────────────
|
||||
def line_of(
|
||||
self, whole: Any, one: dict, qty: Any, times: Any, name: str, copies: dict, depth: int = 0
|
||||
) -> dict[str, Any]:
|
||||
"""담은 로직 줄 하나 → 블록 줄 — 수량 = 수량 ÷ 배(부르는 호표 1 단위 몫)."""
|
||||
"""담은 로직 줄 하나 → 블록 줄 — 대가 줄 수량 = 수량 ÷ 배(부르는 호표 1 단위 몫) ·
|
||||
산근 줄 수량 = 수량 그대로(산근 단가 = 결과단위 1 단위)."""
|
||||
q = _f(qty)
|
||||
t = _f(times) or 1.0
|
||||
each = None if q is None else q / t
|
||||
@@ -171,20 +170,24 @@ class Builder:
|
||||
"qty": each,
|
||||
"why": f"없는 참조 「{ref.get('키')}」",
|
||||
}
|
||||
if refs.kind_of(ref.get("키")) == "promote": # 옛 단가승격 참조 = 산근
|
||||
if refs.kind_of(ref.get("키")) == "promote": # 옛 단가승격 참조 = 일위대가화 = 대가
|
||||
first = (doc.get("담은로직") or [{}])[0]
|
||||
key = sangeun_ref(str(first.get("로직") or ""), first.get("입력값"),
|
||||
first.get("켜쌓기율"), first.get("고름")) # fmt: skip
|
||||
return {"kind": "sangeun", "key": key, "name": doc.get("이름") or name,
|
||||
bid = self.poomsem(whole, first, doc.get("이름") or name)
|
||||
if bid is None:
|
||||
return {"kind": "blank", "name": name, "qty": each, "why": "돈 아닌 로직"}
|
||||
return {"kind": "daega", "key": bid, "name": self.blocks[bid]["name"],
|
||||
"unit": doc.get("단위") or "", "qty": each} # fmt: skip
|
||||
bid = self.precombo(whole, ref, doc, copies, depth + 1)
|
||||
return {"kind": "daega", "key": bid, "name": doc.get("이름") or name,
|
||||
"unit": doc.get("단위") or "", "qty": each} # fmt: skip
|
||||
code = str(one.get("로직") or "")
|
||||
if one.get("승격") is True:
|
||||
if one.get("승격") is not True: # 단가산출 줄 = 산근
|
||||
if not sg.is_money(whole, code):
|
||||
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, "qty": each}
|
||||
bid = self.poomsem(whole, one)
|
||||
return {"kind": "sangeun", "key": key, "name": name or code,
|
||||
"unit": sg.unit_of(code), "qty": q} # fmt: skip
|
||||
bid = self.poomsem(whole, one, str(one.get("별칭") or "") or name)
|
||||
if bid is None:
|
||||
return {
|
||||
"kind": "blank",
|
||||
@@ -312,7 +315,7 @@ def _plain(value: Any) -> Any:
|
||||
def daega_inputs(project_root: str | Path) -> dict[str, Any]:
|
||||
"""대가 셈 입력 — `{blocks: [...], owners: {구조물 id: 블록 id}}` · 사본 없으면 빈 것.
|
||||
|
||||
차례 = 조합(구조물 id 차례) → 사전조합 → 품셈(처음 본 차례) — 호표 번호 = 이 차례.
|
||||
차례 = 조합(구조물 id 차례) → 사전조합 → 일위대가화(품셈 · 처음 본 차례) — 호표 번호 = 이 차례.
|
||||
"""
|
||||
base = structures_dir(project_root)
|
||||
build = Builder()
|
||||
|
||||
Reference in New Issue
Block a user