feat(M02): 산출근거 표번 — 도번과 같은 규칙 자동 채번 · 좌측 「산출근거」 목록
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0168FQuoV7vDh5nhnSowW5cp
This commit is contained in:
@@ -118,6 +118,9 @@ export const createStructure = (column: string): Promise<TemplateDoc> =>
|
||||
export const fetchStructureNumbers = (): Promise<Record<string, string>> =>
|
||||
call("/structures/numbers");
|
||||
|
||||
/** `{열 id: 표번}` — 좌측 산출근거 줄(도번과 같은 규칙 · 「표-01」 꼴) */
|
||||
export const fetchBasisNumbers = (): Promise<Record<string, string>> => call("/basis/numbers");
|
||||
|
||||
/* --- 프로젝트 층 단추 다섯 --- */
|
||||
const project = (id: string, tail: string): string => `/projects/${enc(id)}/templates/${tail}`;
|
||||
const post = (path: string, body: object = {}): Promise<unknown> =>
|
||||
|
||||
@@ -43,6 +43,11 @@ def post_structure(column: str) -> dict:
|
||||
return _call(store.create_structure, column)
|
||||
|
||||
|
||||
@router.get("/basis/numbers")
|
||||
def get_basis_numbers() -> dict:
|
||||
return _call(store.basis_numbers)
|
||||
|
||||
|
||||
@router.get("/templates/{kind}/{name}")
|
||||
def get_template(kind: str, name: str) -> dict:
|
||||
return _call(store.read, kind, name)
|
||||
|
||||
@@ -31,6 +31,7 @@ _BASIS_HEAD_STYLE = {
|
||||
"테두리": {side: {"선": "thin"} for side in ("위", "아래", "왼", "오른")},
|
||||
}
|
||||
_NUMBER = re.compile(r"^구-(\d+)$")
|
||||
_TABLE_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 고침)
|
||||
@@ -221,16 +222,47 @@ def basis_doc(column: str, old: Any = None) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _basis_docs() -> list[tuple[str, Any]]:
|
||||
folder = FOLDER / "basis"
|
||||
return [(p.stem, json.loads(p.read_bytes())) for p in sorted(folder.glob("[!.]*.json"))]
|
||||
|
||||
|
||||
def _table_number(doc: Any) -> int:
|
||||
found = _TABLE_NUMBER.match(str(doc.get("표번") or "")) if isinstance(doc, dict) else None
|
||||
return int(found.group(1)) if found else 0
|
||||
|
||||
|
||||
def ensure_basis(column: str, old: Any = None) -> bool:
|
||||
"""산출근거 파일이 없으면 만듦(식 없는 새 문서라 `계산값` 없음 · 첫 [저장] 때 서버가 적음)."""
|
||||
"""산출근거 파일이 없으면 만듦(식 없는 새 문서라 `계산값` 없음 · 첫 [저장] 때 서버가 적음).
|
||||
표번 = 도번과 같은 규칙(있는 것 중 가장 큰 번호 + 1) · 만든 뒤로는 안 바뀜."""
|
||||
path = _path("basis", column)
|
||||
with _LOCK:
|
||||
if path.is_file():
|
||||
return False
|
||||
atomic_write_json(path, basis_doc(column, old))
|
||||
number = max((_table_number(doc) for _name, doc in _basis_docs()), default=0) + 1
|
||||
doc = {**basis_doc(column, old), "표번": f"표-{number:02d}"}
|
||||
atomic_write_json(path, doc)
|
||||
return True
|
||||
|
||||
|
||||
def basis_numbers() -> dict[str, str | None]:
|
||||
"""`{열 id: 표번}` — 좌측 산출근거 줄이 한 번에 받음.
|
||||
표번이 없는 옛 파일은 여기서 만나는 순서로 채움(도번 규칙과 같음)."""
|
||||
docs = _basis_docs()
|
||||
missing = [name for name, doc in docs if not (isinstance(doc, dict) and doc.get("표번"))]
|
||||
if missing:
|
||||
with _LOCK:
|
||||
docs = _basis_docs() # 잠금 안에서 다시 읽음 — 그 사이 누가 채웠을 수 있음
|
||||
number = max((_table_number(doc) for _name, doc in docs), default=0)
|
||||
for name, doc in docs:
|
||||
if isinstance(doc, dict) and not doc.get("표번"):
|
||||
number += 1
|
||||
doc["표번"] = f"표-{number:02d}"
|
||||
atomic_write_json(_path("basis", name), doc)
|
||||
docs = _basis_docs()
|
||||
return {name: (doc.get("표번") if isinstance(doc, dict) else None) for name, doc in docs}
|
||||
|
||||
|
||||
def _server_values(doc: Any) -> dict[str, Any]:
|
||||
try:
|
||||
return spreadsheet.with_server_values(doc)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_UI_Side.ts
|
||||
* 마스터 템플릿 좌측 패널 — 시스템 양식 목록(표/도면/구조물 도면) · 새로 · 본떠 · 지우기.
|
||||
* 서버 길은 계약 `6_계약.md`. 구조물 도면 줄은 집계표 열마다 하나(서버가 미리 만들어 둠 · 도번 항상 있음).
|
||||
* 마스터 템플릿 좌측 패널 — 시스템 양식 목록(표/도면/구조물 도면/산출근거) · 새로 · 본떠 · 지우기.
|
||||
* 서버 길은 계약 `6_계약.md`. 구조물 도면 · 산출근거 줄은 집계표 열마다 하나씩 —
|
||||
* 서버가 미리 만들어 둠(도번·표번 항상 있음 · 같은 번호 매기기 규칙).
|
||||
* ========================================================================== */
|
||||
|
||||
import {
|
||||
@@ -17,6 +18,7 @@ import { t as L } from "@ui/ui_template_locale";
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import {
|
||||
deleteTemplate,
|
||||
fetchBasisNumbers,
|
||||
fetchStructureNumbers,
|
||||
listTemplates,
|
||||
readTemplate,
|
||||
@@ -82,6 +84,7 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
let items: TemplateInfo[] = [];
|
||||
let columns: StructureColumn[] = [];
|
||||
let numbers: Record<string, string> = {};
|
||||
let basisNumbers: Record<string, string> = {};
|
||||
let seq = 0;
|
||||
|
||||
let picked: { kind: Kind; name: string } | null = null;
|
||||
@@ -95,12 +98,14 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
KINDS.map((k) => [k.kind, el("div", { className: "m02-side__items ui-sidebar-list" })]),
|
||||
);
|
||||
const structureBody = el("div", { className: "m02-side__items ui-sidebar-list" });
|
||||
const basisBody = el("div", { className: "m02-side__items ui-sidebar-list" });
|
||||
const listHost = el("div", {
|
||||
className: "m02-side__lists ui-sidebar-panel",
|
||||
children: [
|
||||
section(L("M02_KindTable"), bodies.get("table")!),
|
||||
section(L("M02_KindDrawing"), bodies.get("drawing")!),
|
||||
section(L("M02_KindStructure"), structureBody),
|
||||
section(L("M02_KindBasis"), basisBody),
|
||||
],
|
||||
});
|
||||
attachCollapsible(listHost);
|
||||
@@ -166,6 +171,24 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
);
|
||||
}
|
||||
|
||||
/** 산출근거 줄 — 구조물 도면과 같은 열 목록 · 표번은 도번과 같은 규칙(서버가 미리 만들어 둠) */
|
||||
function paintBasis(): void {
|
||||
basisBody.replaceChildren(
|
||||
...(columns.length
|
||||
? columns.map((col) => {
|
||||
const num = basisNumbers[col.id] ?? L("M02_StructureNone");
|
||||
const b = el("button", {
|
||||
className: `m02-side__item ui-sidebar-row${isPicked("basis", col.id) ? " is-active" : ""}`,
|
||||
text: `${columnHead(col)} · ${num}`,
|
||||
attrs: { type: "button" },
|
||||
});
|
||||
b.addEventListener("click", () => void selectBasisColumn(col));
|
||||
return b;
|
||||
})
|
||||
: [el("p", { className: "m02-side__empty", text: L("M02_NoTemplates") })]),
|
||||
);
|
||||
}
|
||||
|
||||
function paintButtons(): void {
|
||||
const editable = canEdit(layer, opt.isAdmin);
|
||||
btnNew.disabled = !editable;
|
||||
@@ -192,23 +215,37 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
label: columnHead(col),
|
||||
});
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
paintButtons();
|
||||
}
|
||||
|
||||
async function selectBasisColumn(col: StructureColumn): Promise<void> {
|
||||
if (!isPicked("basis", col.id) && !(await opt.confirmLeave())) return;
|
||||
picked = null;
|
||||
opt.onOpen({ layer, projectId: null, kind: "basis", name: col.id, label: columnHead(col) });
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
paintButtons();
|
||||
}
|
||||
|
||||
async function refreshStructure(): Promise<void> {
|
||||
try {
|
||||
const [table, nums] = await Promise.all([
|
||||
const [table, nums, basisNums] = await Promise.all([
|
||||
readTemplate(layer, "table", STRUCTURE_TABLE, null),
|
||||
fetchStructureNumbers(),
|
||||
fetchBasisNumbers(),
|
||||
]);
|
||||
columns = ((table.문서 as { 열?: StructureColumn[] }).열 ?? []) as StructureColumn[];
|
||||
numbers = nums;
|
||||
basisNumbers = basisNums;
|
||||
} catch (error) {
|
||||
columns = [];
|
||||
numbers = {};
|
||||
basisNumbers = {};
|
||||
showToast(L("M02_LoadFailed").replace("{value}", why(error)), "error");
|
||||
}
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
}
|
||||
|
||||
async function refresh(): Promise<void> {
|
||||
@@ -309,6 +346,7 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
picked = { kind, name };
|
||||
paintList();
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-01"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-02"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-03"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-04"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-05"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-06"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-07"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-08"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-09"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-10"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-11"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-12"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-13"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-14"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-15"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-16"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-17"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-18"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-19"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-20"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-21"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-22"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-23"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-24"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-25"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-26"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-27"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-28"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-29"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-30"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-31"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-32"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-33"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-34",
|
||||
"계산값": {}
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-35"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-36"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-37"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-38"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-39"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-40"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-41"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-42"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-43"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-44"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-45"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-46"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-47"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-48"
|
||||
}
|
||||
|
||||
@@ -52,5 +52,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"활성": "s1"
|
||||
"활성": "s1",
|
||||
"표번": "표-49"
|
||||
}
|
||||
|
||||
@@ -88,6 +88,29 @@ def test_새_구조물_도면은_산출근거_따로(client):
|
||||
assert heads == ["공종", "규격", "산출 내역", "단위", "수량(m당)"]
|
||||
|
||||
|
||||
def test_표번은_도번과_같은_규칙(client):
|
||||
made1 = client.post("/api/m02/structures/rv").json()["문서"]
|
||||
made2 = client.post("/api/m02/structures/pv_b").json()["문서"]
|
||||
assert made1["도번"] == "구-01" and made2["도번"] == "구-02"
|
||||
basis1 = client.get("/api/m02/templates/basis/rv").json()["문서"]
|
||||
basis2 = client.get("/api/m02/templates/basis/pv_b").json()["문서"]
|
||||
assert basis1["표번"] == "표-01" and basis2["표번"] == "표-02"
|
||||
numbers = client.get("/api/m02/basis/numbers").json()
|
||||
assert numbers["rv"] == "표-01" and numbers["pv_b"] == "표-02"
|
||||
|
||||
|
||||
def test_표번_없는_옛_파일은_만나면_채움(client, tmp_path):
|
||||
store.ensure_basis("rv")
|
||||
doc = store.read("basis", "rv")["문서"]
|
||||
del doc["표번"]
|
||||
(tmp_path / "basis" / "rv.json").write_text(
|
||||
json.dumps(doc, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
numbers = client.get("/api/m02/basis/numbers").json()
|
||||
assert numbers["rv"] == "표-01"
|
||||
assert store.read("basis", "rv")["문서"]["표번"] == "표-01" # 한 번 채우면 파일에 남음
|
||||
|
||||
|
||||
def test_저장은_서버_계산값만_적음(client):
|
||||
store.ensure_basis("rv")
|
||||
got = client.get("/api/m02/templates/basis/rv").json()
|
||||
|
||||
@@ -22,6 +22,7 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
M02 = ROOT / "M02_MasterTemplete"
|
||||
PANEL = M02 / "M02_MasterTemplete_Structure.ts"
|
||||
MAIN = M02 / "M02_MasterTemplete_UI_Main.ts"
|
||||
SIDE = M02 / "M02_MasterTemplete_UI_Side.ts"
|
||||
|
||||
|
||||
def _text(path: Path) -> str:
|
||||
@@ -39,23 +40,28 @@ def test_소스가_700줄을_넘지_않음():
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("source", "names"),
|
||||
("source", "names", "target"),
|
||||
[
|
||||
(
|
||||
"M02_MasterTemplete_Api_Fetch.ts",
|
||||
["createStructure", "fetchStructureNumbers", "readTemplate", "saveTemplate"],
|
||||
["createStructure", "fetchStructureNumbers", "readTemplate"],
|
||||
PANEL,
|
||||
),
|
||||
("M02_MasterTemplete_Drawing.ts", ["mountDrawingTemplate"]),
|
||||
("../A00_Common/spreadsheet/spreadsheet.ts", ["createSpreadsheet"]),
|
||||
# 산출근거 저장은 컨테이너가 안 함 — [산출근거 수정] 이 여는 메인 칸 전체 편집이 함
|
||||
("M02_MasterTemplete_Api_Fetch.ts", ["saveTemplate"], MAIN),
|
||||
# 좌측 산출근거 줄 번호는 좌측 패널이 받음(구조물 도면 줄과 같은 자리)
|
||||
("M02_MasterTemplete_Api_Fetch.ts", ["fetchBasisNumbers"], SIDE),
|
||||
("M02_MasterTemplete_Drawing.ts", ["mountDrawingTemplate"], PANEL),
|
||||
("../A00_Common/spreadsheet/spreadsheet.ts", ["createSpreadsheet"], PANEL),
|
||||
],
|
||||
)
|
||||
def test_컨테이너가_쓰는_이름이_내보내진_것(source: str, names: list[str]):
|
||||
def test_컨테이너가_쓰는_이름이_내보내진_것(source: str, names: list[str], target: Path):
|
||||
"""도면 부품 · 스프레드시트(D) 가 이름을 바꾸면 여기서 먼저 걸림."""
|
||||
body = _text(M02 / source)
|
||||
panel = _text(PANEL)
|
||||
used = _text(target)
|
||||
for name in names:
|
||||
assert re.search(rf"export (const|function|interface) {name}\b", body), f"{source}:{name}"
|
||||
assert name in panel, name
|
||||
assert name in used, name
|
||||
|
||||
|
||||
def test_집계표만_컨테이너를_붙임():
|
||||
|
||||
@@ -9,6 +9,7 @@ export const ui_locales_m2 = {
|
||||
M02_KindTable: ["표 양식", "Table templates"],
|
||||
M02_KindDrawing: ["도면 양식", "Drawing templates"],
|
||||
M02_KindStructure: ["구조물 도면", "Structure drawings"],
|
||||
M02_KindBasis: ["산출근거", "Cost basis"],
|
||||
M02_NoTemplates: ["양식 없음", "None"],
|
||||
M02_New: ["새로", "New"],
|
||||
M02_Copy: ["본떠 만들기", "Copy as new"],
|
||||
@@ -37,6 +38,7 @@ export const ui_locales_m2 = {
|
||||
],
|
||||
M02_NoSheet: ["표 편집 부품이 아직 없음", "Table editor not available yet"],
|
||||
M02_NoDrawing: ["도면 편집 부품이 아직 없음", "Drawing editor not available yet"],
|
||||
M02_NoSpreadsheet: ["산출근거 편집 부품이 아직 없음", "Cost basis editor not available yet"],
|
||||
M02_NameExists: ["이미 있는 이름", "That name already exists"],
|
||||
M02_BadName: [
|
||||
'쓸 수 없는 글자 — / \\ : * ? " < > | 와 .. · 앞의 점은 안 됨',
|
||||
|
||||
Reference in New Issue
Block a user