From b08b79bc6c6fa08d5d51a06da4c3c8b615705529 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 15:16:55 +0900 Subject: [PATCH] =?UTF-8?q?feat(b08):=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=EC=97=90=20=ED=95=AD=EB=AA=A9=20=EC=A2=85?= =?UTF-8?q?=EB=A5=98=20=EB=B0=B0=EC=A7=80=20[=EC=96=91=EC=8B=9D=ED=98=95]?= =?UTF-8?q?=C2=B7[=EA=B3=A0=EC=A0=95=ED=98=95]=20=E2=80=94=20=EB=AA=85?= =?UTF-8?q?=EC=84=B8=2013=EC=9E=A5=EB=8C=80=EB=A1=9C=20=EC=8B=9D=EC=9D=B4?= =?UTF-8?q?=20=ED=95=9C=20=EC=A4=84=EC=9D=B4=EB=9D=BC=EB=8F=84=20=EC=9E=88?= =?UTF-8?q?=EB=82=98=EB=A1=9C=EB=A7=8C=20=EA=B0=80=EB=A6=84(=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=EB=90=9C=20item=5Fkind=20=ED=91=9C=EC=8B=9C=EB=8A=94?= =?UTF-8?q?=20=EC=95=88=20=EB=AF=BF=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq --- .../B08_Quantity_Engine_StructureLibrary.py | 20 +++++++++++++++++-- .../B08_Quantity_UI_StructureSheet_Library.ts | 6 +++++- .../tester/test_b08_structure_library.py | 20 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/B08_Quantity/B08_Quantity_Engine_StructureLibrary.py b/B08_Quantity/B08_Quantity_Engine_StructureLibrary.py index e23e3f9a..bdfaaef5 100644 --- a/B08_Quantity/B08_Quantity_Engine_StructureLibrary.py +++ b/B08_Quantity/B08_Quantity_Engine_StructureLibrary.py @@ -44,10 +44,26 @@ def _items(folder: Path) -> list[dict[str, Any]]: return [json.loads(p.read_text(encoding="utf-8")) for p in sorted(folder.glob("*.json"))] +def item_kind(item: dict[str, Any]) -> str: + """양식형(`form`) · 고정형(`fixed`) — 명세 13장: 칸은 같고 **식이 한 줄이라도 있나**로만 가름. + 저장된 `item_kind` 표시는 안 믿음 — 줄과 어긋나면 배지가 거짓이 됨.""" + return ( + "form" + if any(str(row.get("formula") or "").strip() for row in item.get("rows") or []) + else "fixed" + ) + + def list_items(dirs: dict[str, Path], type_id: str) -> list[dict[str, Any]]: - """가져오기 고르개 — 단마다 그 종류의 항목(단·코드·이름).""" + """가져오기 고르개 — 단마다 그 종류의 항목(단·코드·이름·종류).""" return [ - {"tier": tier, "code": item.get("code"), "name": item.get("name"), "type_id": type_id} + { + "tier": tier, + "code": item.get("code"), + "name": item.get("name"), + "type_id": type_id, + "kind": item_kind(item), + } for tier in TIERS if tier in dirs for item in _items(dirs[tier]) diff --git a/B08_Quantity/B08_Quantity_UI_StructureSheet_Library.ts b/B08_Quantity/B08_Quantity_UI_StructureSheet_Library.ts index bc063301..196a177a 100644 --- a/B08_Quantity/B08_Quantity_UI_StructureSheet_Library.ts +++ b/B08_Quantity/B08_Quantity_UI_StructureSheet_Library.ts @@ -10,11 +10,14 @@ import { API_BASE_URL } from "@config/config_frontend"; const TIER_LABELS: Record = { personal: "개인", company: "회사", program: "기본" }; +/** 항목 종류 배지 — 양식형 = 제원을 바꾸면 수량이 다시 남 · 고정형 = 박힌 수량(명세 13장). */ +const KIND_LABELS: Record = { form: "양식형", fixed: "고정형" }; interface LibraryItem { tier: string; code: string; name: string; + kind: string; } /** 장 머리의 양식 표시 — **어느 단에서 가져왔나** + **그 뒤 고쳤나**(지금 읽는 단이 아님). */ @@ -92,7 +95,8 @@ export function buildLibraryPanel(options: LibraryPanelOptions): HTMLElement { const option = document.createElement("option"); option.value = `${item.tier}|${item.code}`; const now = item.code === currentCode ? " (지금)" : ""; - option.textContent = `${TIER_LABELS[item.tier] ?? item.tier} · ${item.name}${now}`; + const kind = KIND_LABELS[item.kind] ?? item.kind; + option.textContent = `${TIER_LABELS[item.tier] ?? item.tier} · [${kind}] ${item.name}${now}`; return option; }), ); diff --git a/resources/tester/test_b08_structure_library.py b/resources/tester/test_b08_structure_library.py index 10f4b4aa..5a6acf0c 100644 --- a/resources/tester/test_b08_structure_library.py +++ b/resources/tester/test_b08_structure_library.py @@ -216,6 +216,26 @@ def test_확정_때_기본_양식을_박는다(project: Path) -> None: assert library_module.pin_program_templates(project) == 0 # 이미 박힌 것은 그대로 +def test_항목마다_양식형_고정형_종류가_붙는다(tmp_path: Path) -> None: + """PLAN 4장 「항목마다 종류 배지」 — 명세 13장: 칸은 같고 `formula` 유무로만 갈림.""" + folder = tmp_path / "personal" + folder.mkdir() + fixed = { + "type_id": "masonry_wet", + "code": "AX-ST-0000beef", + "name": "뽑은 항목", + "rows": [{"name": "돌쌓기", "formula": "", "amount": 20.9}], + } + (folder / "AX-ST-0000beef.json").write_text(json.dumps(fixed), encoding="utf-8") + dirs = {"personal": folder, "program": library_module.TEMPLATE_DIR} + kinds = {item["tier"]: item["kind"] for item in library_module.list_items(dirs, "masonry_wet")} + assert kinds == {"personal": "fixed", "program": "form"} + ui = (ROOT / "B08_Quantity" / "B08_Quantity_UI_StructureSheet_Library.ts").read_text( + encoding="utf-8" + ) + assert "양식형" in ui and "고정형" in ui and "item.kind" in ui + + def test_코드_모양이_아니면_박지_않는다(project: Path) -> None: with pytest.raises(ValueError): library_module.import_item(project, {"type_id": "masonry_wet", "code": "../x"}, "personal")