feat(723-4): B09 사본 일위대가(SC) 안 줄 수량 = 굳힌 손 → 구조물 사본 면적 알약 값 → 없음(빈 줄 · 이 공사에서 입력)
- Labor_Source.precombo = 빈 손 1 로 셈 걷음 · 안 줄 · 자재 수량 = 굳힌 옛 손 → 쓰는 구조물 사본 면적 알약 값 (723-1 「참조 id#차례」 · 「참조 id#자재|코드」 · 짝 = M02_Labor_Expand._qty 차례) → 없음 = qty null · `ask` - 알약 · 없음 쓴 SC 블록 덮개 열쇠 = `C:참조|S:사본` · `C:참조|L:서고`(쓰는 곳별 · 옛 손뿐 = `C:참조` 그대로) - 셈 짝(Calc_Labor .py ↔ .ts) = `ask` 줄 수량 비면 비고 · 연결 없음 「수량 없음 — 이 공사에서 입력」 · 702 덮개 qty 입력 뒤 금액 · 글 없음 - 지킴 시험 test_723_4_b09_sc_qty(경고 · 입력 금액 · 알약 굳힘 · 옛 손 · 거울) - 기존 공사 금액 전후 같음(검증_랩탑메인 · 검증_sub8 B09 셈 답 통째 같음) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,11 @@
|
||||
(`M02_Item_Prices.calc` = 142-1 층 셈 꼴)로 돌려 줄 수량을 받고, 블록 꼴로 펼침:
|
||||
|
||||
조합 구조물 조합 사본 하나 = 블록(엑셀 「조합」 꼴) · 줄 = 담은 로직 줄 · 자재 줄
|
||||
사전조합 참조 SC = 겹 대가(굳힌 `참조사본`) · 줄 = 그 안 로직 · 참조 줄(수량 = 손) ·
|
||||
자재 줄(175-2 · 수량 = 손 · 빈칸 = 수량 없음)
|
||||
사전조합 참조 SC = 겹 대가(굳힌 `참조사본`) · 줄 = 그 안 로직 · 참조 줄 · 자재 줄(175-2) ·
|
||||
안 줄 수량(723-4) = 굳힌 옛 손 → 쓰는 구조물 사본 면적 알약 값(723-1
|
||||
「참조 id#차례」 · 「참조 id#자재|코드」) → 없음 = 빈 줄(`ask` · 이 공사에서 입력 =
|
||||
702 덮개 qty) ·
|
||||
알약 · 없음 쓴 블록 덮개 = `C:참조|S:사본` · `C:참조|L:서고`(옛 손뿐 = `C:참조`)
|
||||
품셈 일위대가화(승격) 줄 · 옛 참조 SP = 로직마다 따로(노무 · 재료 · 중기 · 산근 · % 줄)
|
||||
— 조합이 수량만큼 부름 · 기계 = 기계경비 로직 줄 · 다른 돈 로직 줄 = 하위 산근
|
||||
산근 단가산출 줄(일위대가화 아닌 로직 줄) = 산근 호표 참조(키 = 145-1 `sangeun_key`)
|
||||
@@ -56,6 +59,7 @@ BASE_PART = re.compile(r"^\s*(노무비|재료비|경비)\s*\*")
|
||||
BASE_LINE = re.compile(r"^\s*줄\.'([^']+)'\s*\*")
|
||||
BASE_LABOR = re.compile(r"^\s*\(.*\*\s*LB\d+")
|
||||
AREA_FILE = "면적.json"
|
||||
ASK = "수량 없음 — 이 공사에서 입력" # 723-4 SC 안 줄 수량 빈칸(펼침 품 · 수량 칸)
|
||||
|
||||
|
||||
def _f(value: Any) -> float | None:
|
||||
@@ -253,10 +257,18 @@ class Builder:
|
||||
|
||||
# ── 조합 · 사전조합 줄 ──────────────────────────────────────────────
|
||||
def line_of(
|
||||
self, whole: Any, one: dict, qty: Any, times: Any, name: str, copies: dict, depth: int = 0
|
||||
self,
|
||||
whole: Any,
|
||||
one: dict,
|
||||
qty: Any,
|
||||
times: Any,
|
||||
name: str,
|
||||
copies: dict,
|
||||
depth: int = 0,
|
||||
use: tuple[dict, str] = ({}, ""),
|
||||
) -> dict[str, Any]:
|
||||
"""담은 로직 줄 하나 → 블록 줄 — 대가 줄 수량 = 수량 ÷ 배(부르는 호표 1 단위 몫) ·
|
||||
산근 줄 수량 = 수량 그대로(산근 단가 = 결과단위 1 단위)."""
|
||||
산근 줄 수량 = 수량 그대로(산근 단가 = 결과단위 1 단위) · use = 쓰는 곳(알약 값 · 자리)."""
|
||||
q = _f(qty)
|
||||
t = _f(times) or 1.0
|
||||
each = None if q is None else q / t
|
||||
@@ -290,7 +302,7 @@ class Builder:
|
||||
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)
|
||||
bid = self.precombo(whole, ref, doc, copies, depth + 1, use)
|
||||
return {"kind": "daega", "key": bid, "name": doc.get("이름") or name,
|
||||
"unit": doc.get("단위") or "", "qty": each} # fmt: skip
|
||||
code = str(one.get("로직") or "")
|
||||
@@ -312,10 +324,27 @@ class Builder:
|
||||
return {"kind": "daega", "key": bid, "name": block["name"], "spec": block["spec"],
|
||||
"spec_raw": block.get("spec_raw"), "unit": block["unit"], "qty": each} # fmt: skip
|
||||
|
||||
def precombo(self, whole: Any, ref: dict, doc: dict, copies: dict, depth: int = 0) -> str:
|
||||
"""사전조합(SC) = 겹 대가 — 줄 수량 = 손(기본 1) · 로직 줄 = 품셈 호표 · 자재 줄(175-2)."""
|
||||
def precombo(
|
||||
self,
|
||||
whole: Any,
|
||||
ref: dict,
|
||||
doc: dict,
|
||||
copies: dict,
|
||||
depth: int = 0,
|
||||
use: tuple[dict, str] = ({}, ""),
|
||||
) -> str:
|
||||
"""사전조합(SC) = 겹 대가 — 로직 줄 = 품셈 호표 · 자재 줄(175-2) · 줄 수량(723-4) =
|
||||
옛 손 → use 알약 값 → 없음(`ask`) · use = (쓰는 구조물 사본 면적 알약 값, 덮개 자리)."""
|
||||
from M01_MasterData import M01_MasterData_Store_Combo as combo_store
|
||||
|
||||
rid, (given, where) = refs.ref_id(ref), use
|
||||
loose: list[bool] = [] # 줄마다 손 아닌 수량(알약 · 없음)
|
||||
|
||||
def qty_of(key: str, hand: Any) -> float | None: # 짝 = `M02_Labor_Expand._qty` 차례
|
||||
got = _f(hand)
|
||||
loose.append(got is None)
|
||||
return _f(given.get(key)) if got is None else got
|
||||
|
||||
inner = [one for one in doc.get("담은로직") or [] if isinstance(one, dict)]
|
||||
kept = doc.get("알약") # 굳힌 알약(142-2 · 지금 M01 안 읽음 · 719-2) · 없으면 셈
|
||||
pills = {
|
||||
@@ -325,20 +354,20 @@ class Builder:
|
||||
for code in pill["로직"]
|
||||
}
|
||||
lines = []
|
||||
for one in inner:
|
||||
hand = _f((one.get("수량") or {}).get("손"))
|
||||
for n, one in enumerate(inner):
|
||||
qty = qty_of(f"{rid}#{n}", (one.get("수량") or {}).get("손"))
|
||||
pill = pills.get(one.get("로직")) or {}
|
||||
times = pill.get("배") if "로직" in one else 1
|
||||
name = pill.get("이름") or str(one.get("로직") or "")
|
||||
if depth >= refs.DEPTH and isinstance(one.get("참조"), dict):
|
||||
lines.append({"kind": "blank", "name": name, "qty": hand, "why": "참조 깊이 넘음"})
|
||||
lines.append({"kind": "blank", "name": name, "qty": qty, "why": "참조 깊이 넘음"})
|
||||
continue
|
||||
qty = 1 if hand is None else hand
|
||||
lines.append(self.line_of(whole, one, qty, times, name, copies, depth))
|
||||
lines += [
|
||||
_material(whole, mat, (mat.get("수량") or {}).get("손"))
|
||||
for mat in doc.get("자재") or []
|
||||
]
|
||||
lines.append(self.line_of(whole, one, qty, times, name, copies, depth, use))
|
||||
for mat in doc.get("자재") or []:
|
||||
key = f"{rid}#{labor_doc.material_key(mat.get('코드'))}"
|
||||
lines.append(_material(whole, mat, qty_of(key, (mat.get("수량") or {}).get("손"))))
|
||||
lines = [ln if ln["kind"] == "blank" or ln.get("qty") is not None else {**ln, "ask": ASK}
|
||||
for ln in lines] # fmt: skip
|
||||
return self.put(
|
||||
{
|
||||
"kind": "사전조합",
|
||||
@@ -347,9 +376,10 @@ class Builder:
|
||||
"spec_raw": str(ref.get("키") or ""),
|
||||
"unit": doc.get("단위") or "",
|
||||
"cite": "",
|
||||
"origin": {"참조": refs.ref_id(ref)},
|
||||
"origin": {"참조": rid},
|
||||
"lines": lines,
|
||||
}
|
||||
},
|
||||
f"C:{rid}|{where}" if where and any(loose) else None,
|
||||
)
|
||||
|
||||
# ── 조합(구조물 사본) ───────────────────────────────────────────────
|
||||
@@ -361,7 +391,9 @@ class Builder:
|
||||
values = read_json(folder / VALUES_FILE) or {}
|
||||
picks = read_json(folder / PICKS_FILE) or {}
|
||||
whole = item_prices.load_prices(prices)
|
||||
result = item_prices.calc(labor, prices, values, picks, read_json(folder / AREA_FILE))
|
||||
area = read_json(folder / AREA_FILE)
|
||||
result = item_prices.calc(labor, prices, values, picks, area)
|
||||
use = (labor_doc.area_values(area), cover or f"S:{folder.name}") # SC 안 줄 알약(723-4)
|
||||
doc = item_prices.with_values(labor.get("줄") or {}, values, picks)
|
||||
copies = labor.get("참조사본") or {}
|
||||
lines = []
|
||||
@@ -371,7 +403,14 @@ class Builder:
|
||||
(p for p in result.get("알약") or [] if p.get("키") == done.get("알약")), {}
|
||||
)
|
||||
line = self.line_of(
|
||||
whole, one, done.get("수량"), done.get("배"), pill.get("이름") or name, copies
|
||||
whole,
|
||||
one,
|
||||
done.get("수량"),
|
||||
done.get("배"),
|
||||
pill.get("이름") or name,
|
||||
copies,
|
||||
0,
|
||||
use,
|
||||
)
|
||||
if line.get("qty") is None and line["kind"] != "blank":
|
||||
line = {**line, "why": "수량 없음 — 면적산출 알약 칸 · 손 수량을 넣음"}
|
||||
@@ -405,7 +444,7 @@ class Builder:
|
||||
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)
|
||||
return self.precombo(whole, ref, doc, copies, 1, ({}, f"L:{folder.name}"))
|
||||
|
||||
|
||||
def fixed_line(fx: dict, qty: Any) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user