fix(B05,B06): 독립 기슭막이 구간값이 조정창 키와 어긋나 리셋되던 것을 고친다

B06 조정창은 구간값(길이·전/후)을 `{role}_revet_length_m` 등 배관 기슭막이와 같은
키로 pipe_points에 저장한다. 그런데 독립 기슭막이만 백엔드가 전용 키(`length_m`·
`before_m`·`after_m`)를 읽어, 조정창에서 바꾼 값이 저장돼도 매번 원래 값으로
되돌아왔다(실데이터 확인: 234.1m에 두 벌의 키가 따로 존재).

배관과 옵션 키를 한 벌로 합쳐 원천을 하나로 만든다 — 이제 두 기슭막이의 차이는
관 유무(hidden_pipe)뿐이다.

- B06_Section_Engine_Culvert `_revet_side`: `{role}_revet_*` 우선, 없으면 옛 전용
  키로 폴백(구 저장분 호환)
- B05_Profile_UI_Drainage_Facility: 독립 기슭막이 폼이 OUTLET_REVET_KEYS를 쓰고
  저장 시 inlet_revet_* 에도 같이 싣는다. 읽기는 legacyRevetOptions로 옛 키 폴백

검증: tmp/tests/test_revet_span_source.py 3건 통과, tsc --noEmit 통과,
load_culvert_sets 실데이터로 234.1m 유입 10.5/5/5.5 · 유출 42/21/21 반영 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 19:41:02 +09:00
co-authored by Claude Opus 5
parent f916aa52a1
commit e959879500
2 changed files with 51 additions and 12 deletions
@@ -235,10 +235,30 @@ export function createFacilityOptionsForm(
revetSide.value = "양쪽";
const revetTiers = numberInput("1", "1");
revetTiers.value = "1";
const standaloneRevet = createRevetmentFields(
{ form: "form", length: "length_m", height: "height_m", before: "before_m", after: "after_m" },
{ ...REVET_COMMON_DEFAULTS, form: "돌쌓기(메)" },
);
// 옵션 키는 배관 유출 기슭막이와 **같은 한 벌**을 쓴다 — B06 조정창의 구간값도 이 키로
// 저장되므로 원천이 하나여야 한다(2026-08-28: 전용 키 `length_m`을 쓰던 동안 조정창
// 값이 매번 되돌아갔다). 읽을 때만 옛 전용 키를 폴백으로 본다.
const standaloneRevet = createRevetmentFields(OUTLET_REVET_KEYS, {
...REVET_COMMON_DEFAULTS,
form: "돌쌓기(메)",
});
/** 옛 전용 키(`form`·`length_m`…)로 저장된 값을 배관 키 자리로 옮겨 읽는다. */
const legacyRevetOptions = (
options: Record<string, string | number>,
): Record<string, string | number> => {
const merged = { ...options };
const pairs: Array<[string, string]> = [
["outlet_revet_form", "form"],
["outlet_revet_length_m", "length_m"],
["outlet_revet_height_m", "height_m"],
["outlet_revet_before_m", "before_m"],
["outlet_revet_after_m", "after_m"],
];
for (const [key, legacy] of pairs) {
if (merged[key] === undefined && options[legacy] !== undefined) merged[key] = options[legacy];
}
return merged;
};
revetGroup.body.append(
grid(labeled("설치 측", revetSide), standaloneRevet.formField),
...standaloneRevet.rows,
@@ -472,7 +492,7 @@ export function createFacilityOptionsForm(
fordSlope.value = text("ford_slope_pct");
revetSide.value = text("side") || "양쪽";
revetTiers.value = text("tiers") || "1";
standaloneRevet.write(options);
standaloneRevet.write(legacyRevetOptions(options));
// 월류 폭 기본값 — 세월교 10m·물넘이 포장 5m(2026-08-18 사용자 확정, config 정의처).
if (!fordWidth.value && (facility === "ford_pavement" || facility === "ford_bridge")) {
fordWidth.value = String(
@@ -524,6 +544,17 @@ export function createFacilityOptionsForm(
const tiers = Number.parseInt(revetTiers.value, 10);
if (Number.isFinite(tiers) && tiers >= 1) options.tiers = tiers;
standaloneRevet.read(options);
// 양쪽 벽은 같은 한 벌이 정본 — 유입 키에도 같은 값을 실어 벽 둘이 갈리지 않게 한다.
for (const [inletKey, outletKey] of [
[INLET_REVET_KEYS.form, OUTLET_REVET_KEYS.form],
[INLET_REVET_KEYS.length, OUTLET_REVET_KEYS.length],
[INLET_REVET_KEYS.height, OUTLET_REVET_KEYS.height],
[INLET_REVET_KEYS.before, OUTLET_REVET_KEYS.before],
[INLET_REVET_KEYS.after, OUTLET_REVET_KEYS.after],
] as Array<[string | undefined, string | undefined]>) {
if (inletKey && outletKey && options[outletKey] !== undefined)
options[inletKey] = options[outletKey];
}
}
return options;
},
+15 -7
View File
@@ -217,19 +217,27 @@ def _culvert_set(options: dict[str, Any] | None) -> dict[str, Any]:
def _revet_side(values: dict[str, Any], role: str) -> dict[str, Any]:
"""독립 기슭막이 한쪽 벽 제원 — 배관 유입/유출 벽과 같은 스펙 모양으로 만든다.
독립 기슭막이는 형태·높이·길이·전후를 한 벌만 가지므로 양쪽 벽에 같은 값을 싣는다.
실제로 어느 쪽(좌/우)을 세울지는 측점 방향을 아는 B06이 `side`로 가린다.
옵션 키도 배관 벽과 **같은 한 벌**(`{role}_revet_*`)을 쓴다 — B06 조정창의 구간값
(길이·전/후)이 그 키로 저장되므로, 여기서 다른 키를 읽으면 조정값이 매번 되돌아온다
(2026-08-28 화면 실측: 조정창 길이 10.5가 저장돼도 10으로 리셋). 역할 키가 없으면
B05 폼이 한 벌로 담던 옛 키(`length_m`…)로 폴백한다.
"""
height = _number(values.get("height_m"), None)
form = values.get("form")
height = _number(values.get(f"{role}_revet_height_m"), _number(values.get("height_m"), None))
form = values.get(f"{role}_revet_form") or values.get("form")
spec: dict[str, Any] = {
"role": role,
"structure": "기슭막이",
"revet_form": str(form) if form else None,
"revet_height_m": height,
"revet_length_m": _number(values.get("length_m"), None),
"revet_before_m": _number(values.get("before_m"), None),
"revet_after_m": _number(values.get("after_m"), None),
"revet_length_m": _number(
values.get(f"{role}_revet_length_m"), _number(values.get("length_m"), None)
),
"revet_before_m": _number(
values.get(f"{role}_revet_before_m"), _number(values.get("before_m"), None)
),
"revet_after_m": _number(
values.get(f"{role}_revet_after_m"), _number(values.get("after_m"), None)
),
"face_slope": REVET_FACE_SLOPE,
}
if height is not None and height > 0: