diff --git a/M02_MasterTemplete/M02_MasterTemplete_Items.py b/M02_MasterTemplete/M02_MasterTemplete_Items.py index e6953465..ea87928a 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Items.py +++ b/M02_MasterTemplete/M02_MasterTemplete_Items.py @@ -13,6 +13,8 @@ from pathlib import Path from typing import Any from common_util.common_util_json import atomic_write_json +from M01_MasterData import M01_MasterData_Store as m01 +from M01_MasterData import M01_MasterData_Store_Combo as combo_store from M02_MasterTemplete import M02_MasterTemplete_Matset as matset from M02_MasterTemplete import M02_MasterTemplete_Store as store @@ -61,12 +63,36 @@ def ensure(folder: Path) -> bool: return True +def _combo_names() -> dict[str, str]: + """일위대가 조합 키 → 이름(M01 시스템 마스터 · 층 무관) — 못 읽으면 빈 표.""" + try: + rows = m01.read(combo_store.FILE)[0].get("줄") or [] + except m01.StoreError: + return {} + return {str(r.get("키")): str(r.get("이름") or "") for r in rows} + + def items(folder: Path) -> list[dict[str, Any]]: """그 층 항목 — SS 줄마다 `{key, 열, 종류, 분류1~3, 단위, 일위대가, 자재조합, - 도번, 면적번, 재료번}`.""" + 도번, 면적번, 재료번, names}` · `names` = 화면 이름(키 대신 · 없으면 null) — + 일위대가 = 조합 이름 · 자재조합 = 그 층 자재 조합 이름 · + 도번 · 면적번 · 재료번 = 항목(열) 이름.""" + ua = _combo_names() with store.at(folder): rows = store.read_combos().get("줄", []) - return [{"key": r.get("키"), **{k: r.get(k) for k in ITEM_FIELDS}} for r in rows] + um = {str(r.get("키")): str(r.get("이름") or "") for r in matset.listing()["줄"]} + out = [] + for r in rows: + head = " ".join(str(r[k]) for k in ("종류", "분류1", "분류2", "분류3") if r.get(k)) + names = { + "일위대가": ua.get(str(r.get("일위대가"))) if r.get("일위대가") else None, + "자재조합": um.get(str(r.get("자재조합"))) if r.get("자재조합") else None, + **{ + k: (head or r.get("열")) if r.get(k) else None for k in ("도번", "면적번", "재료번") + }, + } + out.append({"key": r.get("키"), **{k: r.get(k) for k in ITEM_FIELDS}, "names": names}) + return out def _fresh_id(want: str, used: set[str]) -> str: diff --git a/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py b/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py index 1e8fa03b..9cfbe6c8 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py +++ b/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py @@ -6,6 +6,8 @@ personal 읽기 · 쓰기 = 본인(SYSTEM_ADMIN 은 누구나) · 회사 없음 = 쓰기 409 「회사부터 등록」 옛 길(`/api/m02/structures/…` 등 · `M02_MasterTemplete_Router.py`)은 시스템 층 별칭으로 그대로. 회사 · 개인 층은 처음 부를 때 빈 뼈대를 만듦(`Items.ensure`). +결정(PLAN 21-9): 일위대가(UA) 갈라 내기는 새 UA 가 M01 시스템 마스터에 생기므로 층 길에서도 +SYSTEM_ADMIN 만 · 첫 진입 뒤 구조물집계표가 이미 있어 같은 이름 새로 쓰기(판 없이)는 409 그대로. """ from __future__ import annotations diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index 849c54fe..4597dd7a 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -154,9 +154,12 @@ export function buildSide(opt: SideOptions): SideHandle { className: "m02-side__lists ui-sidebar-panel", children: [ summary, - section(options.title, options.body), + // 구조물 옵션 보이기 · 도면 양식은 시스템 층(마스터 템플릿)에만 ...(layer === "system" - ? [section(L("M02_KindDrawing"), bodies.get("drawing")!, ...actionRow("drawing"))] + ? [ + section(options.title, options.body), + section(L("M02_KindDrawing"), bodies.get("drawing")!, ...actionRow("drawing")), + ] : []), ], }); diff --git a/resources/tester/test_m02_layers_store.py b/resources/tester/test_m02_layers_store.py index 715ed51f..2afbc3c2 100644 --- a/resources/tester/test_m02_layers_store.py +++ b/resources/tester/test_m02_layers_store.py @@ -76,7 +76,8 @@ def test_시스템_층은_그대로_항목_목록은_SS_줄(world): rows = client.get("/api/m02/items").json() ss = store.read_combos()["줄"] assert [r["key"] for r in rows] == [r["키"] for r in ss] and rows[0]["key"] == "SS000001" - assert set(rows[0]) == {"key", *items_router.items_store.ITEM_FIELDS} + assert set(rows[0]) == {"key", "names", *items_router.items_store.ITEM_FIELDS} + assert set(rows[0]["names"]) == {"일위대가", "자재조합", "도번", "면적번", "재료번"} assert client.get("/api/m02/layers/system/structure-combos").json() == store.read_combos() # 시스템 층 쓰기는 SYSTEM_ADMIN 만 assert client.post("/api/m02/layers/system/structures/rv").status_code == 403 @@ -145,14 +146,26 @@ def test_회사_항목을_개인으로_복사(world): row = {"이름": "회사 자재", "담은자재": [_material()]} um = admin.post(f"{base}/material-combos", json={"판": 판, "줄": row}).json()["키"] admin.post(f"{base}/structures/cw/use-combo", json={"종류": "자재조합", "키": um}) - ss = admin.get("/api/m02/items?layer=company").json()[0]["key"] + admin.post(f"{base}/structures/cw/use-combo", json={"종류": "일위대가", "키": "UA000004"}) + row = admin.get("/api/m02/items?layer=company").json()[0] + ss = row["key"] + ua_name = items_router.items_store._combo_names()["UA000004"] + assert row["names"] == { # 키 대신 화면 이름 + "일위대가": ua_name, + "자재조합": "회사 자재", + "도번": "돌기슭막이 찰쌓기 회사형", + "면적번": "돌기슭막이 찰쌓기 회사형", + "재료번": "돌기슭막이 찰쌓기 회사형", + } user = _as(world) body = {"from_layer": "company", "from_owner": "7", "key": ss} got = user.post("/api/m02/layers/personal/items/copy", json=body) assert got.status_code == 200, got.text made = got.json() - assert made["열"] == "cw" and made["key"] == "SS000001" + assert made["열"] == "cw" and made["key"] == "SS000001" # 응답 = 새 SS 키(바로 고름) + mine_rows = user.get("/api/m02/items?layer=personal").json() + assert mine_rows[0]["key"] == made["key"] and mine_rows[0]["names"]["일위대가"] == ua_name mine = "/api/m02/layers/personal" copy = user.get(f"{mine}/templates/structure/cw").json()["문서"] assert copy["도면"]["entities"] == [{"type": "line"}] and copy["열"] == "cw"