Merge remote-tracking branch 'origin/sub_laptop_4' into sub_laptop_2
This commit is contained in:
@@ -8,8 +8,9 @@ import { API_BASE_URL } from "@config/config_frontend";
|
||||
/** 층 이름 — 서버와 같은 글 */
|
||||
export type Layer = "system" | "company" | "personal" | "project";
|
||||
/** 서버 종류 — 화면의 「표 양식」 = table · 「도면 양식」 = drawing · 「구조물 도면」 = structure ·
|
||||
* 「상세 산출근거」 = basis(구조물집계표 열마다 한 통합문서) */
|
||||
export type Kind = "table" | "drawing" | "structure" | "basis";
|
||||
* 「면적산출」 = area · 「재료산출」 = material(둘 다 구조물집계표 열마다 한 통합문서) ·
|
||||
* @deprecated "basis" — 옛 이름(서버 길은 area 로 보냄) · sub2 가 새 이름으로 옮기면 지움 */
|
||||
export type Kind = "table" | "drawing" | "structure" | "area" | "material" | "basis";
|
||||
|
||||
export interface TemplateInfo {
|
||||
종류: Kind;
|
||||
@@ -49,8 +50,10 @@ const enc = encodeURIComponent;
|
||||
const q = (projectId: string | null): string => (projectId ? `?project_id=${enc(projectId)}` : "");
|
||||
const head = (layer: Layer): string =>
|
||||
layer === "system" ? "/templates" : `/layers/${layer}/templates`;
|
||||
/** 서버 길의 종류 — 옛 "basis" 는 "area" 로 */
|
||||
const kindPath = (kind: Kind): string => (kind === "basis" ? "area" : kind);
|
||||
const item = (layer: Layer, kind: Kind, name: string, projectId: string | null): string =>
|
||||
`${head(layer)}/${kind}/${enc(name)}${layer === "system" ? "" : q(projectId)}`;
|
||||
`${head(layer)}/${kindPath(kind)}/${enc(name)}${layer === "system" ? "" : q(projectId)}`;
|
||||
|
||||
/** 시스템 길은 배열 · 층 길은 `{층, 양식: [...]}` — 둘 다 목록으로 */
|
||||
export const listTemplates = async (
|
||||
@@ -82,7 +85,7 @@ export const saveTemplate = (
|
||||
|
||||
/** 시스템 층만 지움 */
|
||||
export const deleteTemplate = (kind: Kind, name: string, 판: string): Promise<{ ok: boolean }> =>
|
||||
call(`/templates/${kind}/${enc(name)}?판=${enc(판)}`, { method: "DELETE" });
|
||||
call(`/templates/${kindPath(kind)}/${enc(name)}?판=${enc(판)}`, { method: "DELETE" });
|
||||
|
||||
/** 개인·회사 층에서 지움 — 판이 맞을 때만(다르면 409) */
|
||||
export const deleteLayerTemplate = (
|
||||
@@ -91,12 +94,14 @@ export const deleteLayerTemplate = (
|
||||
name: string,
|
||||
판: string,
|
||||
): Promise<{ ok: boolean }> =>
|
||||
call(`/layers/${layer}/templates/${kind}/${enc(name)}?판=${enc(판)}`, { method: "DELETE" });
|
||||
call(`/layers/${layer}/templates/${kindPath(kind)}/${enc(name)}?판=${enc(판)}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
/* --- 구조물 도면(종류 structure · 이름 = 구조물집계표 열 id) ---
|
||||
* 읽기 · 저장 · 지우기는 위 readTemplate · saveTemplate · deleteTemplate 에 "structure" */
|
||||
|
||||
/** 구조물 도면 문서 — `도면` = 도면 양식과 같은 CAD 문서 · 산출근거는 따로(`basis` · 같은 열 id) */
|
||||
/** 구조물 도면 문서 — `도면` = 도면 양식과 같은 CAD 문서 · 면적산출 · 재료산출은 따로(같은 열 id) */
|
||||
export interface StructureDoc {
|
||||
양식: "구조물도면";
|
||||
종류: "structure";
|
||||
@@ -110,30 +115,45 @@ export interface StructureDoc {
|
||||
export const listStructures = async (): Promise<TemplateInfo[]> =>
|
||||
(await listTemplates("system", null)).filter((t) => t.종류 === "structure");
|
||||
|
||||
/** 새로 — 도번 자동 · 빈 도면 (+ 없으면 빈 산출근거 통합문서) · 이미 있으면 StaleError */
|
||||
/** 새로 — 도번 자동 · 빈 도면 (+ 없으면 빈 면적산출 · 재료산출 통합문서) · 이미 있으면 StaleError */
|
||||
export const createStructure = (column: string): Promise<TemplateDoc> =>
|
||||
call(`/structures/${enc(column)}`, { method: "POST" });
|
||||
|
||||
/** 한 열의 번호 — 도번 `SD` · 표번 `SC` + 같은 6자리 [종류][분류1][분류2](없는 쪽은 빈 글) */
|
||||
/** 한 열의 번호 — 도번 `SD` · 면적번 `SA` · 재료번 `SM` + 같은 8자리
|
||||
* [종류][분류1][분류2][분류3](없는 쪽은 빈 글) */
|
||||
export interface StructureNumbers {
|
||||
도번: string;
|
||||
표번: string;
|
||||
면적번: string;
|
||||
재료번: string;
|
||||
/** @deprecated 옛 이름 — 면적번과 같은 값 · sub2 가 옮기면 지움 */
|
||||
표번?: string;
|
||||
}
|
||||
|
||||
/** `{열 id: {도번, 표번}}` — 구조물 열만(글 열 · 없는 열은 키 없음 = 「없음」) */
|
||||
export const fetchStructureNumbers = (): Promise<Record<string, StructureNumbers>> =>
|
||||
call("/structures/numbers");
|
||||
/** `{열 id: {도번, 면적번, 재료번}}` — 구조물 열만(글 열 · 없는 열은 키 없음 = 「없음」) */
|
||||
export const fetchStructureNumbers = async (): Promise<Record<string, StructureNumbers>> => {
|
||||
const got = await call<Record<string, StructureNumbers>>("/structures/numbers");
|
||||
for (const row of Object.values(got)) row.표번 = row.면적번;
|
||||
return got;
|
||||
};
|
||||
|
||||
/** 다른 열(`from`) 구조물 도면 내용을 받는 열(`column`)로 가져옴 — 도번 · 열은 받는 쪽 그대로 */
|
||||
export const copyStructure = async (column: string, from: string): Promise<void> => {
|
||||
await call(`/structures/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" });
|
||||
};
|
||||
|
||||
/** 다른 열(`from`) 산출근거 내용을 받는 열(`column`)로 가져옴 — 표번 · 열은 받는 쪽 그대로 */
|
||||
export const copyBasis = async (column: string, from: string): Promise<void> => {
|
||||
await call(`/basis/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" });
|
||||
/** 다른 열(`from`) 면적산출 내용(알약 연결 포함)을 받는 열(`column`)로 가져옴 — 면적번 · 열은 그대로 */
|
||||
export const copyArea = async (column: string, from: string): Promise<void> => {
|
||||
await call(`/area/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" });
|
||||
};
|
||||
|
||||
/** 다른 열(`from`) 재료산출 내용(알약 연결 포함)을 받는 열(`column`)로 가져옴 — 재료번 · 열은 그대로 */
|
||||
export const copyMaterial = async (column: string, from: string): Promise<void> => {
|
||||
await call(`/material/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" });
|
||||
};
|
||||
|
||||
/** @deprecated 옛 이름 — copyArea · sub2 가 옮기면 지움 */
|
||||
export const copyBasis = copyArea;
|
||||
|
||||
/* --- 프로젝트 층 단추 다섯 --- */
|
||||
const project = (id: string, tail: string): string => `/projects/${enc(id)}/templates/${tail}`;
|
||||
const post = (path: string, body: object = {}): Promise<unknown> =>
|
||||
|
||||
@@ -39,20 +39,24 @@ def get_structure_numbers() -> dict:
|
||||
|
||||
@router.post("/structures/{column}")
|
||||
def post_structure(column: str) -> dict:
|
||||
"""구조물 도면 새로 — 열 id 하나 · 도번 자동 · 빈 산출근거도 · 이미 있으면 409."""
|
||||
"""구조물 도면 새로 — 열 id 하나 · 도번 자동 · 빈 면적산출 · 재료산출도 · 이미 있으면 409."""
|
||||
return _call(store.create_structure, column)
|
||||
|
||||
|
||||
# 복사 가져오기 — 다른 열(`from`) 내용을 받는 열로 · 번호 · 열은 받는 쪽 그대로
|
||||
@router.post("/structures/{column}/copy")
|
||||
def post_structure_copy(column: str, source: str = Query(alias="from")) -> dict:
|
||||
"""다른 열 구조물 도면 내용을 가져옴 — 도번 · 열은 받는 쪽 그대로."""
|
||||
return _call(store.copy_structure, column, source)
|
||||
return _call(store.copy_from, "structure", column, source)
|
||||
|
||||
|
||||
@router.post("/basis/{column}/copy")
|
||||
def post_basis_copy(column: str, source: str = Query(alias="from")) -> dict:
|
||||
"""다른 열 산출근거 내용을 가져옴 — 표번 · 열은 받는 쪽 그대로."""
|
||||
return _call(store.copy_basis, 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}")
|
||||
|
||||
@@ -261,8 +261,9 @@ async def save_layer_template(
|
||||
if kind == "table" and fill.is_fillable(document):
|
||||
# 채운 표가 와도 설계값은 안 받아 적음 — 양식 + 손 값만(5장 · 브라우저 값을 믿지 않음)
|
||||
document = fill.strip_design(document)
|
||||
if kind == "basis":
|
||||
# 산출근거 계산값은 서버가 같은 TS 엔진을 Node 로 돌려 새로 적음(브라우저 값 버림 · 5장 ②)
|
||||
if kind in ("area", "material"):
|
||||
# 면적산출 · 재료산출 계산값은 서버가 같은 TS 엔진을 Node 로 돌려 새로 적음
|
||||
# (브라우저 값 버림 · 5장 ②)
|
||||
try:
|
||||
document = await asyncio.to_thread(spreadsheet.with_server_values, document)
|
||||
except spreadsheet.RecalcError as error:
|
||||
|
||||
@@ -20,19 +20,24 @@ from M02_MasterTemplete import M02_Template_Layers as layers
|
||||
|
||||
FOLDER: Path = Path(__file__).resolve().parent.parent / "resources" / "master_template"
|
||||
KINDS = layers.KINDS # 시험은 FOLDER 를 사본으로 바꿈
|
||||
# 구조물 도면 = 구조물집계표 구조물 열(꼴=수) 마다 하나(파일 이름 = 열 id) · 도각 없는 전용 화면
|
||||
# 산출근거 = 같은 열 id 로 따로 한 파일(`basis/<열 id>.json` · 스프레드시트 통합문서)
|
||||
# 번호 = 도번 SD · 표번 SC + 6자리 [종류][분류1][분류2](제목행 = 열 `머리` 세 층 · 없는 층 00) —
|
||||
# 한 열의 두 번호는 같은 6자리 · 한 번 준 번호는 파일에 남아 안 바뀜(PLAN 12장)
|
||||
# 구조물집계표 구조물 열(꼴=수) 마다 세 파일(파일 이름 = 열 id) —
|
||||
# 구조물 도면 `structure/` (도각 없는 전용 화면) · 면적산출 `area/` · 재료산출 `material/`
|
||||
# (둘 다 스프레드시트 통합문서)
|
||||
# 번호 = 도번 SD · 면적번 SA · 재료번 SM + 8자리 [종류][분류1][분류2][분류3]
|
||||
# (제목행 = 열 `머리` 네 층 · 없는 층 00) — 한 열의 세 번호는 같은 8자리 ·
|
||||
# 한 번 준 번호는 파일에 남아 안 바뀜(PLAN 13장)
|
||||
STRUCTURE_TABLE = "구조물집계표"
|
||||
_BASIS_HEAD = ("공종", "규격", "산출 내역", "단위", "수량(m당)")
|
||||
_BASIS_HEAD_STYLE = {
|
||||
"굵게": True,
|
||||
"가로": "center",
|
||||
"세로": "center",
|
||||
"테두리": {side: {"선": "thin"} for side in ("위", "아래", "왼", "오른")},
|
||||
}
|
||||
_CODE = re.compile(r"^S[DC](\d{6})$")
|
||||
# (종류, 문서 번호 칸, 머리 글자, 시트 이름) — 시트 이름이 None 이면 구조물 도면
|
||||
PARTS = (
|
||||
("structure", "도번", "SD", None),
|
||||
("area", "면적번", "SA", "면적산출"),
|
||||
("material", "재료번", "SM", "재료산출"),
|
||||
)
|
||||
_KEY = {kind: key for kind, key, _, _ in PARTS}
|
||||
SHEETS = ("area", "material") # 통합문서 종류 — [저장] 때 서버가 계산값을 새로 적음
|
||||
_LEVELS = 4
|
||||
_CODE = re.compile(r"^S[DAM](\d{8})$")
|
||||
_OLD_CODE = re.compile(r"^S[DCA](\d{6})$") # PLAN 12장 6자리 — 끝에 00 을 붙여 읽음
|
||||
_BAD_NAME = re.compile(r'[\\/:*?"<>|\x00-\x1f]|\.\.')
|
||||
# 도각 없는 빈 CAD 문서의 기본 도면층 — 없으면 B07 CAD 가 못 엶("Cannot read properties
|
||||
# of undefined (reading 'length')") · CAD 쪽도 없으면 새로 채우지만(2026-09-27 sub1 고침)
|
||||
@@ -94,18 +99,22 @@ def _check_skeleton(kind: str, doc: Any) -> None:
|
||||
|
||||
def write(kind: str, name: str, version: str, doc: Any) -> dict[str, Any]:
|
||||
"""`version` 이 빈 글이면 새로 만듦(이미 있으면 409) · 아니면 그 판일 때만 덮어씀.
|
||||
구조물집계표를 저장하면 — 새 구조물 열의 빈 구조물 도면 · 산출근거를 번호와 같이 만들고
|
||||
지운 열(또는 구조물이 아니게 된 열)의 두 파일을 지움(잠금 밖에서 · 안에서 부르면 죽음).
|
||||
산출근거(basis)는 서버가 같은 TS 엔진을 Node 로 돌려 `계산값` 을 새로 적음
|
||||
(브라우저 값 버림 · 5장 ②)."""
|
||||
구조물집계표를 저장하면 — 새 구조물 열의 빈 구조물 도면 · 면적산출 · 재료산출을 번호와 같이
|
||||
만들고 지운 열(또는 구조물이 아니게 된 열)의 세 파일을 지움(잠금 밖에서 · 안에서 부르면 죽음).
|
||||
통합문서(area · material)는 서버가 같은 TS 엔진을 Node 로 돌려 `계산값` 을 새로 적음
|
||||
(브라우저 값 버림 · 5장 ②) · 알약 `연결` 은 문서와 같이 그대로 적힘.
|
||||
번호 칸(도번 · 면적번 · 재료번)은 서버 것 — 있던 파일 번호를 그대로 둠(화면 값을 안 받음)."""
|
||||
path = _path(kind, name)
|
||||
_check_skeleton(kind, doc)
|
||||
if kind == "basis":
|
||||
if kind in SHEETS:
|
||||
doc = _server_values(doc)
|
||||
with _LOCK:
|
||||
have = version_of(path.read_bytes()) if path.is_file() else ""
|
||||
if have != (version or ""):
|
||||
raise StoreError(409, {"stale": [name], "판": have})
|
||||
if have and kind in _KEY:
|
||||
number = json.loads(path.read_bytes()).get(_KEY[kind])
|
||||
doc = {**doc, _KEY[kind]: number} if number else doc
|
||||
atomic_write_json(path, doc)
|
||||
info = _info(kind, path)
|
||||
if kind == "table" and name == STRUCTURE_TABLE:
|
||||
@@ -124,43 +133,45 @@ def delete(kind: str, name: str, version: str | None = None) -> None:
|
||||
path.unlink()
|
||||
|
||||
|
||||
# ── 구조물 도면 ───────────────────────────────────────
|
||||
# ── 구조물 도면 · 면적산출 · 재료산출 ─────────────────────
|
||||
|
||||
|
||||
def _structures() -> list[tuple[str, Any]]:
|
||||
folder = FOLDER / "structure"
|
||||
def _docs(kind: str) -> list[tuple[str, Any]]:
|
||||
folder = FOLDER / kind
|
||||
return [(p.stem, json.loads(p.read_bytes())) for p in sorted(folder.glob("[!.]*.json"))]
|
||||
|
||||
|
||||
def _code(doc: Any, key: str) -> str | None:
|
||||
"""문서의 새 번호 6자리 — 옛 `구-01` · `표-01` · 없음은 None."""
|
||||
found = _CODE.match(str(doc.get(key) or "")) if isinstance(doc, dict) else None
|
||||
return found.group(1) if found else None
|
||||
"""문서의 번호 8자리 — 12장 6자리는 끝에 00 · 옛 `구-01` · `표-01` · 없음은 None."""
|
||||
text = str(doc.get(key) or "") if isinstance(doc, dict) else ""
|
||||
if found := _CODE.match(text):
|
||||
return found.group(1)
|
||||
found = _OLD_CODE.match(text)
|
||||
return found.group(1) + "00" if found else None
|
||||
|
||||
|
||||
def _codes() -> dict[str, str]:
|
||||
"""`{열 id: 6자리}` — 구조물 도면 도번 먼저 · 없으면 산출근거 표번."""
|
||||
codes = {name: c for name, doc in _basis_docs() if (c := _code(doc, "표번"))}
|
||||
codes.update({name: c for name, doc in _structures() if (c := _code(doc, "도번"))})
|
||||
"""`{열 id: 8자리}` — 구조물 도면 도번 먼저 · 없으면 면적번 · 재료번."""
|
||||
codes: dict[str, str] = {}
|
||||
for kind, key, _, _ in reversed(PARTS):
|
||||
codes.update({name: c for name, doc in _docs(kind) if (c := _code(doc, key))})
|
||||
return codes
|
||||
|
||||
|
||||
def structure_numbers() -> dict[str, dict[str, str]]:
|
||||
"""`{열 id: {도번, 표번}}` — 집계표 도면 · 산출근거 줄 · 좌측 줄이 한 번에 받음
|
||||
(없는 쪽은 빈 글)."""
|
||||
"""`{열 id: {도번, 면적번, 재료번}}` — 집계표 줄 · 좌측 줄이 한 번에 받음(없는 쪽은 빈 글)."""
|
||||
out: dict[str, dict[str, str]] = {}
|
||||
for name, doc in _structures():
|
||||
out.setdefault(name, {"도번": "", "표번": ""})["도번"] = str(doc.get("도번") or "")
|
||||
for name, doc in _basis_docs():
|
||||
out.setdefault(name, {"도번": "", "표번": ""})["표번"] = str(doc.get("표번") or "")
|
||||
for kind, key, _, _ in PARTS:
|
||||
for name, doc in _docs(kind):
|
||||
row = out.setdefault(name, {k: "" for _, k, _, _ in PARTS})
|
||||
row[key] = str(doc.get(key) or "")
|
||||
return out
|
||||
|
||||
|
||||
def _heads(col: dict[str, Any]) -> tuple[str | None, str | None, str | None]:
|
||||
"""제목행 세 층(종류 · 분류1 · 분류2) — 빈 층은 None."""
|
||||
parts = [*(col.get("머리") or []), None, None, None][:3]
|
||||
clean = [str(p).strip() if p is not None else "" for p in parts]
|
||||
return (clean[0] or None, clean[1] or None, clean[2] or None)
|
||||
def _heads(col: dict[str, Any]) -> tuple[str | None, ...]:
|
||||
"""제목행 네 층(종류 · 분류1 · 분류2 · 분류3) — 빈 층은 None."""
|
||||
parts = [*(col.get("머리") or []), *[None] * _LEVELS][:_LEVELS]
|
||||
return tuple((str(p).strip() if p is not None else "") or None for p in parts)
|
||||
|
||||
|
||||
def _structure_columns() -> list[dict[str, Any]]:
|
||||
@@ -178,32 +189,36 @@ def _column_ids() -> list[str]:
|
||||
|
||||
|
||||
def _pick_code(col: dict[str, Any], taken: dict[str, str], heads: dict[str, tuple]) -> str:
|
||||
"""새 열 번호 — 같은 종류(· 분류1)가 이미 번호를 가졌으면 그 두 자리를 이어 받고 · 없으면 그 층
|
||||
가장 큰 번호 + 1 · 빈 층은 00. 같은 6자리가 이미 있으면(같은 제목행 · 빈 분류2 겹침) 분류2 를
|
||||
그 무리 가장 큰 번호 + 1 로. `taken` = 이미 번호 있는 열 `{열 id: 6자리}` ·
|
||||
`heads` = 열 id → 제목행."""
|
||||
kind, first, second = _heads(col)
|
||||
"""새 열 번호 — 층마다 같은 무리(그 층까지 제목행이 같은 열)가 이미 번호를 가졌으면 그 두 자리를
|
||||
이어 받고 · 없으면 그 층 가장 큰 번호 + 1 · 빈 층(종류 밖)은 00 · 맨 아래 층(분류3)은 늘 다음
|
||||
번호. 같은 8자리가 이미 있으면(같은 제목행 · 빈 분류3 겹침) 맨 아래 층을 그 무리 다음 번호로.
|
||||
`taken` = 이미 번호 있는 열 `{열 id: 8자리}` · `heads` = 열 id → 제목행."""
|
||||
parts = _heads(col)
|
||||
codes = list(taken.values())
|
||||
mates = [(heads[cid], code) for cid, code in taken.items() if cid in heads]
|
||||
|
||||
def next_of(prefix: str, at: slice) -> str:
|
||||
def next_of(prefix: str) -> str:
|
||||
at = slice(len(prefix), len(prefix) + 2)
|
||||
found = [int(c[at]) for c in codes if c.startswith(prefix)]
|
||||
return f"{max(found, default=0) + 1:02d}"
|
||||
|
||||
k = next((c[:2] for h, c in mates if h[0] == kind), None) or next_of("", slice(0, 2))
|
||||
if first is None:
|
||||
a = "00"
|
||||
else:
|
||||
same = (c[2:4] for h, c in mates if h[:2] == (kind, first) and c[:2] == k)
|
||||
a = next(same, None) or next_of(k, slice(2, 4))
|
||||
b = "00" if second is None else next_of(k + a, slice(4, 6))
|
||||
if k + a + b in codes:
|
||||
b = next_of(k + a, slice(4, 6))
|
||||
return k + a + b
|
||||
code = ""
|
||||
for depth, head in enumerate(parts):
|
||||
if depth and head is None:
|
||||
code += "00"
|
||||
continue
|
||||
at = slice(2 * depth, 2 * depth + 2)
|
||||
group = parts[: depth + 1]
|
||||
same = (c[at] for h, c in mates if h[: depth + 1] == group and c.startswith(code))
|
||||
leaf = depth == _LEVELS - 1
|
||||
code += (None if leaf else next(same, None)) or next_of(code)
|
||||
if code in codes:
|
||||
code = code[:-2] + next_of(code[:-2])
|
||||
return code
|
||||
|
||||
|
||||
def _new_code(column: str) -> str:
|
||||
"""이 열이 쓸 6자리 — 짝 파일에 이미 있으면 그것 · 없으면 새로(잠금 안에서 부름)."""
|
||||
"""이 열이 쓸 8자리 — 짝 파일에 이미 있으면 그것 · 없으면 새로(잠금 안에서 부름)."""
|
||||
taken = _codes()
|
||||
if column in taken:
|
||||
return taken[column]
|
||||
@@ -212,144 +227,72 @@ def _new_code(column: str) -> str:
|
||||
return _pick_code(col, taken, {c["id"]: _heads(c) for c in cols})
|
||||
|
||||
|
||||
def create_structure(column: str) -> dict[str, Any]:
|
||||
"""구조물 열 하나의 구조물 도면 새로 — 도번 SD + 6자리(산출근거와 같은 6자리) · 이미 있으면 409.
|
||||
산출근거 파일이 없으면 빈 통합문서도 같이 만듦."""
|
||||
path = _path("structure", column)
|
||||
if column not in _column_ids():
|
||||
raise StoreError(404, f"{STRUCTURE_TABLE}에 없는 구조물 열 「{column}」")
|
||||
with _LOCK:
|
||||
if path.is_file():
|
||||
raise StoreError(409, f"이미 있는 구조물 도면 「{column}」")
|
||||
atomic_write_json(path, {**_blank_structure(column), "도번": f"SD{_new_code(column)}"})
|
||||
ensure_basis(column)
|
||||
return read("structure", column)
|
||||
|
||||
|
||||
def _blank_structure(column: str) -> dict[str, Any]:
|
||||
"""빈 구조물 도면(도번은 부르는 쪽이 붙임) — 도각 없음 · 기본 도면층 하나."""
|
||||
def _blank(kind: str, column: str) -> dict[str, Any]:
|
||||
"""빈 문서(번호는 부르는 쪽이 붙임) — 구조물 도면 = 도각 없음 · 기본 도면층 하나 /
|
||||
면적산출 · 재료산출 = 진짜 빈 시트 하나(식 없어 `계산값` 없음 · 첫 [저장] 때 서버가 적음)."""
|
||||
if kind == "structure":
|
||||
return {
|
||||
"양식": "구조물도면",
|
||||
"종류": "structure",
|
||||
"판": 1,
|
||||
"열": column,
|
||||
"도면": {"format": 6, "entities": [], "layers": [dict(_BLANK_LAYER)]},
|
||||
}
|
||||
sheet = next(name for k, _, _, name in PARTS if k == kind)
|
||||
return {
|
||||
"양식": "구조물도면",
|
||||
"종류": "structure",
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": column,
|
||||
"도면": {"format": 6, "entities": [], "layers": [dict(_BLANK_LAYER)]},
|
||||
"서식": [{}],
|
||||
"시트": [{"id": "s1", "이름": sheet, "칸": {}}],
|
||||
"활성": "s1",
|
||||
}
|
||||
|
||||
|
||||
def copy_structure(column: str, source: str) -> dict[str, Any]:
|
||||
"""다른 열 구조물 도면 내용을 가져옴 — 도번 · 열은 받는 쪽 그대로(두 열은 따로 움직임)."""
|
||||
return _copy("structure", column, source, ("열", "도번"))
|
||||
def _ensure(kind: str, column: str) -> bool:
|
||||
"""그 열의 파일이 없으면 빈 문서를 번호(세 파일이 같은 8자리)와 같이 만듦 · 있으면 그대로."""
|
||||
path = _path(kind, column)
|
||||
head = next(h for k, _, h, _ in PARTS if k == kind)
|
||||
with _LOCK:
|
||||
if path.is_file():
|
||||
return False
|
||||
atomic_write_json(path, {**_blank(kind, column), _KEY[kind]: head + _new_code(column)})
|
||||
return True
|
||||
|
||||
|
||||
def copy_basis(column: str, source: str) -> dict[str, Any]:
|
||||
"""다른 열 산출근거 내용을 가져옴 — 표번 · 열은 받는 쪽 그대로 · 계산값은 서버가 다시 풂."""
|
||||
return _copy("basis", column, source, ("열", "표번"))
|
||||
def create_structure(column: str) -> dict[str, Any]:
|
||||
"""구조물 열 하나의 구조물 도면 새로 — 도번 SD + 8자리 · 이미 있으면 409.
|
||||
면적산출 · 재료산출 파일이 없으면 빈 통합문서도 같이 만듦(같은 8자리)."""
|
||||
if column not in _column_ids():
|
||||
raise StoreError(404, f"{STRUCTURE_TABLE}에 없는 구조물 열 「{column}」")
|
||||
if not _ensure("structure", column):
|
||||
raise StoreError(409, f"이미 있는 구조물 도면 「{column}」")
|
||||
for kind in SHEETS:
|
||||
_ensure(kind, column)
|
||||
return read("structure", column)
|
||||
|
||||
|
||||
def _copy(kind: str, column: str, source: str, keep: tuple[str, ...]) -> dict[str, Any]:
|
||||
def copy_from(kind: str, column: str, source: str) -> dict[str, Any]:
|
||||
"""다른 열(`source`) 내용을 받는 열(`column`)로 가져옴 — 번호 · 열은 받는 쪽 그대로(두 열은
|
||||
따로 움직임) · 통합문서는 알약 `연결` 도 같이 오고 계산값은 서버가 다시 풂."""
|
||||
if kind not in _KEY:
|
||||
raise StoreError(404, f"없는 종류 「{kind}」")
|
||||
target = read(kind, column)["문서"]
|
||||
doc = {**read(kind, source)["문서"], **{key: target.get(key) for key in keep}}
|
||||
if kind == "basis":
|
||||
doc = {**read(kind, source)["문서"], **{k: target.get(k) for k in ("열", _KEY[kind])}}
|
||||
if kind in SHEETS:
|
||||
doc = _server_values({k: v for k, v in doc.items() if k != "계산값"})
|
||||
with _LOCK:
|
||||
atomic_write_json(_path(kind, column), doc)
|
||||
return _info(kind, _path(kind, column))
|
||||
|
||||
|
||||
# ── 산출근거 ──────────────────────────────────────────
|
||||
|
||||
|
||||
def _col_letters(index: int) -> str:
|
||||
letters = ""
|
||||
index += 1
|
||||
while index:
|
||||
index, rest = divmod(index - 1, 26)
|
||||
letters = chr(65 + rest) + letters
|
||||
return letters
|
||||
|
||||
|
||||
def basis_doc(column: str, old: Any = None) -> dict[str, Any]:
|
||||
"""산출근거 통합문서 — 새로는 진짜 빈 시트(머리 줄 없음).
|
||||
`old` = 옛 구조물 문서 `산출근거` 표(열 · 줄) — 머리 글 · 칸 값 · 열 폭을 옮김
|
||||
(옛 열 식은 없어 안 옮김)."""
|
||||
columns = old.get("열") if isinstance(old, dict) and isinstance(old.get("열"), list) else None
|
||||
if columns:
|
||||
heads = [
|
||||
" ".join(str(h) for h in (c.get("머리") or []) if h) or c.get("id", "") for c in columns
|
||||
]
|
||||
else:
|
||||
heads, columns = [], []
|
||||
cells: dict[str, Any] = {
|
||||
f"{_col_letters(i)}1": {"값": head, "서식": 1} for i, head in enumerate(heads) if head
|
||||
}
|
||||
for r, row in enumerate(old.get("줄", []) if columns else [], start=2):
|
||||
values = row.get("값", {}) if isinstance(row, dict) else {}
|
||||
for i, col in enumerate(columns):
|
||||
value = values.get(col.get("id"))
|
||||
if value not in (None, ""):
|
||||
cells[f"{_col_letters(i)}{r}"] = {"값": value}
|
||||
widths = ((old or {}).get("보기") or {}).get("열너비") or {} if columns else {}
|
||||
# 옛 폭은 px · 새 폭은 엑셀 글자 단위(숫자 폭 7px 어림)
|
||||
info = {
|
||||
_col_letters(i): {"폭": round(widths[c["id"]] / 7, 1)}
|
||||
for i, c in enumerate(columns)
|
||||
if isinstance(widths.get(c.get("id")), (int, float))
|
||||
}
|
||||
sheet: dict[str, Any] = {"id": "s1", "이름": "산출근거", "칸": cells}
|
||||
if info:
|
||||
sheet["열"] = info
|
||||
return {
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": column,
|
||||
"서식": [{}, _BASIS_HEAD_STYLE] if heads else [{}],
|
||||
"시트": [sheet],
|
||||
"활성": "s1",
|
||||
}
|
||||
|
||||
|
||||
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 ensure_basis(column: str, old: Any = None) -> bool:
|
||||
"""산출근거 파일이 없으면 만듦(식 없는 새 문서라 `계산값` 없음 · 첫 [저장] 때 서버가 적음).
|
||||
표번 = SC + 구조물 도면과 같은 6자리 · 만든 뒤로는 안 바뀜."""
|
||||
path = _path("basis", column)
|
||||
with _LOCK:
|
||||
if path.is_file():
|
||||
return False
|
||||
doc = {**basis_doc(column, old), "표번": f"SC{_new_code(column)}"}
|
||||
atomic_write_json(path, doc)
|
||||
return True
|
||||
|
||||
|
||||
def _strip_default_head(doc: Any) -> bool:
|
||||
"""옛 빈 산출근거의 1 행 기본 머리(공종 · 규격 · 산출 내역 · 단위 · 수량(m당))를 지움 —
|
||||
다섯 칸이 모두 그 글일 때만(사용자가 고친 머리는 둠) · 머리 서식만 쓰였으면 서식 표도 비움."""
|
||||
sheets = doc.get("시트") if isinstance(doc, dict) else None
|
||||
cells = sheets[0].get("칸") if isinstance(sheets, list) and sheets else None
|
||||
keys = [f"{_col_letters(i)}1" for i in range(len(_BASIS_HEAD))]
|
||||
if not isinstance(cells, dict) or [(cells.get(k) or {}).get("값") for k in keys] != list(
|
||||
_BASIS_HEAD
|
||||
):
|
||||
return False
|
||||
for k in keys:
|
||||
del cells[k]
|
||||
if not any(isinstance(c, dict) and c.get("서식") for s in sheets for c in s["칸"].values()):
|
||||
doc["서식"] = [{}]
|
||||
return True
|
||||
|
||||
|
||||
def prune_removed_structures() -> list[str]:
|
||||
"""집계표에서 지운 열(또는 구조물이 아니게 된 열)의 구조물 도면 · 산출근거를 같이 지움 —
|
||||
"""집계표에서 지운 열(또는 구조물이 아니게 된 열)의 세 파일을 같이 지움 —
|
||||
번호는 비워 둠(다른 열 번호를 안 당김)."""
|
||||
keep = set(_column_ids())
|
||||
gone: list[str] = []
|
||||
with _LOCK:
|
||||
for kind in ("structure", "basis"):
|
||||
for kind in _KEY:
|
||||
for path in sorted((FOLDER / kind).glob("[!.]*.json")):
|
||||
if path.stem not in keep:
|
||||
path.unlink()
|
||||
@@ -357,10 +300,35 @@ def prune_removed_structures() -> list[str]:
|
||||
return gone
|
||||
|
||||
|
||||
def _move_basis() -> list[str]:
|
||||
"""12장 산출근거 `basis/` → 면적산출 `area/` (내용 · 알약 연결 그대로 · 표번 칸 → 면적번 ·
|
||||
시트 이름 산출근거 → 면적산출) · 옮긴 뒤 빈 `basis/` 폴더를 지움."""
|
||||
old = FOLDER / "basis"
|
||||
if not old.is_dir():
|
||||
return []
|
||||
moved: list[str] = []
|
||||
with _LOCK:
|
||||
for path in sorted(old.glob("[!.]*.json")):
|
||||
doc = json.loads(path.read_bytes())
|
||||
doc["면적번"] = doc.pop("표번", None) # 번호는 renumber 가 SA 8자리로
|
||||
for sheet in doc.get("시트") or []:
|
||||
if isinstance(sheet, dict) and sheet.get("이름") == "산출근거":
|
||||
sheet["이름"] = "면적산출"
|
||||
target = _path("area", path.stem)
|
||||
if not target.is_file():
|
||||
atomic_write_json(target, doc)
|
||||
path.unlink()
|
||||
moved.append(path.stem)
|
||||
if not any(old.iterdir()):
|
||||
old.rmdir()
|
||||
return moved
|
||||
|
||||
|
||||
def renumber() -> dict[str, list[str]]:
|
||||
"""한 번 — 옛 번호(구-NN · 표-NN · 없음)를 새 번호로(열 차례 = 종류 · 분류1 · 분류2 차례) ·
|
||||
옛 빈 산출근거 기본 머리 줄 지움 · 구조물 아닌 열 파일 지움 · 빠진 파일 만듦.
|
||||
내용은 그대로 · 새 번호가 이미 있으면 그대로(두 번 돌려도 같음)."""
|
||||
"""한 번 — 옛 산출근거를 면적산출로 옮김 · 옛 번호(6자리 · 구-NN · 표-NN · 없음)를 8자리로
|
||||
(6자리는 끝에 00 · 없는 열 차례 = 종류 · 분류1 · 분류2 · 분류3 차례) ·
|
||||
구조물 아닌 열 파일 지움 · 빠진 파일(재료산출 등) 만듦. 내용은 그대로 · 두 번 돌려도 같음."""
|
||||
moved = _move_basis()
|
||||
gone = prune_removed_structures()
|
||||
changed: list[str] = []
|
||||
made: list[str] = []
|
||||
@@ -371,23 +339,20 @@ def renumber() -> dict[str, list[str]]:
|
||||
for col in cols:
|
||||
if col["id"] not in taken:
|
||||
taken[col["id"]] = _pick_code(col, taken, heads)
|
||||
docs = {"structure": dict(_structures()), "basis": dict(_basis_docs())}
|
||||
docs = {kind: dict(_docs(kind)) for kind in _KEY}
|
||||
for col in cols:
|
||||
cid = col["id"]
|
||||
for kind, key, head in (("structure", "도번", "SD"), ("basis", "표번", "SC")):
|
||||
for kind, key, head, _ in PARTS:
|
||||
number = f"{head}{taken[cid]}"
|
||||
doc = docs[kind].get(cid)
|
||||
if doc is None:
|
||||
blank = _blank_structure(cid) if kind == "structure" else basis_doc(cid)
|
||||
atomic_write_json(_path(kind, cid), {**blank, key: number})
|
||||
atomic_write_json(_path(kind, cid), {**_blank(kind, cid), key: number})
|
||||
made.append(f"{kind}/{cid}")
|
||||
continue
|
||||
stripped = kind == "basis" and _strip_default_head(doc)
|
||||
if doc.get(key) != number or stripped:
|
||||
elif doc.get(key) != number:
|
||||
doc[key] = number
|
||||
atomic_write_json(_path(kind, cid), doc)
|
||||
changed.append(f"{kind}/{cid}")
|
||||
return {"지움": gone, "바꿈": changed, "새로": made}
|
||||
return {"옮김": moved, "지움": gone, "바꿈": changed, "새로": made}
|
||||
|
||||
|
||||
def _server_values(doc: Any) -> dict[str, Any]:
|
||||
@@ -397,36 +362,10 @@ def _server_values(doc: Any) -> dict[str, Any]:
|
||||
raise StoreError(503, str(e)) from e
|
||||
|
||||
|
||||
def migrate_basis() -> dict[str, list[str]]:
|
||||
"""한 번 — 옛 구조물 문서 안 `산출근거` 표를 `basis/<열 id>.json` 으로 옮기고
|
||||
구조물 문서에서 뺌 · 그 뒤 산출근거가 없는 집계표 열은 빈 통합문서.
|
||||
두 번 돌려도 같음(있는 파일은 안 덮음)."""
|
||||
moved: list[str] = []
|
||||
for name, doc in _structures():
|
||||
if not isinstance(doc, dict) or "산출근거" not in doc:
|
||||
continue
|
||||
ensure_basis(name, doc["산출근거"])
|
||||
rest = {key: value for key, value in doc.items() if key != "산출근거"}
|
||||
with _LOCK:
|
||||
atomic_write_json(_path("structure", name), rest)
|
||||
moved.append(name)
|
||||
made = [column for column in _column_ids() if ensure_basis(column)]
|
||||
return {"옮김": moved, "새로": made}
|
||||
|
||||
|
||||
def backfill_missing_structures() -> list[str]:
|
||||
"""구조물 열마다 빈 구조물 도면 · 산출근거가 있게 — 없는 열만 열 차례로 새로
|
||||
만듦(그 무리 다음 번호).
|
||||
그 사이 남이 만들었거나(409) 열 규칙에 안 맞으면(404) 그 열만 건너뜀."""
|
||||
have = {name for name, _ in _structures()}
|
||||
"""구조물 열마다 세 파일이 있게 — 없는 것만 열 차례로 새로 만듦(그 무리 다음 번호).
|
||||
그 사이 남이 만들었으면 그대로 둠."""
|
||||
made: list[str] = []
|
||||
for cid in _column_ids():
|
||||
if cid in have:
|
||||
ensure_basis(cid)
|
||||
continue
|
||||
try:
|
||||
create_structure(cid)
|
||||
made.append(cid)
|
||||
except StoreError:
|
||||
pass
|
||||
made += [f"{kind}/{cid}" for kind in _KEY if _ensure(kind, cid)]
|
||||
return made
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_UI_Side.ts
|
||||
* 마스터 템플릿 좌측 패널 — 시스템 양식 목록(표/도면/구조물 도면/산출근거) · 새로 · 본떠 · 지우기.
|
||||
* 서버 길은 계약 `6_계약.md`. 구조물 도면 · 산출근거 줄은 집계표 구조물 열(꼴=수)마다 하나씩 —
|
||||
* 서버가 미리 만들어 둠(도번 SD · 표번 SC + 같은 6자리) · 줄 글은 번호가 맨 앞(「SD010101 돌기슭막이 …」).
|
||||
* 마스터 템플릿 좌측 패널 — 시스템 양식 목록(표/도면/구조물 도면/면적산출/재료산출) · 새로 · 본떠 · 지우기.
|
||||
* 서버 길은 계약 `6_계약.md`. 구조물 도면 · 면적산출 · 재료산출 줄은 집계표 구조물 열(꼴=수)마다 하나씩 —
|
||||
* 서버가 미리 만들어 둠(도번 SD · 면적번 SA · 재료번 SM + 같은 8자리) ·
|
||||
* 줄 글은 번호가 맨 앞(「SD01010100 돌기슭막이 …」).
|
||||
* ========================================================================== */
|
||||
|
||||
import {
|
||||
@@ -40,6 +41,13 @@ const KINDS: { kind: Kind; label: () => string }[] = [
|
||||
{ kind: "table", label: () => L("M02_KindTable") },
|
||||
{ kind: "drawing", label: () => L("M02_KindDrawing") },
|
||||
];
|
||||
/** 구조물 열마다 한 줄씩인 목록 셋 — 종류 · 번호 칸 · 제목 */
|
||||
type ColumnKind = "structure" | "area" | "material";
|
||||
const COLUMN_KINDS: { kind: ColumnKind; key: keyof StructureNumbers; label: () => string }[] = [
|
||||
{ kind: "structure", key: "도번", label: () => L("M02_KindStructure") },
|
||||
{ kind: "area", key: "면적번", label: () => L("M02_KindArea") },
|
||||
{ kind: "material", key: "재료번", label: () => L("M02_KindMaterial") },
|
||||
];
|
||||
/** 새 양식의 첫 문서 — 표는 `3_표양식.md` 4장 틀 · 도면은 빈 웹캐드 문서 */
|
||||
function stubDoc(kind: Kind, name: string): unknown {
|
||||
return kind === "table"
|
||||
@@ -102,15 +110,15 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
const bodies = new Map<Kind, HTMLElement>(
|
||||
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 columnBodies = COLUMN_KINDS.map(() =>
|
||||
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),
|
||||
...COLUMN_KINDS.map((k, i) => section(k.label(), columnBodies[i])),
|
||||
],
|
||||
});
|
||||
attachCollapsible(listHost);
|
||||
@@ -158,14 +166,12 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/** 구조물 도면 · 산출근거 줄 — 구조물 열마다 하나 · 「번호 이름 변수값」(번호가 없으면 「없음」) */
|
||||
function paintRows(body: HTMLElement, kind: "structure" | "basis"): void {
|
||||
/** 구조물 도면 · 면적산출 · 재료산출 줄 — 구조물 열마다 하나 · 「번호 이름 변수값」(번호가 없으면 「없음」) */
|
||||
function paintRows(body: HTMLElement, kind: ColumnKind, key: keyof StructureNumbers): void {
|
||||
body.replaceChildren(
|
||||
...(columns.length
|
||||
? columns.map((col) => {
|
||||
const num =
|
||||
(kind === "structure" ? numbers[col.id]?.도번 : numbers[col.id]?.표번) ||
|
||||
L("M02_StructureNone");
|
||||
const num = numbers[col.id]?.[key] || L("M02_StructureNone");
|
||||
const b = el("button", {
|
||||
className: `m02-side__item ui-sidebar-row${isPicked(kind, col.id) ? " is-active" : ""}`,
|
||||
text: structureLabel(num, col),
|
||||
@@ -177,8 +183,8 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
: [el("p", { className: "m02-side__empty", text: L("M02_NoTemplates") })]),
|
||||
);
|
||||
}
|
||||
const paintStructure = (): void => paintRows(structureBody, "structure");
|
||||
const paintBasis = (): void => paintRows(basisBody, "basis");
|
||||
const paintColumns = (): void =>
|
||||
COLUMN_KINDS.forEach((k, i) => paintRows(columnBodies[i], k.kind, k.key));
|
||||
|
||||
function paintButtons(): void {
|
||||
const editable = canEdit(layer, opt.isAdmin);
|
||||
@@ -195,13 +201,12 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
paintButtons();
|
||||
}
|
||||
|
||||
async function selectColumn(col: StructureColumn, kind: "structure" | "basis"): Promise<void> {
|
||||
async function selectColumn(col: StructureColumn, kind: ColumnKind): Promise<void> {
|
||||
if (!isPicked(kind, col.id) && !(await opt.confirmLeave())) return;
|
||||
picked = null;
|
||||
// 제목은 편집 화면이 문서 번호를 앞에 붙임(「SD010101 돌기슭막이 …」)
|
||||
opt.onOpen({ layer, projectId: null, kind, name: col.id, label: columnHead(col) });
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
paintColumns();
|
||||
paintButtons();
|
||||
}
|
||||
|
||||
@@ -219,8 +224,7 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
numbers = {};
|
||||
showToast(L("M02_LoadFailed").replace("{value}", why(error)), "error");
|
||||
}
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
paintColumns();
|
||||
}
|
||||
|
||||
async function refresh(): Promise<void> {
|
||||
@@ -320,8 +324,7 @@ export function buildSide(opt: SideOptions): SideHandle {
|
||||
select: (kind, name) => {
|
||||
picked = { kind, name };
|
||||
paintList();
|
||||
paintStructure();
|
||||
paintBasis();
|
||||
paintColumns();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""M02 양식 층 — 네 층의 자리 · 읽기 · 쓰기 · 복사 · manifest.
|
||||
|
||||
층 넷 (PLAN 10-5):
|
||||
system `resources/master_template/{table,drawing,structure,basis}/<이름>.json` (git)
|
||||
company `storage/{회사}/templates/{table,drawing,structure,basis}/`
|
||||
personal `storage/{회사}/{사용자}/templates/{table,drawing,structure,basis}/`
|
||||
project `storage/{회사}/{사용자}/{프로젝트}/templates/{table,drawing,structure,basis}/`
|
||||
system `resources/master_template/{table,drawing,structure,area,material}/<이름>.json` (git)
|
||||
company `storage/{회사}/templates/{table,drawing,structure,area,material}/`
|
||||
personal `storage/{회사}/{사용자}/templates/{table,drawing,structure,area,material}/`
|
||||
project `storage/{회사}/{사용자}/{프로젝트}/templates/{…같은 다섯}/`
|
||||
+ 초기 사본 `templates/_initial/`
|
||||
|
||||
- 층 폴더마다 `manifest.json` — 그 폴더에 든 양식의 출처 `{"table/이름": {층, 이름, 판, 적용일}}`.
|
||||
@@ -26,8 +26,8 @@ from common_util.common_util_json import atomic_write_json
|
||||
from config import config_system
|
||||
|
||||
LAYERS = ("system", "company", "personal", "project")
|
||||
# basis = 산출근거 통합문서(구조물집계표 열마다 · 스프레드시트 `A00_Common/spreadsheet/`)
|
||||
KINDS = ("table", "drawing", "structure", "basis")
|
||||
# area = 면적산출 · material = 재료산출 — 통합문서(구조물집계표 열마다 · `A00_Common/spreadsheet/`)
|
||||
KINDS = ("table", "drawing", "structure", "area", "material")
|
||||
TEMPLATES_DIRNAME = "templates"
|
||||
INITIAL_DIRNAME = "_initial"
|
||||
MANIFEST_NAME = "manifest.json"
|
||||
@@ -167,7 +167,8 @@ _SKELETON = {
|
||||
"table": ("열",),
|
||||
"drawing": ("entities",),
|
||||
"structure": ("도면.entities",),
|
||||
"basis": ("시트",),
|
||||
"area": ("시트",),
|
||||
"material": ("시트",),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC140000"
|
||||
"면적번": "SA14000000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC220000"
|
||||
"면적번": "SA22000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC120102"
|
||||
"면적번": "SA12010200"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC120101"
|
||||
"면적번": "SA12010100"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC050101"
|
||||
"면적번": "SA05010100"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC050102"
|
||||
"면적번": "SA05010200"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC010102"
|
||||
"면적번": "SA01010200"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC240000"
|
||||
"면적번": "SA24000000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC130100"
|
||||
"면적번": "SA13010000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC250100"
|
||||
"면적번": "SA25010000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC060100"
|
||||
"면적번": "SA06010000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC070102"
|
||||
"면적번": "SA07010200"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC070101"
|
||||
"면적번": "SA07010100"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC190000"
|
||||
"면적번": "SA19000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC180000"
|
||||
"면적번": "SA18000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC200000"
|
||||
"면적번": "SA20000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC350000"
|
||||
"면적번": "SA35000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC330000"
|
||||
"면적번": "SA33000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC260000"
|
||||
"면적번": "SA26000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC310000"
|
||||
"면적번": "SA31000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC300000"
|
||||
"면적번": "SA30000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC270100"
|
||||
"면적번": "SA27010000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC270200"
|
||||
"면적번": "SA27020000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC340000"
|
||||
"면적번": "SA34000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC320000"
|
||||
"면적번": "SA32000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC290000"
|
||||
"면적번": "SA29000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC280000"
|
||||
"면적번": "SA28000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC360000"
|
||||
"면적번": "SA36000000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC210000"
|
||||
"면적번": "SA21000000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC100101"
|
||||
"면적번": "SA10010100"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC080102"
|
||||
"면적번": "SA08010200"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC080101"
|
||||
"면적번": "SA08010100"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pg_in",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"계산값": {},
|
||||
"면적번": "SA04010100"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC030102"
|
||||
"면적번": "SA03010200"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC030101"
|
||||
"면적번": "SA03010100"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC230000"
|
||||
"면적번": "SA23000000"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC020104"
|
||||
"면적번": "SA02010400"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC020101"
|
||||
"면적번": "SA02010100"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC020105"
|
||||
"면적번": "SA02010500"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC020102"
|
||||
"면적번": "SA02010200"
|
||||
}
|
||||
+2
-2
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC020103"
|
||||
"면적번": "SA02010300"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC150000"
|
||||
"면적번": "SA15000000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC010101"
|
||||
"면적번": "SA01010100"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC090101"
|
||||
"면적번": "SA09010100"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC110101"
|
||||
"면적번": "SA11010100"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC170000"
|
||||
"면적번": "SA17000000"
|
||||
}
|
||||
@@ -8,10 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "면적산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC160000"
|
||||
"면적번": "SA16000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "be",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM14000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "bg",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM22000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "bm_area",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM12010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "bm_len",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM12010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "bx_len",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM05010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "bx_wing",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM05010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "c1",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM01010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "cs",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM24000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "ec",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM13010000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "etc",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM25010000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "fb",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM06010000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "fp_l",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM07010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "fp_w",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM07010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "gr_curb",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM19000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "gr_rail",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM18000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "gr_sign",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM20000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_break",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM35000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_ford_basin",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM33000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_intake",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM26000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_pg_rail",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM31000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_pv_rail",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM30000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_rip_c",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM27010000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_rip_m",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM27020000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_rockfall",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM34000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_soil_ditch",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM32000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_stone_bed",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM29000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_stone_ch",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM28000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "h_waste",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM36000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "mr",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM21000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "ms",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM10010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "od_cnt",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM08010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "od_len",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM08010100"
|
||||
}
|
||||
+2
-3
@@ -8,11 +8,10 @@
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "산출근거",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"표번": "SC040101",
|
||||
"계산값": {}
|
||||
"재료번": "SM04010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pp_cp",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM03010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pp_len",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM03010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "ps",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM23000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pv_a",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM02010400"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pv_b",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM02010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pv_jt",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM02010500"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pv_l",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM02010200"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "pv_w",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM02010300"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "rf",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM15000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "rv",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM01010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "rw",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM09010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "sg",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM11010100"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "ta",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM17000000"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"종류": "통합문서",
|
||||
"판": 1,
|
||||
"열": "wy",
|
||||
"서식": [
|
||||
{}
|
||||
],
|
||||
"시트": [
|
||||
{
|
||||
"id": "s1",
|
||||
"이름": "재료산출",
|
||||
"칸": {}
|
||||
}
|
||||
],
|
||||
"활성": "s1",
|
||||
"재료번": "SM16000000"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user