Files
Aislo/B09_Estimation/B09_Estimation_Router_MaterialPrices.py
T

103 lines
3.7 KiB
Python

"""B09 원가계산 라우터 — **자재 수동 단가** 읽기·저장 (PLAN 1장 Ⓐ · `…_MaterialPrices`).
⚠ 저장은 단가·출처만 — 금액은 다음 조회 때 서버가 그 값으로 **다시 조립**함
(브라우저 값을 받아 적지 않음).
"""
from __future__ import annotations
import logging
from datetime import date
from typing import Any
from uuid import UUID
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from B09_Estimation.B09_Estimation_MaterialPrices import (
MATERIAL_PRICES_KEY,
MaterialPriceError,
listing,
merge,
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/projects", tags=["B09 Estimation Material Prices"])
class MaterialPriceChange(BaseModel):
"""단가 칸 하나 — `price_krw` 가 비면 그 줄을 지움(단가 없음으로 돌아감)."""
key: str
price_krw: Any = None
source: str = ""
name: str = ""
spec: str = ""
unit: str = ""
class MaterialPriceRequest(BaseModel):
changes: list[MaterialPriceChange] = Field(default_factory=list)
async def _rows(project_id: UUID) -> dict[str, Any]:
from B09_Estimation.B09_Estimation_Router import _build_for, _project_root_of
from common_util.common_util_project_settings import estimation_settings
root = await _project_root_of(project_id)
settings = estimation_settings(root) if root else {}
build = await _build_for(project_id)
rows = listing(build, settings.get(MATERIAL_PRICES_KEY))
return {
"status": "success",
"rows": rows,
"unconfirmed_count": sum(1 for row in rows if row.get("price_krw")),
}
@router.get("/{project_id}/estimation/material-prices")
async def get_material_prices(project_id: UUID) -> JSONResponse:
"""단가 칸을 낼 자재 줄 — 자원 축 자재(코드) + 자원 축에서 사라진 저장 줄."""
try:
return JSONResponse(content=await _rows(project_id))
except Exception:
logger.exception("B09 자재 수동 단가 조회 실패: project_id=%s", project_id)
return JSONResponse(
status_code=500,
content={"status": "error", "message": "자재 단가 목록을 못 만들었습니다."},
)
@router.put("/{project_id}/estimation/material-prices")
async def put_material_prices(project_id: UUID, payload: MaterialPriceRequest) -> JSONResponse:
"""단가 저장 — 0 이하·수가 아닌 값은 받지 않음 · 값·출처가 같으면 넣은 날 그대로."""
from B09_Estimation.B09_Estimation_Router import _project_root_of
from common_util.common_util_project_settings import estimation_settings, save_section
root = await _project_root_of(project_id)
if root is None:
return JSONResponse(
status_code=404,
content={"status": "error", "message": "프로젝트 저장 폴더를 찾지 못했습니다."},
)
try:
prices = merge(
estimation_settings(root).get(MATERIAL_PRICES_KEY),
[change.model_dump() for change in payload.changes],
date.today().isoformat(),
)
except MaterialPriceError as error:
return JSONResponse(status_code=422, content={"status": "error", "message": str(error)})
try:
save_section(
root, "estimation", {MATERIAL_PRICES_KEY: prices}, replace_keys=(MATERIAL_PRICES_KEY,)
)
return JSONResponse(content=await _rows(project_id))
except Exception:
logger.exception("B09 자재 수동 단가 저장 실패: project_id=%s", project_id)
return JSONResponse(
status_code=500,
content={"status": "error", "message": "자재 단가를 저장하지 못했습니다."},
)