"""M02 마스터 템플릿 API — 시스템 층(계약 `6_계약.md` 서버 길 첫 묶음). ⚠ 권한은 등록하는 쪽(`main.py`)이 `system_admin_only` 로 붙임. """ from __future__ import annotations from typing import Any from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel from M02_MasterTemplete import M02_MasterTemplete_Branch as branch from M02_MasterTemplete import M02_MasterTemplete_Store as store router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete"]) class SaveBody(BaseModel): 판: str = "" 문서: Any class BranchBody(BaseModel): 본뜬키: str 담은로직: list[dict[str, Any]] 이름: str = "" 판: str = "" # 집계표 판 — 주면 어긋날 때 409 class MaterialBranchBody(BaseModel): 본뜬키: str = "" # 빈 글 = 이 열의 첫 자재 조합 담은자재: list[dict[str, Any]] 이름: str = "" 판: str = "" def _call(fn, *args): try: return fn(*args) except store.StoreError as e: raise HTTPException(status_code=e.status, detail=e.detail) from e @router.get("/templates") def get_templates() -> list[dict]: return _call(store.list_all) @router.get("/structures/numbers") def get_structure_numbers() -> dict: return _call(store.structure_numbers) @router.get("/structure-combos") def get_structure_combos() -> dict: """구조물집계표조합 — 구조물 열마다 줄 하나(키 SS · 제목행 · 번호 · 바인딩) · 읽기만.""" return _call(store.read_combos) @router.post("/structures/{column}") def post_structure(column: str) -> dict: """구조물 도면 새로 — 열 id 하나 · 도번 자동 · 빈 면적산출 · 재료산출도 · 이미 있으면 409.""" return _call(store.create_structure, column) @router.post("/structures/{column}/combo") def post_structure_combo(column: str, body: BranchBody) -> dict: """조합 갈라 내기 — 고친 담은 로직으로 새 UA(같은 로직 집합이면 있는 키) · 이 열만 가리킴.""" return _call(branch.branch_combo, column, body.본뜬키, body.담은로직, body.이름, body.판) @router.post("/structures/{column}/material-combo") def post_structure_material_combo(column: str, body: MaterialBranchBody) -> dict: """자재 조합 갈라 내기 — 같은 규칙(같은 자재 집합이면 있는 키 · 아니면 새 UM).""" return _call( branch.branch_material_combo, column, body.본뜬키, body.담은자재, body.이름, body.판 ) @router.get("/material-combos/{key}") def get_material_combo(key: str) -> dict: """자재 조합 한 줄(키 UM) — 키 · 이름 · 담은자재 · 본뜬키 · 소유 · 비고.""" return _call(branch.material_combo, key) # 복사 가져오기 — 다른 열(`from`) 내용을 받는 열로 · 번호 · 열은 받는 쪽 그대로 @router.post("/structures/{column}/copy") def post_structure_copy(column: str, source: str = Query(alias="from")) -> dict: return _call(store.copy_from, "structure", column, source) @router.post("/area/{column}/copy") def post_area_copy(column: str, source: str = Query(alias="from")) -> dict: return _call(store.copy_from, "area", column, source) @router.post("/material/{column}/copy") def post_material_copy(column: str, source: str = Query(alias="from")) -> dict: return _call(store.copy_from, "material", column, source) @router.get("/templates/{kind}/{name}") def get_template(kind: str, name: str) -> dict: return _call(store.read, kind, name) @router.put("/templates/{kind}/{name}") def put_template(kind: str, name: str, body: SaveBody) -> dict: return _call(store.write, kind, name, body.판, body.문서) @router.delete("/templates/{kind}/{name}") def delete_template(kind: str, name: str, 판: str | None = None) -> dict: _call(store.delete, kind, name, 판) return {"ok": True}