무슨 일이 있었나 랩탑 줄의 병합 `20ba886c`(Merge origin/main_desktop_1·main_laptop_1·sub_desktop_1 into sub_laptop_1)가 우리 파일 22개를 떨구고 35개 파일의 내용을 옛것으로 되돌림. 손으로 지운 커밋은 없고 **병합 자체가 떨군 것**임. 그것이 `origin/dev`·`main_laptop_1`·`sub_laptop_1`· `CODEX` 까지 퍼졌고(데스크탑 둘만 무사), 이 창의 병합 `d92c1f2b` 로 들어옴. 잃었던 것 - 공용 — `common_util_provenance.py` · `ui_template_provenance.ts` - B08 — 근거 사전 · 좌측 패널 상자 모듈 · 토량환산계수 칸 - B09 — 근거 사전 셋 - B05 — 계획노선 편집 모듈 아홉 · 지형 라우터 · B04 지도 모듈 - 시험 셋과, 35개 파일 안의 최근 작업(환산계수 고르기 · 근거 호버 배선 등) 어떻게 되살렸나 `611a2b40`(병합 직전, 전부 온전)에서 `git show <커밋>:<경로>` 로 내용만 꺼내 되돌림. 이력은 안 건드림. ⚠ HEAD 에만 있던 「추가 816줄」은 랩탑의 새 작업이 아니라 **되살아난 옛 코드**였음(B05 편집은 모듈로 쪼개기 전 덩어리 · B08 라우터는 환산계수 고르기 전 옛 상수판). 되돌릴 시점 이후의 **진짜 새 커밋은 둘뿐**이라 그 둘만 패치로 다시 얹음 — `9f827bf6`(리로드 빌드 고리 끊기, 데스크탑 보조) · `b9bca6b3`(B06 조정창 1px, 랩탑). 위키 여덟은 코덱스 몫이라 손대지 않음. 자체검증 — 양쪽 작업이 다 살아 있음을 짚어 확인: `main.py` 의 「개발 서버는 살려 둔다」 · `B05_Profile_Engine_Grade.py` 의 `plan_curve_length_limit_m` · `B08_..._EarthworkGrid.ts` 의 `attachProvenance`. `tsc --noEmit` 통과 · `pytest -q` **1317 passed, 28 skipped** (되살리기 전에는 시험 둘이 수집 단계에서 깨져 있었음). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RANEBHns1S4tkmsYwewtk
269 lines
12 KiB
Python
269 lines
12 KiB
Python
"""B09 **바깥에서 온 값**과 **모으는 표**의 근거 사전 (PLAN 8-36 ④).
|
|
|
|
왜 파일을 갈랐나
|
|
`B09_Estimation_Provenance.py` 가 700줄을 넘었다(CLAUDE.md 4장). 가르는 금은
|
|
**값을 낳는 장**과 **값이 바깥에서 오거나 모으기만 하는 장**이다 —
|
|
· 저쪽: 원가계산서·내역서·일위대가·자재대·중기·기초자료 목록표
|
|
· 이쪽: 자재단가대비표·환율및기초자료·단가산출근거·산출기초
|
|
이쪽 넷은 식이 얇거나 아예 없고, 대신 **「어느 판에서 왔나」**가 알맹이다.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from common_util.common_util_provenance import (
|
|
TIER_BLOCKED,
|
|
TIER_CALC,
|
|
TIER_INPUT,
|
|
TIER_STANDARD,
|
|
TIER_UNCLASSIFIED,
|
|
ColumnProvenance,
|
|
sheet_provenance,
|
|
)
|
|
from B09_Estimation.B09_Estimation_Provenance_Common import _label, _note_column
|
|
|
|
|
|
# =============================================================================
|
|
# ⑥ 자재단가대비표 — **원천 다섯 중 하나를 고르는** 자리
|
|
# =============================================================================
|
|
|
|
|
|
def material_comparison_sheet() -> dict[str, Any]:
|
|
"""실무 시트의 「기.가 · 유.물 · 견적」 약호가 곧 채택 근거다.
|
|
|
|
⚠ 원천 칸이 **여럿이라도 뜻은 하나**다(어느 판이 얼마라 했나). 슬롯마다 열 키를
|
|
따로 두면 사전이 다섯 벌이 되고 판이 늘 때마다 어긋난다 — 한 키를 나눠 쓴다.
|
|
"""
|
|
return sheet_provenance(
|
|
[
|
|
_label("code", "코드번호"),
|
|
_label("name", "명 칭"),
|
|
_label("spec", "규 격"),
|
|
_label("unit", "단위"),
|
|
ColumnProvenance(
|
|
key="slot_price",
|
|
label="원천 단가",
|
|
tier=TIER_STANDARD,
|
|
formula="그 판이 적어 놓은 값을 그대로 옮김",
|
|
source="물가지·거래가격·업체 견적 등 판마다 다름 — 오른쪽 「페이지」 칸이 "
|
|
"어느 쪽에서 왔는지 가리킨다",
|
|
rule="⚠ **빈 칸은 「0원」이 아니라 「그 판에 그 품목이 없다」** — "
|
|
"0 으로 때우지 않는다",
|
|
code="B09_Estimation_Lists_Sources.py:79 material_price_comparison",
|
|
),
|
|
ColumnProvenance(
|
|
key="slot_page",
|
|
label="페이지",
|
|
tier=TIER_STANDARD,
|
|
formula="그 값이 실린 쪽수·출처 표기",
|
|
source="실무 시트의 「기.가 1,024」 같은 약호 — 사람이 원문을 찾아갈 열쇠",
|
|
code="B09_Estimation_Lists_Sources.py:79 material_price_comparison",
|
|
),
|
|
ColumnProvenance(
|
|
key="adopted_price_krw",
|
|
label="적 용",
|
|
tier=TIER_CALC,
|
|
formula="원천 다섯 중 **채택한 하나**의 값",
|
|
source="채택한 칸은 표에서 굵게 선다 — 표가 스스로 「어느 값을 썼나」를 밝힌다",
|
|
rule="⚠ **값 안에 고름이 숨은 열**(PLAN 8-36 ㉯). 실무 약호가 곧 채택 규칙이다 "
|
|
"— 「기.가」(정부 기준가격) · 「유.물」(물가정보) · 「견적」(업체). "
|
|
"슬롯 6 이 곧 적용 단가라 그 자리에 값이 서면 「적용」 칸을 따로 안 세운다",
|
|
code="B09_Estimation_Lists_Sources.py:79 material_price_comparison",
|
|
),
|
|
_note_column("B09_Estimation_Lists_Sources.py:79"),
|
|
]
|
|
)
|
|
|
|
|
|
# =============================================================================
|
|
# ⑦ 환율및기초자료 — **바깥에서 온 값**과 **우리가 고른 것**이 갈리는 장
|
|
# =============================================================================
|
|
|
|
|
|
def base_reference_labor_sheet() -> dict[str, Any]:
|
|
"""② 인건비 — 공표 노임을 시간당으로 푼 표."""
|
|
return sheet_provenance(
|
|
[
|
|
_label("code", "코드번호"),
|
|
_label("name", "직 종"),
|
|
ColumnProvenance(
|
|
key="day_wage_krw",
|
|
label="일 당",
|
|
tier=TIER_STANDARD,
|
|
formula="공표 노임을 그대로 옮김",
|
|
source="대한건설협회 시중노임 공표값. ⚠ **기본급여액뿐**이라 제수당·상여금·"
|
|
"퇴직급여충당금은 따로 계상해야 한다",
|
|
rule="표본이 얇은 직종(조사현장 5곳 미만 `*` · 미조사 `**`)은 금액은 서되 "
|
|
"그 사실을 화면이 따로 알린다",
|
|
code="B09_Estimation_Lists_Sources.py:137 base_reference_data",
|
|
),
|
|
ColumnProvenance(
|
|
key="hourly_krw",
|
|
label="시간당",
|
|
tier=TIER_CALC,
|
|
formula="일당 ÷ 8시간",
|
|
source="⚠ **나눈 값을 자르지 않고 그대로 둔다** — 여기서 원 단위로 자르면 "
|
|
"기계 시간당 사용료가 조금씩 어긋난다. 자르는 자리는 일위대가·내역서 쪽",
|
|
code="B09_Estimation_Lists_Sources.py:137 base_reference_data",
|
|
),
|
|
ColumnProvenance(
|
|
key="formula",
|
|
label="산 식",
|
|
tier=TIER_CALC,
|
|
formula="그 줄이 실제로 쓴 셈을 사람이 읽게 적은 한 줄",
|
|
source="서버가 값과 같이 지음 — 화면이 따로 짓지 않는다",
|
|
code="B09_Estimation_Lists_Sources.py:137 base_reference_data",
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def base_reference_fuel_sheet() -> dict[str, Any]:
|
|
"""③ 단가 및 재료비 — 유가 한 줄. 지역을 고르면 기계 연료비가 다시 선다."""
|
|
return sheet_provenance(
|
|
[
|
|
_label("item", "항 목"),
|
|
ColumnProvenance(
|
|
key="price_krw",
|
|
label="단 가",
|
|
tier=TIER_STANDARD,
|
|
formula="공시가를 그대로 옮김 (전국 또는 고른 시도)",
|
|
source="한국석유공사 공시가. 이 값이 바뀌면 **기계 연료비가 통째로 다시 선다**",
|
|
code="B09_Estimation_Lists_Sources.py:137 base_reference_data",
|
|
),
|
|
ColumnProvenance(
|
|
key="scope",
|
|
label="적용 범위",
|
|
tier=TIER_INPUT,
|
|
formula="사용자가 고른 유가 범위 (전국 공시가 또는 현장 시도)",
|
|
source="안 고르면 전국 공시가로 선다 — 고른 적이 없다는 뜻이지 "
|
|
"「전국이 맞다」는 뜻이 아니다",
|
|
rule="⚠ **자료가 없는 시도는 고를 수 없게 둔다.** 고르게만 해 두고 값이 없으면 "
|
|
"조용히 틀린 값이 선다",
|
|
code="B09_Estimation_Lists_Sources.py:44 fuel_scopes",
|
|
),
|
|
ColumnProvenance(
|
|
key="effective_date",
|
|
label="기준일",
|
|
tier=TIER_STANDARD,
|
|
formula="그 공시가의 기준일",
|
|
source="어느 판으로 섰는지는 산출기초 ① 에 지문까지 남는다",
|
|
code="B09_Estimation_Lists_Sources.py:137 base_reference_data",
|
|
),
|
|
_label("dataset_id", "자료", "판 이름 — 산출기초 ① 의 같은 낱말"),
|
|
]
|
|
)
|
|
|
|
|
|
# =============================================================================
|
|
# ⑧ 모으는 표 둘 — **값을 낳지 않는다.** 식은 비우고 「어디서 왔나」만 적는다
|
|
# =============================================================================
|
|
|
|
|
|
def price_basis_sheet() -> dict[str, Any]:
|
|
"""단가산출근거 목록 — 내역서 줄의 단가가 어느 일위대가에서 왔는지.
|
|
|
|
⚠ **여기서 값을 다시 셈하지 않는다.** 그래서 `formula` 를 비웠다 — 없는 식을
|
|
지어 적으면 읽는 사람이 「여기서 계산한다」고 잘못 읽는다.
|
|
"""
|
|
return sheet_provenance(
|
|
[
|
|
ColumnProvenance(
|
|
key="number",
|
|
label="번호",
|
|
tier=TIER_STANDARD,
|
|
source="내역서에 **쓰인 차례**대로 매긴 번호 — 실무 참조번호(「단산 46」)가 "
|
|
"곧 이 번호다",
|
|
code="B09_Estimation_PriceBasis.py:74 build_price_basis",
|
|
),
|
|
_label("name", "공종", "공종 이름 + 규격"),
|
|
_label("unit", "단위"),
|
|
ColumnProvenance(
|
|
key="unit_price_krw",
|
|
label="단가",
|
|
tier=TIER_CALC,
|
|
source="한 층 아래 일위대가 본표의 합계를 **그대로 옮긴 값** — 그 표는 "
|
|
"일위대가 탭에서 본다(본문 「참조」가 그 코드를 가리킨다)",
|
|
code="B09_Estimation_PriceBasis.py:74 build_price_basis",
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def basis_sheet_tables() -> dict[str, dict[str, Any]]:
|
|
"""산출기초 네 표. **모으기만 하는 장**이라 식이 없다.
|
|
|
|
⚠ 이 장이 답하는 물음은 「이 값이 얼마인가」가 아니라 **「어느 판·어느 근거로
|
|
섰나」** 다. 그래서 `source` 와 등급만 채운다.
|
|
"""
|
|
versions = sheet_provenance(
|
|
[
|
|
_label("dataset_id", "자료", "판 이름"),
|
|
_label("file", "파일", "그 판의 파일 이름"),
|
|
ColumnProvenance(
|
|
key="effective_date",
|
|
label="기준일",
|
|
tier=TIER_STANDARD,
|
|
source="그 판이 언제 것인가 — 값이 달라졌을 때 가장 먼저 보는 자리",
|
|
code="B09_Estimation_BasisSheet.py:44 dataset_versions",
|
|
),
|
|
ColumnProvenance(
|
|
key="sha256",
|
|
label="지문(앞 12)",
|
|
tier=TIER_UNCLASSIFIED,
|
|
source="파일 해시. 값도 근거도 아니고 **재현성 표식**이라 여섯 어디에도 "
|
|
"안 맞는다(PLAN 8-36 ㉰). 「같은 판으로 다시 세웠나」를 가르는 데만 쓴다",
|
|
code="B09_Estimation_BasisSheet.py:44 dataset_versions",
|
|
),
|
|
]
|
|
)
|
|
chosen = sheet_provenance(
|
|
[
|
|
_label("item", "항 목", "고를 수 있는 자리의 이름"),
|
|
ColumnProvenance(
|
|
key="value",
|
|
label="고른 값",
|
|
tier=TIER_INPUT,
|
|
source="사용자가 산출 조건에서 고른 값 — 비어 있으면 **고른 적이 없어 "
|
|
"확정 기본값으로 돌고 있다**는 뜻이다",
|
|
code="B09_Estimation_BasisSheet.py:77 chosen_conditions",
|
|
),
|
|
]
|
|
)
|
|
items = sheet_provenance(
|
|
[
|
|
_label("code", "코드"),
|
|
_label("name", "공 종"),
|
|
_label("unit", "단위"),
|
|
ColumnProvenance(
|
|
key="notes",
|
|
label="근 거",
|
|
tier=TIER_STANDARD,
|
|
source="그 공종의 단가가 **어느 품셈 표·어느 [주]** 를 따랐는지. 줄에 달린 "
|
|
"근거를 모아 온 것이라 여기서 새로 짓지 않는다",
|
|
code="B09_Estimation_BasisSheet.py:106 work_item_basis",
|
|
),
|
|
]
|
|
)
|
|
gaps = sheet_provenance(
|
|
[
|
|
_label("kind", "갈 래", "못 채운 사유의 갈래"),
|
|
_label("code", "코드"),
|
|
ColumnProvenance(
|
|
key="reason",
|
|
label="사 유",
|
|
tier=TIER_BLOCKED,
|
|
source="⚠ **0 으로 때우지 않고 남겨 둔 자리.** 근거가 오면 채워질 자리이지 "
|
|
"「세면 안 되는 자리」가 아니다",
|
|
code="B09_Estimation_BasisSheet.py:134 open_gaps",
|
|
),
|
|
]
|
|
)
|
|
return {
|
|
"basis_versions": versions,
|
|
"basis_chosen": chosen,
|
|
"basis_items": items,
|
|
"basis_gaps": gaps,
|
|
}
|