diff --git a/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py b/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py index f3e3eb17..f94daae3 100644 --- a/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py +++ b/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py @@ -94,7 +94,7 @@ def _split_by_rock(total: float, source: SummaryInput) -> list[tuple[str, float, given = sum(ratios.values()) if given <= 0: return [("암", total, "")] - note = "" if abs(given - 100.0) < 1e-9 else f"입력 합 {given:g} % → 100 % 로 안분" + note = "" if abs(given - 100.0) < 1e-9 else f"암 갈래 입력 합 {given:g} % → 100 % 로 안분" return [(name, total * ratios[name] / given, note) for name in classes if ratios[name] > 0] diff --git a/B08_Quantity/B08_Quantity_Engine_Preparation.py b/B08_Quantity/B08_Quantity_Engine_Preparation.py index bcb52002..ba97fa77 100644 --- a/B08_Quantity/B08_Quantity_Engine_Preparation.py +++ b/B08_Quantity/B08_Quantity_Engine_Preparation.py @@ -22,6 +22,8 @@ from __future__ import annotations from typing import Any, Iterable +from B08_Quantity.B08_Quantity_Wording import type_label + #: 사방 시설로 보는 구조물 종류 — **레지스트리의 실제 `type_id` 를 쓴다**(D 그룹 + 흙막이). #: 목록에 없으면 그 노선에 사방공이 **없는** 것이다. 이름을 지어내면 영영 안 걸린다. EROSION_CONTROL_TYPES = frozenset( @@ -200,8 +202,16 @@ def _level_frame_row(slope_rows: list[dict[str, Any]]) -> dict[str, Any]: } -def erosion_rows(structures: Iterable[dict[str, Any]] = ()) -> list[dict[str, Any]]: - """사방공 줄 — 이 노선에 사방 시설이 **있을 때만** 값이 선다.""" +def erosion_rows( + structures: Iterable[dict[str, Any]] = (), + names: dict[str, str] | None = None, +) -> list[dict[str, Any]]: + """사방공 줄 — 이 노선에 사방 시설이 **있을 때만** 값이 선다. + + ⚠ 줄 이름은 **레지스트리 이름**을 쓴다 — `type_id` 를 그대로 적으면 화면에 + `soil_guard` 같은 개발자 키가 뜬다(B08 ㉑ 과 같은 병). `names` 가 없으면 + 문구표가 받아 주고, 그것도 없으면 키를 보이되 **지어내지는 않는다**. + """ found = sorted( { str(item.get("type_id")) @@ -224,7 +234,8 @@ def erosion_rows(structures: Iterable[dict[str, Any]] = ()) -> list[dict[str, An return [ { "group": "사방공", - "item": type_id, + "item": type_label(type_id, names), + "type_id": type_id, "unit": "개소", "amount": None, "status": STATUS_PENDING, @@ -240,10 +251,11 @@ def build_table( structures: Iterable[dict[str, Any]] = (), slope_rows: Iterable[dict[str, Any]] = (), topsoil_thickness_m: float | None = None, + names: dict[str, str] | None = None, ) -> dict[str, Any]: """화면·인계가 그대로 쓰는 모양. **못 서는 줄도 목록에 남는다.**""" rows = preparation_rows(slope_totals, slope_rows, topsoil_thickness_m) + erosion_rows( - structures + structures, names ) return { "columns": ["구분", "공종", "단위", "수량", "상태", "사유"], diff --git a/B08_Quantity/B08_Quantity_Router_Earthwork.py b/B08_Quantity/B08_Quantity_Router_Earthwork.py index b6eb40d6..23373193 100644 --- a/B08_Quantity/B08_Quantity_Router_Earthwork.py +++ b/B08_Quantity/B08_Quantity_Router_Earthwork.py @@ -22,6 +22,7 @@ from fastapi.responses import JSONResponse from pydantic import BaseModel from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path +from B05_Profile.B05_Profile_Structures_Schema import structure_type_map from B06_Section.B06_Section_Repository import ( get_cross_section_designs, get_longitudinal_section, @@ -99,11 +100,13 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse: ) ) # 준비공·사방공 — 못 서는 줄도 사유와 함께 남긴다(빈 표는 「빠뜨림」과 구별이 안 됨). + structures = await _route_structures(project_id) table["preparation"] = build_preparation_table( slope.get("totals") or {}, - await _route_structures(project_id), + structures, slope.get("rows") or [], settings.get("topsoil_thickness_m"), + {type_id: definition.name for type_id, definition in structure_type_map().items()}, ) method, method_is_default = concrete_placing_method(settings) # ⚠ 금액에 바로 걸리는 값이라 「기본값으로 돌고 있음」을 응답에 실어 화면이 띄우게 한다. diff --git a/B08_Quantity/B08_Quantity_UI_Page.ts b/B08_Quantity/B08_Quantity_UI_Page.ts index fdf95ad2..2e0413c6 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.ts +++ b/B08_Quantity/B08_Quantity_UI_Page.ts @@ -218,7 +218,10 @@ function buildQuantitySidePanel( panel.append(field(L("B08_Quantity_Side_RockRatios"), "")); for (const name of classes) { // 「암」은 비율을 넣으면 사라지는 되메움 줄이라 비율 칸을 두지 않는다. - if (name !== "암") { + // 「토사」도 비율 칸을 두지 않는다 — 토사 물량은 토적표의 흙깎기 값이 그대로 서고, + // 비율은 **암 총량을 갈래로 나누는 데만** 쓰인다(집계 엔진 `_rock_split`). + // 칸을 두면 넣은 값이 조용히 버려져 「입력 합 60 %」 같은 안내가 뜬다(2026-09-08 통과). + if (name !== "암" && name !== "토사") { panel.append( numberField(name, draft.rock_ratios_pct[name] ?? 0, (value) => { draft.rock_ratios_pct[name] = value; diff --git a/B08_Quantity/B08_Quantity_Wording.py b/B08_Quantity/B08_Quantity_Wording.py index b535dd4e..2ce69b9a 100644 --- a/B08_Quantity/B08_Quantity_Wording.py +++ b/B08_Quantity/B08_Quantity_Wording.py @@ -30,6 +30,11 @@ TYPE_LABELS = { "ford_pavement": "물넘이포장", "ford_bridge": "세월교", "box_culvert": "BOX암거", + # 사방 시설 — 레지스트리 이름 그대로 옮긴 것(2026-09-08 확인). 지어낸 이름 아님. + "erosion_check": "골막이", + "bed_sill": "바닥막이", + "check_dam_small": "소형사방댐(복합형)", + "revetment": "기슭막이", } #: 저장 제원 칸의 사람 이름 + **어디서 채우는지**. 키 이름을 화면에 내보내지 않기 위한 표. diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index 038fb415..c7380d2f 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -657,7 +657,10 @@ export const ui_locales_b2 = { B08_Quantity_Ratio_SeedCut: ["초류종자살포(절토면)", "Seed spray (cut)"], B08_Quantity_Ratio_TreeRemoval: ["지장목제거", "Obstacle removal"], B08_Quantity_Side_RockSet: ["암 갈래 세트", "Rock class set"], - B08_Quantity_Side_RockRatios: ["지반 구성비(%)", "Ground composition (%)"], + B08_Quantity_Side_RockRatios: [ + "암 갈래 구성비(%) — 암 총량 기준", + "Rock class split (%) of rock volume", + ], B08_Quantity_Btn_Save: ["저장", "Save"], B08_Quantity_Save_Success: ["산출 조건을 저장했습니다.", "Calculation settings saved."], B08_Quantity_Save_Failed: ["산출 조건을 저장하지 못했습니다.", "Failed to save the settings."],