- 화면 이름 「구조물집계표」 · 「구조물 집계 양식」 → 「구조물 조합」(좌측 줄 · 상세 제목 · 안내글) · 파일 이름 · 양식 키 · API 길 그대로 - 좌측 패널 「항목 나무」 컨테이너 없앰 — 페이지가 나무를 만들어 편집 칸 왼쪽(aside)에 붙임 · 「구조물 조합」일 때만 보임 · 오른쪽 클릭 · 찾기 · 표와 서로 고름 · 고치기 전 저장 물음 그대로 - 표 부품 lockHead — 열 더하기 · 분류N 더하기 · 열 지우기 단추 없음 · 머리 · 층 이름 고치기 없음(단위 · 일위대가 · 조회 후 선택은 그대로) - 비고 열 · 전구간 줄 · 합계 줄은 보일 문서에서만 뺌(comboView) · [저장] 때 저장본 것을 다시 끼움(comboMerge) · 양식 파일 · M02_Table_Fill 그대로 - 서버 — 화면 표 [저장](store.write screen=True)은 있던 항목의 경로 · 층 이름 · 빈 폴더를 저장본 그대로(tree.keep_paths · 브라우저 머리 안 받음) · 머리는 경로에서 다시 만듦 · 나무 고침 줄기 그대로 - 나무 같은 이름 409 는 「이미 있는 항목」 까닭 그대로 보임(판 다름 안내와 가름) - 시험 test_m02_combo_screen 3(숨김 · 되끼움 Node · 잠금 · 나무 자리) · test_m02_tree 표 저장 약속 새로 · 이름 · 잠금 기대값 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
179 lines
6.3 KiB
Python
179 lines
6.3 KiB
Python
"""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_Matset as matset
|
|
from M02_MasterTemplete import M02_MasterTemplete_Options as options
|
|
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 = ""
|
|
|
|
|
|
class MatsetBody(BaseModel):
|
|
판: str = "" # 자재 조합 표 판 — 어긋나면 409
|
|
줄: dict[str, Any]
|
|
|
|
|
|
class OptionsBody(BaseModel):
|
|
판: str = "" # 옵션 표시 표 판 — 어긋나면 409
|
|
표시: dict[str, Any] # {종류: {옵션 키: 참 · 거짓}} — 거짓만 남김
|
|
|
|
|
|
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.판
|
|
)
|
|
|
|
|
|
class UseComboBody(BaseModel):
|
|
종류: str # 일위대가 · 자재조합
|
|
키: str
|
|
판: str = "" # 집계표 판 — 주면 어긋날 때 409
|
|
|
|
|
|
@router.post("/structures/{column}/use-combo")
|
|
def post_structure_use_combo(column: str, body: UseComboBody) -> dict:
|
|
"""있는 조합을 이 열에 씀 — 집계표 그 열 일위대가 · 자재조합 키만 바꿈(조합은 그대로)."""
|
|
return _call(branch.use_combo, column, body.종류, body.키, body.판)
|
|
|
|
|
|
# 자재 조합 표(마스터 템플릿 `자재조합.json` · 키 UM) — 좌측 「자재 조합」 컨테이너
|
|
@router.get("/material-combos")
|
|
def get_material_combos() -> dict:
|
|
"""표 통째 — `{판, 줄}`(줄마다 담은 자재 포함)."""
|
|
return _call(matset.listing)
|
|
|
|
|
|
@router.get("/material-combos/{key}")
|
|
def get_material_combo(key: str) -> dict:
|
|
"""자재 조합 한 줄(키 UM) — 키 · 이름 · 담은자재 · 본뜬키 · 소유 · 비고."""
|
|
return _call(branch.material_combo, key)
|
|
|
|
|
|
@router.post("/material-combos")
|
|
def post_material_combo(body: MatsetBody) -> dict:
|
|
"""새로 · 본떠 만들기(`줄.본뜬키`) — 키는 표 「다음」 번호 · `{키, 판, 줄}`."""
|
|
return _call(matset.create, body.줄, body.판)
|
|
|
|
|
|
@router.put("/material-combos/{key}")
|
|
def put_material_combo(key: str, body: MatsetBody) -> dict:
|
|
"""고치기 — 이름 · 비고 · 담은자재(본뜬키 · 소유는 있던 것)."""
|
|
return _call(matset.edit, key, body.줄, body.판)
|
|
|
|
|
|
@router.delete("/material-combos/{key}")
|
|
def delete_material_combo(key: str, 판: str = "") -> dict:
|
|
"""지우기 — 구조물 열이 쓰면 409 `{errors, users}`."""
|
|
return _call(matset.delete, key, 판)
|
|
|
|
|
|
# 구조물 옵션 보이기 — B05/B06 폼에 보일 옵션(마스터 템플릿 `구조물옵션표시.json`)
|
|
@router.get("/structure-options")
|
|
def get_structure_options() -> dict:
|
|
"""`{판, 표시, 종류}` — 표시 = 숨긴 것만 · 종류 = 옵션 있는 종류와 그 옵션."""
|
|
return _call(options.listing)
|
|
|
|
|
|
@router.put("/structure-options")
|
|
def put_structure_options(body: OptionsBody) -> dict:
|
|
"""고쳐 씀(낡은 판이면 409 · 없는 종류 · 키는 422) — 돌려주는 것 = 새 `{판, 표시, 종류}`."""
|
|
return _call(options.save, body.표시, body.판)
|
|
|
|
|
|
# 복사 가져오기 — 다른 열(`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.문서, True) # 화면 [저장](26-7e)
|
|
|
|
|
|
@router.delete("/templates/{kind}/{name}")
|
|
def delete_template(kind: str, name: str, 판: str | None = None) -> dict:
|
|
_call(store.delete, kind, name, 판)
|
|
return {"ok": True}
|