별표2 (5)(가) 열셋째. 근거는 줄마다 이미 있었고 묶는 자리만 없었음. 원가계산에 「산출기초」 탭으로 세움. 넷으로 접음 — ① 어느 판으로 계산했나(데이터 기준일·지문) ② 무엇을 골랐나(고른 값만) ③ 공종마다 무엇을 근거로 했나(줄 문구 그대로) ④ 못 채운 자리(0 으로 안 때운 자리). - 판 목록은 장부(_manifest.json)를 그대로 읽음 — 목록을 따로 적으면 한쪽만 고쳐짐. - ⚠ 여기서 값을 다시 계산하지 않음 — 금액 칸이 아예 없음(두 벌 방지, 시험으로 못 박음). ⇒ 「설계서 구성」의 산출기초가 반쪽 → 있음. 법이 정한 13 중 9 가 서고, 우리가 더 낼 것은 공사설명서 하나(서식·설계하중 표기는 사용자에게 받아야 함). 곁들여: 자재총괄의 「미분류」를 「미정(발주기관 결정)」으로 고침 — 발주기관이 정할 자리인데 우리가 못 만든 것처럼 읽히던 문구(V-14). 값·판정은 그대로. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
103 lines
3.7 KiB
Python
103 lines
3.7 KiB
Python
"""설계서 구성표 — 법이 정한 목차와 우리가 내는 것 (2026-09-09).
|
|
|
|
산림자원법 시행규칙 [별표2] (5)(가) 가 **설계서 작성 순서를 열셋으로 못 박아** 두었다.
|
|
그 목록과 우리 화면을 맞대 두지 않으면 「무엇을 못 내는가」가 계획서에만 남는다.
|
|
|
|
⚠ 겨누는 것 넷
|
|
① 법이 적은 **차례 그대로**다 — 순서를 우리가 바꾸지 않는다
|
|
② **「없음」과 「우리 몫 아님」을 가른다** — 시방서·예정공정표는 설계자가 쓰는 문서다
|
|
③ 「반쪽」은 반쪽이라 적는다 — 중기경비계산서·산출기초
|
|
④ 못 내는 것에는 **무엇을 받아야 내는지**가 적혀 있다
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B09_Estimation.B09_Estimation_DesignDocIndex import ( # noqa: E402
|
|
DESIGN_DOC_ITEMS,
|
|
OWNER_DESIGNER,
|
|
OWNER_PROGRAM,
|
|
STATUS_MISSING,
|
|
STATUS_PARTIAL,
|
|
STATUS_READY,
|
|
design_doc_index,
|
|
)
|
|
|
|
#: 별표2 (5)(가) 원문 차례.
|
|
_LAW_ORDER = (
|
|
"목차",
|
|
"공사설명서",
|
|
"일반시방서",
|
|
"특별시방서",
|
|
"예정공정표",
|
|
"예산내역서",
|
|
"일위대가표",
|
|
"단가산출서",
|
|
"각종 중기경비계산서",
|
|
"공종별 수량계산서",
|
|
"각종 소요자재총괄표",
|
|
"토적표",
|
|
"산출기초",
|
|
)
|
|
|
|
|
|
def test_법이_적은_차례_그대로다() -> None:
|
|
assert tuple(item["name"] for item in DESIGN_DOC_ITEMS) == _LAW_ORDER
|
|
assert [item["order"] for item in DESIGN_DOC_ITEMS] == list(range(1, 14))
|
|
|
|
|
|
def test_설계자_몫과_우리_몫을_가른다() -> None:
|
|
"""② 「없음」 넷 가운데 셋은 **애초에 우리가 만들 것이 아니다.**"""
|
|
theirs = {item["name"] for item in DESIGN_DOC_ITEMS if item["owner"] == OWNER_DESIGNER}
|
|
assert theirs == {"일반시방서", "특별시방서", "예정공정표"}
|
|
for item in DESIGN_DOC_ITEMS:
|
|
if item["owner"] == OWNER_DESIGNER:
|
|
assert item["status"] == STATUS_MISSING
|
|
|
|
|
|
def test_반쪽이_남지_않았다() -> None:
|
|
"""⚠ 2026-09-09 밤 — 중기경비계산서·산출기초를 차례로 세우며 **반쪽이 다 없어졌다.**
|
|
|
|
이제 우리 몫 가운데 「없음」은 공사설명서 하나이고, 그것은 **서식·설계하중 표기를
|
|
사용자에게 받아야** 서는 자리다.
|
|
"""
|
|
partial = {item["name"] for item in DESIGN_DOC_ITEMS if item["status"] == STATUS_PARTIAL}
|
|
assert partial == set()
|
|
ours_missing = {
|
|
item["name"]
|
|
for item in DESIGN_DOC_ITEMS
|
|
if item["owner"] == OWNER_PROGRAM and item["status"] != STATUS_READY
|
|
}
|
|
assert ours_missing == {"공사설명서"}
|
|
|
|
|
|
def test_서는_것은_어디서_나오는지_적혀_있다() -> None:
|
|
for item in DESIGN_DOC_ITEMS:
|
|
if item["status"] == STATUS_READY:
|
|
assert item["where"], f"{item['name']}: 어느 화면인지 안 적혀 있음"
|
|
|
|
|
|
def test_못_내는_것에는_무엇이_필요한지_적혀_있다() -> None:
|
|
"""④ 「없음」으로만 두면 다음 사람이 무엇부터 해야 할지 모른다."""
|
|
for item in DESIGN_DOC_ITEMS:
|
|
if item["status"] != STATUS_READY:
|
|
assert item["note"].strip(), f"{item['name']}: 사유가 비어 있음"
|
|
|
|
|
|
def test_요약이_셈과_맞는다() -> None:
|
|
data = design_doc_index()
|
|
assert data["counts"][STATUS_READY] == 9
|
|
assert sum(data["counts"].values()) == len(DESIGN_DOC_ITEMS)
|
|
ours_missing = [
|
|
item["name"]
|
|
for item in DESIGN_DOC_ITEMS
|
|
if item["owner"] == OWNER_PROGRAM and item["status"] != STATUS_READY
|
|
]
|
|
for name in ours_missing:
|
|
assert name in data["summary"]
|