feat(b09): 운반 단가 창 오른쪽 계산 칸 나눔 · 운반거리 칸 자동계산 단추 (277-1~4)

- 오른쪽 글 상자 → 칸 차례(거리 · 사이클 8-2-8 · 단가산출) · 칸마다 제목 · 몸 · 바닥 결과
- 사이클 칸 = Cm · 사이클시간 · t2 받는 로직만 · 조합 = 담은 로직 줄마다 미리보기 · 하위 산근 이어 붙임
- logic-preview 응답에 blocks(머리 · 호표 줄 · 계 · 출처) 더함 · 셈 · lines 그대로
- 값 칸 숫자 칸 = ui-input--sm(고르기 칸과 같은 높이) · 「거리에서」 → 「자동계산」 단추

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
This commit is contained in:
2026-10-12 18:25:05 +09:00
co-authored by Claude Opus 5.5
parent 9413b6c69a
commit f43a36aa39
8 changed files with 497 additions and 72 deletions
+19 -2
View File
@@ -8,7 +8,7 @@
[단가 갱신] — 수요 단가 전부를 지금 M01 값으로 새로 굳힘
POST /api/projects/{id}/estimation/logic-preview {code, given, rate, picks}
「운반 단가」 모달 계산 절차(253-3) — 그 로직 한 산근을 프로젝트 단가로 풀어 글 줄 · 단가 ·
저장 안 함(`render_sangeun_text` 를 `Sangeun.Solver` 로)
저장 안 함(`render_sangeun_text` 를 `Sangeun.Solver` 로) · `blocks` = 칸 나눔(277-2)
POST /api/projects/{id}/estimation/library {kind, layer, owner, key}
설계내역서 연결 고르기(204-2) — 서고 구조물조합 · 사전조합을 프로젝트 사본으로
(있으면 그대로) → {폴더, 만듦, key = 대가 블록 id, payload} ·
@@ -236,7 +236,8 @@ def logic_preview(root: Path, body: PreviewBody) -> dict[str, Any]:
keys = sangeun.hand_resource_keys({"haul_refs": {"preview": {"tab": "logic", **src}}})
user = store.read_user(root)
base = compute_base(prices.build_inputs(store.read_book(root), keys), user)
block = sangeun.Solver(base).add(sangeun.hand_whole(hands), src, None)
solver = sangeun.Solver(base)
block = solver.add(sangeun.hand_whole(hands), src, None)
text = [
*block["head"],
*(t for ln in block["lines"] for t in ln.get("text") or []),
@@ -249,6 +250,22 @@ def logic_preview(root: Path, body: PreviewBody) -> dict[str, Any]:
"price": block["sum"],
"parts": {p: block[p] for p in sangeun.PARTS},
"why": block["why"],
# 277-2 칸 나눔 — 맨 위 + 하위 산근 블록(차례) · 셈 그대로 · 화면이 머리 · 줄 · 계를 칸으로
"blocks": [_block_shown(b) for b in sorted(solver.blocks.values(), key=lambda b: b["no"])],
}
SHOWN_LINE = ("name", "spec", "qty", "unit", "kind", *sangeun.PARTS, "why", "text")
def _block_shown(block: dict[str, Any]) -> dict[str, Any]:
"""미리보기 칸 하나 — Solver 블록에서 화면이 쓰는 것만(머리 · 호표 줄 · 계 · 출처)."""
return {
**{k: block.get(k) for k in ("no", "logic", "name", "unit", "source", "head", "foot")},
"price": block.get("sum"),
"parts": {p: block.get(p) for p in sangeun.PARTS},
"why": block.get("why"),
"lines": [{k: ln.get(k) for k in SHOWN_LINE} for ln in block.get("lines") or []],
}