fix(M02): 구조물·도면 빈 문서에 기본 도면층 채움

- Store.py create_structure · UI_Side.ts stubDoc(도면 양식 「새로」) 둘 다
  빈 CAD 문서에 기본 도면층 하나(id·name "기본") 넣음 — 없으면 sub1 이 고친
  CAD import 가드가 대신 채우지만 뿌리도 처음부터 채움
- 이미 만들어진 구조물 도면 49개(resources/master_template/structure/*.json)
  도면.layers 채움(한 번 도는 정리) — 다른 칸·도번은 그대로 · 줄바꿈도 그대로
- 시험(test_m02_structure.py) — 새 문서 layers 1개 확인으로 바꿈

검증: M02 시험 28개 · 루트 npm run typecheck 통과. ORCA 로 확인 —
빈 구조물 도면(rv) 오류 없이 열림 · 도면 양식 「새로」 빈 도면 오류 없이 열림 ·
실제 선 하나 넣어 저장 → 다시 열기 → 화면에 그대로 · [저장] 단추로도 저장됨
(검증 뒤 rv 는 원래 빈 문서로 되돌림 · 검증용 임시 양식은 지움).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EUypcnp5d1gU2aeKh9F2H7
This commit is contained in:
2026-09-27 20:16:46 +09:00
co-authored by Claude Sonnet 5
parent d53ef8d3bf
commit dd7300aa02
52 changed files with 457 additions and 53 deletions
@@ -24,6 +24,10 @@ STRUCTURE_TABLE = "구조물집계표"
_BASIS_HEAD = (("work", "공종"), ("spec", "규격"), ("detail", "산출 내역"), ("unit", "단위"))
_NUMBER = re.compile(r"^구-(\d+)$")
_BAD_NAME = re.compile(r'[\\/:*?"<>|\x00-\x1f]|\.\.')
# 도각 없는 빈 CAD 문서의 기본 도면층 — 없으면 B07 CAD 가 못 엶("Cannot read properties
# of undefined (reading 'length')") · CAD 쪽도 없으면 새로 채우지만(2026-09-27 sub1 고침)
# 뿌리도 처음부터 채움
_BLANK_LAYER = {"id": "기본", "name": "기본", "isVisible": True, "isLocked": False}
# ponytail: 저장은 한 번에 하나(프로세스 안 잠금) · 서버를 여럿 띄우면 파일 잠금으로
_LOCK = threading.Lock()
@@ -145,7 +149,7 @@ def create_structure(column: str) -> dict[str, Any]:
"판": 1,
"열": column,
"도번": f"구-{number:02d}",
"도면": {"format": 6, "entities": []},
"도면": {"format": 6, "entities": [], "layers": [dict(_BLANK_LAYER)]},
"산출근거": {"양식": "산출근거", "종류": "표", "판": 1, "열": basis, "줄": basis_rows},
}
atomic_write_json(path, doc)
@@ -45,7 +45,13 @@ function stubDoc(kind: Kind, name: string): unknown {
합계줄: [],
보기: {},
}
: { format: 6, entities: [] };
: {
format: 6,
entities: [],
// 기본 도면층 하나 — 없으면 B07 CAD 가 못 엶("Cannot read properties of
// undefined (reading 'length')", 2026-09-27 실측)
layers: [{ id: "기본", name: "기본", isVisible: true, isLocked: false }],
};
}
export interface SideOptions {
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-25",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-33",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-23",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-22",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-12",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-13",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-35",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-24",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-36",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-14",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-16",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-15",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-30",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-29",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-31",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-47",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-45",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-37",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-43",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-42",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-38",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-39",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-46",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-44",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-41",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-40",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-48",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-49",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-32",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-20",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-01",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-18",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-17",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-11",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-10",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-09",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-34",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-07",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-04",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-08",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-05",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
@@ -6,7 +6,15 @@
"도번": "구-06",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-26",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-03",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-19",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-21",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-02",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-28",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+9 -1
View File
@@ -6,7 +6,15 @@
"도번": "구-27",
"도면": {
"format": 6,
"entities": []
"entities": [],
"layers": [
{
"id": "기본",
"name": "기본",
"isVisible": true,
"isLocked": false
}
]
},
"산출근거": {
"양식": "산출근거",
+4 -2
View File
@@ -34,8 +34,10 @@ def test_새로_도번_자동_409_없는_열(client):
"pp_len",
"구-01",
)
# 도각 없는 전용 화면 — 그린 만큼이 설계 영역
assert doc["도면"] == {"format": 6, "entities": []}
# 도각 없는 전용 화면 — 그린 만큼이 설계 영역 · 기본 도면층 하나(없으면 CAD 가 못 엶)
assert doc["도면"]["format"] == 6
assert doc["도면"]["entities"] == []
assert len(doc["도면"]["layers"]) == 1
basis = doc["산출근거"]
assert [c["머리"][0] for c in basis["열"]] == ["공종", "규격", "산출 내역", "단위", "수량(m당)"]
# 빈 줄 몇 개를 미리 둠 — 줄 더하기 안 눌러도 바로 입력