- 탭 = 기번3 공통 엑셀 보이는 탭 차례(목차 · 안내 = 연결 점검) · 탭 하나 = 공용 표 하나 · 호표 칸 누름 = 그 탭 그 줄로 이동 - 저장 = 프로젝트 B09_Estimation/v1(사용자 값 · 굳힌 단가 한 벌) · 결과는 GET 때 서버 셈 · [저장] · [단가 갱신] 길 - 줄 = 수요 따라(연결된 사본 원천 자리 · 기계 → 운전사 · 연료 · B08 총괄 자재) · 연결 없으면 빈칸 + 연결 점검 목록 - 셈 = 파이썬 · TS 짝 + 거울(운전사 시간 노임 · 자재 최저 자동 + 사용자 고침 · 관급 · 별도 · 중기 손료 + 운전사 + 연료 + 잡품 · 엑셀 끝수) - 공용 표 = 칸 누름 · 줄로 이동(강조) 더함 - 시험 = test_144_b09_skeleton · test_144_b09_base_calc(엑셀 중기 126,000 · 76,436 · 96,829 · 40,659 · 운전사 55,700 · 47,231 · 33,571) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
85 lines
3.6 KiB
Python
85 lines
3.6 KiB
Python
"""B09 원가계산 길(PLAN 144 · 145) — 결과는 저장하지 않고 GET 때 서버가 셈(B08 꼴).
|
|
|
|
GET /api/projects/{id}/estimation 입력(굳힌 단가 · 수요 · B08 수량) + 사용자 값 → 표 전부
|
|
PUT /api/projects/{id}/estimation {user} [저장] — 사용자 값 · 책에 없는 새 단가 줄만 굳힘
|
|
→ 서버가 정본으로 다시 셈
|
|
POST /api/projects/{id}/estimation/prices [단가 갱신] — 수요 단가 전부를 지금 M01 값으로 새로 굳힘
|
|
|
|
저장 자리 = 프로젝트 안 `B09_Estimation/v1/`(`B09_Estimation_Store`) · 단가 = `B09_Estimation_Prices` ·
|
|
셈 = `B09_Estimation_Calc`(화면 짝과 같은 식).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Body
|
|
|
|
from B08_Quantity.B08_Quantity_Router_Hydro import _project_root
|
|
from B08_Quantity.B08_Quantity_Router_Material import material_sheets
|
|
from B09_Estimation import B09_Estimation_Prices as prices
|
|
from B09_Estimation import B09_Estimation_Store as store
|
|
from B09_Estimation.B09_Estimation_Calc_Base import compute_base
|
|
|
|
router = APIRouter(prefix="/api/projects", tags=["B09 Estimation"])
|
|
|
|
|
|
async def _b08(project_id: UUID, notes: list[str]) -> dict[str, Any]:
|
|
"""B08 수량 원천 — 못 읽으면 빈칸 + 알림(B09 는 B08 를 고치지 않음)."""
|
|
out: dict[str, Any] = {}
|
|
try:
|
|
out["materials"] = await material_sheets(project_id)
|
|
except Exception as error: # noqa: BLE001 — 원천 하나가 막혀도 나머지 표는 그림
|
|
notes.append(f"B08 자재 집계표 못 읽음 — {getattr(error, 'detail', error)}")
|
|
out["materials"] = None
|
|
return out
|
|
|
|
|
|
async def payload(project_id: UUID, root: Path) -> dict[str, Any]:
|
|
notes: list[str] = []
|
|
b08 = await _b08(project_id, notes)
|
|
keys = prices.linked_resource_keys(root)
|
|
if not keys:
|
|
notes.append(
|
|
"연결된 구조물 조합 사본 없음 — 노무 · 경비 · 중기 단가 줄은 사본이 연결되면 생김"
|
|
)
|
|
book = await asyncio.to_thread(store.read_book, root)
|
|
inputs = await asyncio.to_thread(prices.build_inputs, book, keys, b08["materials"])
|
|
user = await asyncio.to_thread(store.read_user, root)
|
|
return {
|
|
"inputs": inputs,
|
|
"user": user,
|
|
"book": {"made": book.get("made")} if book else None,
|
|
"sheets": compute_base(inputs, user),
|
|
"notes": notes,
|
|
}
|
|
|
|
|
|
@router.get("/{project_id}/estimation")
|
|
async def estimation(project_id: UUID) -> dict[str, Any]:
|
|
root = await _project_root(project_id)
|
|
return await payload(project_id, root)
|
|
|
|
|
|
@router.put("/{project_id}/estimation")
|
|
async def save_estimation(project_id: UUID, body: dict[str, Any] = Body(...)) -> dict[str, Any]:
|
|
root = await _project_root(project_id)
|
|
await asyncio.to_thread(store.write_user, root, body.get("user"))
|
|
keys = prices.linked_resource_keys(root)
|
|
book = await asyncio.to_thread(store.read_book, root)
|
|
book = await asyncio.to_thread(prices.extend_book, book, keys)
|
|
await asyncio.to_thread(store.write_book, root, book)
|
|
return await payload(project_id, root)
|
|
|
|
|
|
@router.post("/{project_id}/estimation/prices")
|
|
async def refresh_prices(project_id: UUID) -> dict[str, Any]:
|
|
root = await _project_root(project_id)
|
|
keys = prices.linked_resource_keys(root)
|
|
book = await asyncio.to_thread(prices.refresh_book, keys)
|
|
await asyncio.to_thread(store.write_book, root, book)
|
|
return await payload(project_id, root)
|