무슨 일이 있었나 랩탑 줄의 병합 `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
474 lines
22 KiB
Python
474 lines
22 KiB
Python
"""B08 수량 화면의 **근거 사전** — 어느 숫자가 어디서 와서 어떻게 나왔나 (PLAN 8-36 ④).
|
||
|
||
⚠⚠ **개발 전용.** 사전은 `provenance_payload()` 를 거쳐 나가고, 개발환경이 아니면 `None`
|
||
이라 응답에 칸 자체가 안 생긴다. 화면에서 숨기는 것이 아니라 **안 보내는 것**이다.
|
||
|
||
왜 이 파일인가
|
||
「식」과 「원천」의 정답은 값을 낳는 엔진이 안다. 화면 TS 에 손으로 적어 두면 엔진을
|
||
고칠 때 설명만 옛것으로 남는다. 엔진 옆(같은 폴더)에 두어 같이 눈에 들어오게 한다.
|
||
|
||
⚠ **열 단위로 적는다.** 토적표 한 장이 30열 × 200줄 = 6천 칸이라 칸마다 지으면 응답이
|
||
붐는다. 줄마다 갈리는 것(측구 안분 폴백 사유 등)은 줄이 이미 `notes` 로 들고 있고,
|
||
화면이 그것을 카드에 덧붙인다.
|
||
|
||
⚠ **토적표에는 `final`(최종) 열이 없다 — 억지로 붙이지 않았다.**
|
||
이 표는 중간 장부다. 내역서로 나가는 값은 **토공집계표**에서 선다. 여섯 등급을
|
||
한 장에 다 채우려고 아무 열에나 `final` 을 붙이면 「분류가 있다」는 거짓만 남는다.
|
||
이 어긋남은 등급을 고칠 근거이므로 PLAN 8-36 ① 에 그대로 남긴다.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from common_util.common_util_provenance import (
|
||
TIER_CALC,
|
||
TIER_FINAL,
|
||
TIER_INPUT,
|
||
TIER_STANDARD,
|
||
TIER_SURVEY,
|
||
ColumnProvenance,
|
||
provenance_payload,
|
||
sheet_provenance,
|
||
)
|
||
|
||
#: 토량환산계수가 어디서 오는지 — 여러 열이 같은 문장을 쓰므로 한 벌로 둔다.
|
||
_FACTOR_SOURCE = (
|
||
"토량환산계수(다짐) — 기본값 `config_system_design.EARTHWORK_CONVERSION_FACTORS`, "
|
||
"프로젝트가 고른 값이 있으면 산출 조건 패널의 값"
|
||
)
|
||
|
||
#: 단면적 넷의 공통 원천. B06 이 낸 설계 단면을 **그대로** 읽는다(여기서 다시 안 짓는다).
|
||
_SECTION_SOURCE = "B06 횡단 설계가 낸 측점별 단면적"
|
||
|
||
#: 평균단면적법 한 줄. 신규 문서 5장 「다. 공사수량의 산출」.
|
||
_MEAN_AREA = "(앞 측점 단면적 + 이 측점 단면적) ÷ 2 × 두 측점 사이 거리"
|
||
|
||
|
||
def _area(key: str, label: str, extra: str = "") -> ColumnProvenance:
|
||
"""단면적 열 — B06 설계값을 그대로 옮긴 자리라 식이 없다."""
|
||
return ColumnProvenance(
|
||
key=key,
|
||
label=label,
|
||
tier=TIER_SURVEY,
|
||
formula="설계가 낸 값을 그대로 읽음 (여기서 다시 계산하지 않음)",
|
||
source=_SECTION_SOURCE + (f" · {extra}" if extra else ""),
|
||
code="B08_Quantity_Engine_EarthworkTable.py:StationArea.from_design",
|
||
)
|
||
|
||
|
||
def _volume(key: str, label: str, area_label: str) -> ColumnProvenance:
|
||
return ColumnProvenance(
|
||
key=key,
|
||
label=label,
|
||
tier=TIER_CALC,
|
||
formula=_MEAN_AREA.replace("단면적", area_label),
|
||
source="첫 측점은 앞이 없어 비어 있음 (실무 토적표도 첫 줄 체적이 빈칸)",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:200 mean_volume",
|
||
)
|
||
|
||
|
||
def _adjusted(key: str, label: str, volume_label: str) -> ColumnProvenance:
|
||
return ColumnProvenance(
|
||
key=key,
|
||
label=label,
|
||
tier=TIER_CALC,
|
||
formula=f"{volume_label} × 토량환산계수(다짐)",
|
||
source=_FACTOR_SOURCE,
|
||
code="B08_Quantity_Engine_EarthworkTable.py:210",
|
||
)
|
||
|
||
|
||
def earthwork_sheet() -> dict[str, Any]:
|
||
"""토적표 한 장의 사전. 열 키는 화면 `EarthworkRow` 와 같은 낱말이라야 한다."""
|
||
return sheet_provenance(
|
||
[
|
||
ColumnProvenance(
|
||
key="chainage_m",
|
||
label="측점",
|
||
tier=TIER_SURVEY,
|
||
formula="노선 시점에서 잰 이정(m). 화면은 NO.n+m 으로 적음",
|
||
source="B05 종단이 놓은 측점 배치",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:StationArea.chainage_m",
|
||
),
|
||
ColumnProvenance(
|
||
key="distance_m",
|
||
label="거리",
|
||
tier=TIER_CALC,
|
||
formula="이 측점 이정 − 앞 측점 이정",
|
||
source="B05 종단 측점 배치. 첫 줄은 앞이 없어 0",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:195",
|
||
),
|
||
_area("cut_soil_area_m2", "절토 토사 단면적"),
|
||
_volume("cut_soil_volume_m3", "절토 토사 입적", "절토 토사 단면적"),
|
||
_adjusted("cut_soil_adjusted_m3", "절토 토사 보정량", "절토 토사 입적"),
|
||
_area("cut_rock_area_m2", "절토 암석 단면적", "암 갈래는 측점의 `cut_rock_kind`"),
|
||
_volume("cut_rock_volume_m3", "절토 암석 입적", "절토 암석 단면적"),
|
||
_adjusted("cut_rock_adjusted_m3", "절토 암석 보정량", "절토 암석 입적"),
|
||
_area(
|
||
"ditch_soil_area_m2",
|
||
"측구터파기 토사 단면적",
|
||
"지반 유형·암반 경계선으로 B06 이 가른 값. 가름이 없는 옛 저장분만 "
|
||
"절토 토사:암 면적비로 안분하고 그 줄에 사유가 남음",
|
||
),
|
||
_volume("ditch_soil_volume_m3", "측구터파기 토사 입적", "측구 토사 단면적"),
|
||
_adjusted("ditch_soil_adjusted_m3", "측구터파기 토사 보정량", "측구 토사 입적"),
|
||
_area(
|
||
"ditch_rock_area_m2",
|
||
"측구터파기 암석 단면적",
|
||
"위와 같은 가름값. 0.0 은 설계가 낸 「없음」이고 값 없음과 다름",
|
||
),
|
||
_volume("ditch_rock_volume_m3", "측구터파기 암석 입적", "측구 암석 단면적"),
|
||
_adjusted("ditch_rock_adjusted_m3", "측구터파기 암석 보정량", "측구 암석 입적"),
|
||
ColumnProvenance(
|
||
key="adjusted_total_m3",
|
||
label="보정량계",
|
||
tier=TIER_CALC,
|
||
formula="절토 토사 보정량 + 절토 암석 보정량 + 측구 토사 보정량 + 측구 암석 보정량",
|
||
source="네 보정량의 합. 성토에 쓸 수 있는 양으로 환산한 뒤의 값",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:215",
|
||
),
|
||
_area("fill_area_m2", "성토 단면적"),
|
||
_volume("fill_volume_m3", "성토 입적", "성토 단면적"),
|
||
ColumnProvenance(
|
||
key="diverted_m3",
|
||
label="유용토",
|
||
tier=TIER_CALC,
|
||
formula="min(보정량계, 성토 입적)",
|
||
source="그 측점에서 절취분과 성토분이 서로 만나는 몫",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:222",
|
||
),
|
||
ColumnProvenance(
|
||
key="balance_m3",
|
||
label="차인토량",
|
||
tier=TIER_CALC,
|
||
formula="보정량계 − 성토 입적",
|
||
source="양수면 남는 흙(사토), 음수면 모자란 흙(객토)",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:223",
|
||
),
|
||
ColumnProvenance(
|
||
key="cumulative_m3",
|
||
label="누가토량",
|
||
tier=TIER_CALC,
|
||
formula="첫 줄부터 이 줄까지 차인토량을 더해 온 값",
|
||
source="유토곡선(mass haul)의 세로축이 되는 값",
|
||
code="B08_Quantity_Engine_EarthworkTable.py:225",
|
||
),
|
||
]
|
||
)
|
||
|
||
|
||
def summary_sheet() -> dict[str, Any]:
|
||
"""토공집계표 — 토적표·사면표를 공종별 총량으로 모은 장.
|
||
|
||
⚠ **B08 에서 `final`(최종)이 처음 서는 자리다.** 토적표는 중간 장부였고, 내역서로
|
||
나가는 값은 여기 「계」다. 다만 무대(소운반 20m)처럼 **집계에는 오르되 내역 줄이
|
||
되지 않는** 줄이 있어, 그 줄의 「계」는 칸 등급 `excluded` 로 덮어쓴다(화면 배선).
|
||
"""
|
||
return sheet_provenance(
|
||
[
|
||
ColumnProvenance(
|
||
key="group",
|
||
label="구분",
|
||
tier=TIER_STANDARD,
|
||
formula="품셈 공종 갈래 이름을 그대로 씀 (흙깎기·성토·측구터파기…)",
|
||
source="거창 실무 토공집계표 시트의 열 문구를 그대로 옮김",
|
||
code="B08_Quantity_Engine_EarthworkSummary.py:SummaryRow",
|
||
),
|
||
ColumnProvenance(
|
||
key="item",
|
||
label="공종",
|
||
tier=TIER_STANDARD,
|
||
formula="지반 갈래 이름 (토사·연암·발파암…)",
|
||
source="갈래 수는 프로젝트 설정의 암 갈래 세트가 정함 — 코드에 안 박음",
|
||
rule="암 총량을 설계자가 넣은 갈래 비율(%)로 나눠 줄을 만듦",
|
||
code="B08_Quantity_Engine_EarthworkSummary.py:_rock_split",
|
||
),
|
||
ColumnProvenance(
|
||
key="spec",
|
||
label="규격",
|
||
tier=TIER_STANDARD,
|
||
formula="시공 방법 표기 (기계(굴삭기)·백호우…)",
|
||
source="품셈 공종이 요구하는 규격. 암은 시공법(긁어내기/터뜨리기)이 갈림",
|
||
code="B08_Quantity_Engine_EarthworkSummary.py:SummaryRow",
|
||
),
|
||
ColumnProvenance(
|
||
key="unit",
|
||
label="단위",
|
||
tier=TIER_STANDARD,
|
||
formula="품셈 공종이 정한 단위 (㎥·㎡·주…)",
|
||
source="단위가 다르면 내역 단가와 안 맞음 — 여기서 정하지 않고 품셈을 따름",
|
||
code="B08_Quantity_Engine_EarthworkSummary.py:SummaryRow",
|
||
),
|
||
ColumnProvenance(
|
||
key="amount",
|
||
label="계",
|
||
tier=TIER_FINAL,
|
||
formula="토적표·사면표 총량 × 반영률(%)",
|
||
source=(
|
||
"토공은 토적표 합계, 사면은 사면표 합계. ⚠ 반영률은 법정값이 아니라 "
|
||
"설계자가 넣는 값이고 기본 100 %"
|
||
),
|
||
rule="무대(소운반 20m)는 집계에는 오르되 내역 줄이 아님 — 그 줄은 「제외」로 섬",
|
||
code="B08_Quantity_Engine_EarthworkSummary.py:build_table",
|
||
),
|
||
]
|
||
)
|
||
|
||
|
||
def haul_sheet() -> dict[str, Any]:
|
||
"""운반거리 — (운반수단 × 지반유형)별 가중평균 줄.
|
||
|
||
⚠⚠ **상태가 둘이다.** 거리는 다짐상태로 재고, 내역에 오르는 수량만 자연상태(÷C)로 낸다
|
||
(설계실무 요령 5-4-3). 이 표의 「토량」은 **다짐상태**이므로 내역서 수량과 숫자가 다르다 —
|
||
그 어긋남이 정상이라는 것을 카드가 말해 주어야 헛걸음을 안 한다.
|
||
"""
|
||
return sheet_provenance(
|
||
[
|
||
ColumnProvenance(
|
||
key="equipment",
|
||
label="운반수단",
|
||
tier=TIER_CALC,
|
||
formula="유토곡선이 띠마다 고른 수단 (무대·도자운반·덤프운반)",
|
||
source="B06 운반계획(HaulPlan)의 띠. 여기서 다시 고르지 않음",
|
||
code="B08_Quantity_Engine_HaulSummary.py:_legs_of",
|
||
),
|
||
ColumnProvenance(
|
||
key="ground",
|
||
label="지반유형",
|
||
tier=TIER_CALC,
|
||
formula="띠의 토량을 절토 구간 구성비로 안분한 세 갈래 (토사·리핑암·발파암)",
|
||
source="B06 운반계획이 이미 안분해 둔 값",
|
||
code="B08_Quantity_Engine_HaulSummary.py:_legs_of",
|
||
),
|
||
ColumnProvenance(
|
||
key="volume_m3",
|
||
label="토량",
|
||
tier=TIER_CALC,
|
||
formula="그 갈래에 속한 근거 구간들의 토량 합",
|
||
source=(
|
||
"⚠ **다짐상태**임. 내역서에 오르는 수량은 자연상태(÷토량환산계수)라 "
|
||
"숫자가 다름 — 어긋난 것이 아님"
|
||
),
|
||
code="B08_Quantity_Engine_HaulSummary.py:summarize",
|
||
),
|
||
ColumnProvenance(
|
||
key="average_distance_m",
|
||
label="평균운반거리",
|
||
tier=TIER_CALC,
|
||
formula="Σ(토량 × 거리) ÷ Σ(토량) — 단순평균이 아님",
|
||
source="실무 산출서가 「토량 × 거리」를 쌓아 나누는 그 식",
|
||
code="B08_Quantity_Engine_HaulSummary.py:119 average_distance_m",
|
||
),
|
||
ColumnProvenance(
|
||
key="legs",
|
||
label="근거 구간",
|
||
tier=TIER_CALC,
|
||
formula="이 평균을 만든 구간의 개수",
|
||
source="구간 줄은 버리지 않고 표 아래 근거로 함께 냄 — 되짚을 수 있어야 함",
|
||
code="B08_Quantity_Engine_HaulSummary.py:190",
|
||
),
|
||
]
|
||
)
|
||
|
||
|
||
def preparation_sheet() -> dict[str, Any]:
|
||
"""준비공·사방공 — **못 서는 줄도 서는 장.**
|
||
|
||
빈 표를 내면 「빠뜨린 것」과 「원래 없는 것」이 구별되지 않는다. 그래서 값이 없는 줄도
|
||
상태와 사유를 달아 그대로 세운다. 값이 비어 있는 줄의 「수량」은 칸 등급 `blocked` 로
|
||
덮어쓴다 — **근거가 오면 채워질 자리**이지 일부러 비운 자리가 아니다.
|
||
"""
|
||
return sheet_provenance(
|
||
[
|
||
ColumnProvenance(
|
||
key="group",
|
||
label="구분",
|
||
tier=TIER_STANDARD,
|
||
formula="준비공·사방공의 갈래 이름",
|
||
source="품셈 9장(준비공)과 배치된 구조물 종류가 줄을 만듦",
|
||
code="B08_Quantity_Engine_Preparation.py:build_table",
|
||
),
|
||
ColumnProvenance(
|
||
key="item",
|
||
label="공종",
|
||
tier=TIER_STANDARD,
|
||
formula="품셈 공종 이름 (표토제거·제근·임목파쇄…)",
|
||
source="공종이 없으면 줄도 없음 — 화면에서 이름을 짓지 않음",
|
||
code="B08_Quantity_Engine_Preparation.py:build_table",
|
||
),
|
||
ColumnProvenance(
|
||
key="unit",
|
||
label="단위",
|
||
tier=TIER_STANDARD,
|
||
formula="품셈 공종이 정한 단위",
|
||
source="단위가 다르면 내역 단가와 안 맞음",
|
||
code="B08_Quantity_Engine_Preparation.py:build_table",
|
||
),
|
||
ColumnProvenance(
|
||
key="amount",
|
||
label="수량",
|
||
tier=TIER_CALC,
|
||
formula="공종마다 다름 — 표토제거는 면적 × 표토 두께(T), 제근은 임목축적 등급",
|
||
source=(
|
||
"밑수는 사면표·구조물 목록이 내고, 두께·등급·개소는 산출 조건 패널에서 "
|
||
"설계자가 넣음"
|
||
),
|
||
rule="넣어야 할 값이 비면 줄은 서되 수량이 「-」로 남고 사유가 붙음",
|
||
code="B08_Quantity_Engine_Preparation.py:build_table",
|
||
),
|
||
ColumnProvenance(
|
||
key="status",
|
||
label="상태",
|
||
tier=TIER_CALC,
|
||
formula="값을 세웠나 못 세웠나",
|
||
source="못 세운 줄은 옆 칸에 사유가 붙음 — 사유가 곧 무엇을 넣어야 하는지임",
|
||
code="B08_Quantity_Engine_Preparation_Status.py",
|
||
),
|
||
]
|
||
)
|
||
|
||
|
||
def material_sheet() -> dict[str, Any]:
|
||
"""자재총괄 — 구조물 원단위에서 자재별로 모은 장. 관급/사급을 줄마다 고른다."""
|
||
return sheet_provenance(
|
||
[
|
||
ColumnProvenance(
|
||
key="name",
|
||
label="자재",
|
||
tier=TIER_STANDARD,
|
||
formula="품셈·카탈로그의 자재 이름",
|
||
source="구조물 원단위의 성분 이름을 그대로 모음 — 여기서 이름을 짓지 않음",
|
||
code="B08_Quantity_Engine_MaterialSummary.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="unit",
|
||
label="단위",
|
||
tier=TIER_STANDARD,
|
||
formula="자재가 팔리는 단위 (㎥·본·kg…)",
|
||
source="단가가 붙는 단위와 같아야 함",
|
||
code="B08_Quantity_Engine_MaterialSummary.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="net_amount",
|
||
label="순수량",
|
||
tier=TIER_CALC,
|
||
formula="구조물마다 낸 성분 수량의 합 (할증 전)",
|
||
source="구조물 원단위 표의 「수량」을 자재 이름으로 모은 값",
|
||
code="B08_Quantity_Engine_MaterialSummary.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="surcharge_pct",
|
||
label="할증률",
|
||
tier=TIER_STANDARD,
|
||
formula="자재마다 정해진 할증률(%)",
|
||
source="할증 판(dataset)이 정함. 판에 없는 자재는 「-」로 두고 지어내지 않음",
|
||
code="B08_Quantity_Engine_MaterialSummary.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="total_amount",
|
||
label="총수량",
|
||
tier=TIER_FINAL,
|
||
formula="순수량 × (1 + 할증률)",
|
||
source="내역서·자재대로 나가는 값. 할증률이 없으면 순수량 그대로",
|
||
code="B08_Quantity_Engine_MaterialSummary.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="supply",
|
||
label="관급/사급",
|
||
tier=TIER_INPUT,
|
||
formula="설계자가 줄마다 고름",
|
||
source="자재마다 갈리는 발주 결정이라 표 안에서 고름 (2026-09-07 확정)",
|
||
code="B08_Quantity_UI_MaterialGrid.ts",
|
||
),
|
||
ColumnProvenance(
|
||
key="install_by",
|
||
label="설치 주체",
|
||
tier=TIER_INPUT,
|
||
formula="설계자가 줄마다 고름",
|
||
source="⚠ **관급 줄에만 뜻이 있음** — 사급으로 되돌리면 값이 비워짐",
|
||
code="B08_Quantity_UI_MaterialGrid.ts",
|
||
),
|
||
]
|
||
)
|
||
|
||
|
||
def unit_quantity_sheet() -> dict[str, Any]:
|
||
"""구조물 원단위 — 치수에서 성분까지. 성분마다 갈 곳을 적는다.
|
||
|
||
⚠ 이 장은 **근거·출처 열을 이미 화면에 들고 있다**(2026-09-09 부터). 사전은 그 열이
|
||
무엇을 뜻하는지 설명하는 자리이지, 있는 값을 다시 만드는 자리가 아니다.
|
||
"""
|
||
return sheet_provenance(
|
||
[
|
||
ColumnProvenance(
|
||
key="structure",
|
||
label="구조물",
|
||
tier=TIER_SURVEY,
|
||
formula="B05 노선에 놓인 구조물의 이름과 놓인 측점",
|
||
source="측점 표기(NO.4 ~ NO.4+10)는 화면이 만듦 — 서버는 이정만 냄",
|
||
code="B08_Quantity_Engine_Handoff_Rows_Prep.py:182",
|
||
),
|
||
ColumnProvenance(
|
||
key="spec",
|
||
label="규격",
|
||
tier=TIER_SURVEY,
|
||
formula="구조물 제원 (길이 × 높이)",
|
||
source="B05·B06 이 배치할 때 정한 치수. 여기서 다시 정하지 않음",
|
||
code="B08_Quantity_Engine_UnitQuantity.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="component",
|
||
label="성분",
|
||
tier=TIER_STANDARD,
|
||
formula="그 구조물이 쓰는 재료·공종 이름",
|
||
source="품셈 표 또는 실무 관측 원단위표가 정함",
|
||
code="B08_Quantity_Engine_UnitQuantity.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="unit",
|
||
label="단위",
|
||
tier=TIER_STANDARD,
|
||
formula="성분이 세어지는 단위",
|
||
source="단가가 붙는 단위와 같아야 함",
|
||
code="B08_Quantity_Engine_UnitQuantity.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="amount",
|
||
label="수량",
|
||
tier=TIER_CALC,
|
||
formula="치수 전개(길이·높이로 편 식) 또는 실무 관측 원단위 × 개소",
|
||
source="어느 쪽인지는 같은 줄의 「출처」 칸이 말해 줌 (치수 전개 / 실무 관측)",
|
||
rule="치수 전개는 식이 있고, 실무 관측은 관측값이라 식이 없음 — 둘을 섞지 않음",
|
||
code="B08_Quantity_Engine_UnitQuantity.py",
|
||
),
|
||
ColumnProvenance(
|
||
key="destination",
|
||
label="갈 곳",
|
||
tier=TIER_STANDARD,
|
||
formula="이 성분이 어느 표로 가는가 (자재총괄·공종 내역·양쪽)",
|
||
source=(
|
||
"⚠ 갈 곳이 겹치면 이중계상임 — 그것을 막으려고 성분마다 갈 곳을 적음 (PLAN 8-7)"
|
||
),
|
||
code="B08_Quantity_Engine_Handoff_Mapping.py",
|
||
),
|
||
]
|
||
)
|
||
|
||
|
||
def quantity_provenance() -> dict[str, Any] | None:
|
||
"""B08 응답에 실을 사전 — **개발환경이 아니면 `None`.**
|
||
|
||
시트를 늘릴 때는 여기 한 줄만 더한다. 화면은 시트 이름으로 찾아 쓴다.
|
||
"""
|
||
return provenance_payload(
|
||
{
|
||
"earthwork": earthwork_sheet(),
|
||
"summary": summary_sheet(),
|
||
"haul": haul_sheet(),
|
||
"preparation": preparation_sheet(),
|
||
"material": material_sheet(),
|
||
"unit_quantity": unit_quantity_sheet(),
|
||
}
|
||
)
|