fix(B08): 실사용자 통과에서 드러난 화면 결함 셋 수정

㉕ 통과(엔진 실행)에서 잡힌 것.

- 준비공 사방공 줄에 개발자 키(`soil_guard`)가 그대로 뜨던 자리 수정.
  레지스트리 이름을 받아 쓰고, 키는 `type_id` 칸으로만 오감.
  문구표에 사방 시설 이름 넷 추가(레지스트리 값 그대로 옮긴 것).
- 좌측 「토사」 비율 칸 제거 — 넣어도 버려지던 죽은 칸.
  토사 물량은 토적표 값이 그대로 서고, 비율은 암 총량을 갈래로 나눌 때만 쓰임.
- 안분 안내 문구를 「암 갈래 입력 합 …」으로 명시.
  칸에 100 을 채운 사용자에게 「입력 합 60 %」로 뜨던 자리.
- 항목 라벨을 「암 갈래 구성비(%) — 암 총량 기준」으로 바꿈.

시험: 655 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 이 변경과 무관).
사방공 이름 시험 2건 추가, 키를 화면 이름으로 못 박던 옛 시험 1건 정정.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-08 05:13:35 +09:00
co-authored by Claude Opus 5
parent 5bd129bae4
commit 5755b1b413
6 changed files with 34 additions and 8 deletions
@@ -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]
@@ -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": ["구분", "공종", "단위", "수량", "상태", "사유"],
@@ -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)
# ⚠ 금액에 바로 걸리는 값이라 「기본값으로 돌고 있음」을 응답에 실어 화면이 띄우게 한다.
+4 -1
View File
@@ -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;
+5
View File
@@ -30,6 +30,11 @@ TYPE_LABELS = {
"ford_pavement": "물넘이포장",
"ford_bridge": "세월교",
"box_culvert": "BOX암거",
# 사방 시설 — 레지스트리 이름 그대로 옮긴 것(2026-09-08 확인). 지어낸 이름 아님.
"erosion_check": "골막이",
"bed_sill": "바닥막이",
"check_dam_small": "소형사방댐(복합형)",
"revetment": "기슭막이",
}
#: 저장 제원 칸의 사람 이름 + **어디서 채우는지**. 키 이름을 화면에 내보내지 않기 위한 표.
+4 -1
View File
@@ -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."],