Files
Aislo/B09_Estimation/B09_Estimation_Linked.py
T
eomsangdonandClaude Opus 5.5 d9db1dfb1d feat(B09): 단가산출근거 · 목록표 — 승격 줄 · 옛 SP → 산근 블록 · B09 굳힌 단가 · 엑셀 끝수(145-1)
연결된 사본 일위대가 로직 줄 중 승격 + 옛 SP 참조 줄 · 같은 로직 + 같은 입력값 = 산근 하나 · 서버 단독 풀이 ·
대가 · 내역서가 읽는 길 sangeun_key · price_of · Linked.used_keys 빈 입력 = 자동값.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SE9MxeXTu2uaDZ55BJowAY
2026-10-09 15:19:31 +09:00

77 lines
3.3 KiB
Python

"""B09 연결된 사본 → 원가 수요 자원 키(PLAN 144 · 143-1 원천) — 서버 단독 · 읽기만.
연결된 사본 = `M02_Item_Snapshot.linked_snapshots`(지정 ∩ 같은 항목 사본 · B08 · B09 공용).
사본마다 `일위대가.json` 담은 로직을 그 사본 단가 묶음(`단가.json`) · 입력값 · 고른자재로 돌리며
실제로 부른 자원 줄(인력 LB · 기계 EQ · 자재 MT · 유가 MO)만 모음 — 단가 묶음 통째
(고르기 후보 전부)는 안 씀 · 값은 쓰지 않음(단가는 원가 한 벌 `price_book.json` 이 맡음).
구조물 조합 사본 없으면 그 구조물은 없음 · 일위대가 없는 사본은 자원 없음.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any
from M01_MasterData import M01_MasterData_Store as m01
from M02_MasterTemplete import M02_Item_Prices as item_prices
from M02_MasterTemplete.M02_Item_Snapshot import (
PICKS_FILE,
VALUES_FILE,
linked_snapshots,
structures_dir,
)
from M02_MasterTemplete.M02_Item_Source import LABOR_FILE, PRICE_FILE, read_json
#: 원가 단가 목록에 오르는 자원 표
RESOURCE_TABLES = ("LB", "EQ", "MT", "MO")
mf = item_prices.mf
def used_keys(folder: Path) -> list[str]:
"""사본 하나가 담은 로직을 돌릴 때 부른 자원 키 — 부른 차례 · 겹침 없음."""
labor = read_json(folder / LABOR_FILE)
if not isinstance(labor, dict):
return []
snapshot = item_prices.load_prices(read_json(folder / PRICE_FILE) or {})
values = read_json(folder / VALUES_FILE) or {}
picks = read_json(folder / PICKS_FILE) or {}
seen: list[str] = []
base_get = snapshot.get
def get(ref: str) -> dict[str, Any]:
row = base_get(ref)
key = str(row.get("키") or "")
if key[:2] in RESOURCE_TABLES and key not in seen:
seen.append(key)
return row
snapshot.get = get # 이 사본 묶음 한 벌에만 — 마스터 캐시는 안 건드림
for key in item_prices.mcb.logic_keys(labor.get("줄") or {}):
if not key:
continue
given = {name: (one or {}).get("값") for name, one in (values.get(key) or {}).items()}
given = {k: v for k, v in given.items() if v not in (None, "")}
try:
# 빈 입력 = 자동값(142-1 · 145-1 산근 풀이와 같음) — 안 채우면 그 로직 자원을 못 모음
auto = m01.mau.values(snapshot, snapshot.logic(key))
given = {**{k: v for k, v in auto.items() if v is not None}, **m01._dec(given)}
with mf.mp.choosing((picks or {}).get(key)):
mf.run(snapshot, key, given)
except (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError):
continue # 못 돈 로직 — 부른 데까지만
return seen
def linked_resource_keys(project_root: str | Path) -> list[str]:
"""연결된 사본 전부가 부른 자원 키 — 구조물 id 차례 · 겹침 없음."""
base = structures_dir(project_root)
out: list[str] = []
for _owner, linked in sorted(linked_snapshots(project_root).items()):
if "일위대가" not in linked.get("부분", []):
continue
for key in used_keys(base / linked["폴더"]):
if key not in out:
out.append(key)
return out