merge(M01): origin/sub_laptop_7 받음 — 로직 지우기 검사 두 벌을 sub4 한 벌로(labor_errors · 실무문서 · 알림 글)
- sub7 의 LABOR_ROOTS · 겹친 _labor_files · labor_errors 지움 · sub7 시험은 층 자리(layers.SYSTEM_ROOT) · 알림 글만 맞춤 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
This commit is contained in:
@@ -141,8 +141,9 @@ def copy_item(
|
||||
origin: dict[str, Any],
|
||||
path: list[str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""다른 층 항목(SS `key`) → `target` 층 새 열 하나 — 머리 · 단위 · 일위대가 · 바인딩 그대로 ·
|
||||
도면 · 면적산출 · 재료산출 내용(알약 연결 포함) · 항목 연결 · 자재 조합 줄(새 UM) 복사 ·
|
||||
"""다른 층 항목(SS `key`) → `target` 층 새 열 하나 — 머리 · 단위 · 바인딩 그대로 ·
|
||||
도면 · 일위대가(새 SU · 조합 키는 안 받음 · 39-1b) · 면적산출 · 재료산출 내용(알약 연결 포함) ·
|
||||
항목 연결 · 자재 조합 줄(새 UM) 복사 ·
|
||||
새 번호 · 새 SS 키 ·
|
||||
열 `본뜬` = `origin`(원래 층 · 주인 · 키) · `path` = 새 자리(폴더 + 이름 · 없으면 원래 경로 ·
|
||||
붙여넣기 PLAN 30-2). 돌려줌 `{key, 열, 이름}`."""
|
||||
@@ -179,7 +180,13 @@ def copy_item(
|
||||
table = store.read("table", store.STRUCTURE_TABLE)
|
||||
doc = table["문서"]
|
||||
new_id = _fresh_id(col_id, {c.get("id") for c in doc.get("열", []) if isinstance(c, dict)})
|
||||
made = {**json.loads(json.dumps(col)), "id": new_id, "자재조합": new_um, "본뜬": origin}
|
||||
made = {
|
||||
**json.loads(json.dumps(col)),
|
||||
"id": new_id,
|
||||
"일위대가": None, # 정본 = 일위대가 문서(아래 복사)
|
||||
"자재조합": new_um,
|
||||
"본뜬": origin,
|
||||
}
|
||||
if path:
|
||||
made.update({"경로": list(path), "머리": list(path)}) # 머리 줄 수는 저장 맞춤이 채움
|
||||
doc["열"] = [*doc.get("열", []), made]
|
||||
@@ -192,6 +199,7 @@ def copy_item(
|
||||
body = store._server(kind, body)
|
||||
with store._LOCK:
|
||||
atomic_write_json(store._path(kind, new_id), body)
|
||||
store._refresh_labor(new_id) # 면적이 일위대가 뒤에 서므로 칸 값으로 다시
|
||||
ss = next(r for r in store.read_combos()["줄"] if r.get("열") == new_id)
|
||||
inputs_store.copy_between(source, target, col_id, new_id) # 항목 연결도 같이(판 1)
|
||||
return {"key": ss["키"], "열": new_id, "이름": ss["이름"]}
|
||||
|
||||
@@ -400,18 +400,36 @@ def post_use_combo(
|
||||
return _call(folder, branch.use_combo, column, body.종류, body.키, body.판)
|
||||
|
||||
|
||||
def _copy_practice(column: str, key: str) -> dict[str, Any]:
|
||||
"""실무문서 조합(마스터 UA) → 이 열 일위대가 문서(입력값 빈 값 · 39-1b)."""
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
|
||||
if not key.startswith("UA"):
|
||||
raise store.StoreError(400, f"실무문서 조합 키가 아님 「{key}」")
|
||||
return store.copy_from("labor", column, key, body=labor.from_combo(key))
|
||||
|
||||
|
||||
@router.post("/layers/{layer}/{kind}/{column}/copy")
|
||||
def post_copy(
|
||||
layer: Layer,
|
||||
kind: Literal["structures", "area", "material"],
|
||||
kind: Literal["structures", "labor", "area", "material"],
|
||||
column: str,
|
||||
source: str = Query(alias="from"),
|
||||
owner: str | None = None,
|
||||
from_layer: Literal["system", "company", "personal", "practice"] | None = None,
|
||||
from_owner: str | None = None,
|
||||
session: Session = Depends(verify_session),
|
||||
):
|
||||
"""항목 하나 복사해 가져오기(덮어씀 · 번호 · 열 그대로) — 원본 = 같은 층(`from_layer` 없음) ·
|
||||
다른 층(원본 읽기 권한) · 실무문서 조합(`practice` · 일위대가만 · 시스템 관리자만)."""
|
||||
folder = _root(session, layer, owner, write=True)
|
||||
part = "structure" if kind == "structures" else kind
|
||||
return _call(folder, store.copy_from, part, column, source)
|
||||
if from_layer == "practice":
|
||||
if part != "labor" or not access.is_system_admin(session):
|
||||
raise HTTPException(status_code=403, detail="실무문서는 시스템 관리자가 일위대가로만")
|
||||
return _call(folder, _copy_practice, column, source)
|
||||
origin = _root(session, from_layer, from_owner) if from_layer else None
|
||||
return _call(folder, store.copy_from, part, column, source, origin)
|
||||
|
||||
|
||||
@router.get("/layers/{layer}/material-combos")
|
||||
|
||||
@@ -186,6 +186,7 @@ def write(kind: str, name: str, version: str, doc: Any, screen: bool = False) ->
|
||||
for column, source in origins.items(): # 빈 파일 채우기 앞 — 원본 내용으로 만듦
|
||||
for part in _KEY:
|
||||
_ensure(part, column, source)
|
||||
_refresh_labor(column) # 면적이 일위대가 뒤에 서므로 칸 값으로 다시
|
||||
_copy_inputs(source, column)
|
||||
backfill_missing_structures() # 지우기 앞 — 같은 저장의 새 열이 지운 번호를 안 받게
|
||||
prune_removed_structures()
|
||||
@@ -335,7 +336,8 @@ def _ensure(kind: str, column: str, source: str | None = None) -> bool:
|
||||
|
||||
def _take_origins(doc: Any) -> dict[str, str]:
|
||||
"""표 열의 `본뜬열` 을 떼어 냄 → `{새 열: 원본 열}` (구조물 파일이 아직 없는 열만) ·
|
||||
그 열에 일위대가 · 자재조합 키가 비었으면 원본 열 것을 받음."""
|
||||
그 열에 자재조합 키가 비었으면 원본 열 것을 받음 · 일위대가는 원본 열 문서를 새 번호로 복사
|
||||
(`_ensure` · 39-1b — 조합 키는 안 받음)."""
|
||||
cols = (
|
||||
[c for c in (doc.get("열") or []) if isinstance(c, dict)] if isinstance(doc, dict) else []
|
||||
)
|
||||
@@ -348,8 +350,7 @@ def _take_origins(doc: Any) -> dict[str, str]:
|
||||
if (root() / "structure" / f"{col['id']}.json").is_file():
|
||||
continue
|
||||
out[col["id"]] = source
|
||||
for field in ("일위대가", "자재조합"):
|
||||
col[field] = col.get(field) or by_id[source].get(field)
|
||||
col["자재조합"] = col.get("자재조합") or by_id[source].get("자재조합")
|
||||
return out
|
||||
|
||||
|
||||
@@ -365,17 +366,32 @@ def create_structure(column: str) -> dict[str, Any]:
|
||||
return read("structure", column)
|
||||
|
||||
|
||||
def copy_from(kind: str, column: str, source: str) -> dict[str, Any]:
|
||||
def copy_from(
|
||||
kind: str,
|
||||
column: str,
|
||||
source: str,
|
||||
source_root: Path | None = None,
|
||||
body: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""다른 열(`source`) 내용을 받는 열(`column`)로 가져옴 — 번호 · 열은 받는 쪽 그대로(두 열은
|
||||
따로 움직임) · 통합문서는 알약 `연결` 도 같이 오고 계산값은 서버가 다시 풂."""
|
||||
따로 움직임) · 통합문서는 알약 `연결` 도 같이 오고 계산값은 서버가 다시 풂 ·
|
||||
`source_root` = 원본 층 폴더(없으면 지금 층 · 권한은 부르는 쪽) · `body` = 원본 문서를 바로 줌
|
||||
(실무문서 조합 → 일위대가 · 39-1b)."""
|
||||
if kind not in _KEY:
|
||||
raise StoreError(404, f"없는 종류 「{kind}」")
|
||||
target = read(kind, column)["문서"]
|
||||
doc = {**read(kind, source)["문서"], **{k: target.get(k) for k in ("열", _KEY[kind])}}
|
||||
if body is None and source_root is not None:
|
||||
with at(source_root):
|
||||
body = read(kind, source)["문서"]
|
||||
body = read(kind, source)["문서"] if body is None else body
|
||||
doc = {**body, **{k: target.get(k) for k in ("열", _KEY[kind])}}
|
||||
doc = _server(kind, {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))
|
||||
info = _info(kind, _path(kind, column))
|
||||
if kind == "area":
|
||||
_refresh_labor(column)
|
||||
return info
|
||||
|
||||
|
||||
def _copy_inputs(source: str, column: str) -> None:
|
||||
|
||||
Reference in New Issue
Block a user