fix(B08): 개소 구조물이 인계에 연장(m)으로 나가던 것

㉕ 타설 줄 실증 중에 드러남. 관측 원단위가 「개소당」인 종류(집수정 셋)를
인계 줄이 늘 「m · 연장」으로 내보내고 있었음.

- 연장 2m 짜리 집수정이 「2.000 m」로 나가 값이 두 배로 실림
- 성분은 개소 기준으로 맞게 서는데 줄의 축만 어긋나 아무 시험도 안 잡았음
- billing_of() 로 관측표가 정한 단위·개수를 꺼내 전개 결과에 실음
- 인계 줄이 그 단위를 따름. 비어 있으면 종전대로 「m · 연장」

시험 셋 — 개소로 서는 것 · 연장이 길어도 한 개소 · ⚠ 정상 「m」 줄은
그대로일 것(좁게 고치는 짝 시험).

곁들여 타설 줄 실증: 옹벽 H=2.0(20m) + 집수정 Ø800 으로 돌리면
FP-12-01-01#철근구조물 32.840㎥ 가 인계에 섬(옹벽 27+3 · 집수정 2.84).

시험: 677 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-08 06:23:47 +09:00
co-authored by Claude Opus 5
parent f685845e5a
commit de31cd9c42
3 changed files with 59 additions and 2 deletions
+11 -2
View File
@@ -643,13 +643,22 @@ def _structure_rows(
elif entry.get("class_from") in ("back_length", "bond") and class_key is None:
unmatched.append(f"{wording_type_label(type_id)}{class_basis}")
length = float(structure.get("length_m") or 0.0)
# ⚠ 관측 원단위가 「개소당」·「㎡당」인 종류는 **연장으로 세면 축이 어긋난다** —
# 집수정 한 개소가 연장 2m 면 값이 두 배로 실린다(2026-09-08 ㉕ 실증).
# 성분은 개소 기준으로 맞게 서는데 **줄의 축만** 틀렸던 자리다.
bill_unit = str(structure.get("billing_unit") or "") or "m"
bill_quantity = (
float(structure.get("billing_quantity") or 0.0)
if structure.get("billing_unit")
else length
)
rows.append(
{
"work_item_code": code,
"name": str(structure.get("name") or type_id),
"spec": _spec_detail(structure),
"unit": "m",
"quantity": length,
"unit": bill_unit,
"quantity": bill_quantity,
"quantity_gross": None,
"application_ratio_pct": None,
"application_ratio_breakdown": None,
@@ -131,6 +131,26 @@ def scale_for(entry: dict[str, Any], structure: dict[str, Any]) -> tuple[float,
return 1.0, "1 개소"
def billing_of(
type_id: str,
spec: dict[str, Any],
structure: dict[str, Any],
table: ObservedUnitTable | None = None,
) -> tuple[str, float] | None:
"""(내역 단위, 그 단위로 센 수량). 표에 없으면 `None`.
⚠ **왜 있나** — 관측 원단위는 「개소당」·「㎡당」으로도 온다. 그런데 인계 줄이 늘
「m · 연장」으로 나가고 있어, **집수정 한 개소가 「연장 2m」면 값이 두 배로 실렸다**
(2026-09-08 ㉕ 실증에서 드러남). 성분은 개소 기준으로 맞게 서는데 **줄의 축만
어긋나** 있어서 아무 시험도 안 잡았다.
"""
found = (table or load_observed_table()).find(type_id, spec)
if found is None:
return None
scale, _note = scale_for(found, structure)
return str(found.get("unit") or "개소"), scale
def expand_observed(
type_id: str,
spec: dict[str, Any],
@@ -38,6 +38,7 @@ from B08_Quantity.B08_Quantity_Engine_ObservedUnit import (
BASIS_DERIVED,
BASIS_OBSERVED,
ObservedUnitTable,
billing_of,
expand_observed,
load_observed_table,
)
@@ -139,6 +140,11 @@ class StructureQuantity:
options: dict[str, Any] = field(default_factory=dict)
components: list[Component] = field(default_factory=list)
notes: list[str] = field(default_factory=list)
#: 내역 줄이 설 **단위와 그 단위로 센 수량**. 관측 원단위가 「개소당」·「㎡당」인
#: 종류는 연장(m)으로 세면 축이 어긋난다(2026-09-08 ㉕ 실증).
#: 비어 있으면 종전대로 「m · 연장」으로 선다.
billing_unit: str = ""
billing_quantity: float = 0.0
#: ⚠ **저장 제원의 실제 칸 이름**은 `back_len_cm` 이다(레지스트리 확인).
@@ -499,6 +505,22 @@ def _observed_components(
return expand_observed(type_id, spec, structure, observed)
def _observed_billing(
type_id: str,
structure: dict[str, Any],
observed: ObservedUnitTable | None,
) -> tuple[str, float] | None:
"""관측표가 정한 **내역 단위와 개수**. 규격 키가 없는 종류는 건드리지 않는다."""
keys = OBSERVED_SPEC_KEYS.get(type_id)
if keys is None:
return None
options = structure.get("options") or {}
spec = {key: options[key] for key in keys if options.get(key) is not None}
if not spec:
return None
return billing_of(type_id, spec, structure, observed)
def expand(
structure: dict[str, Any],
names: dict[str, str] | None = None,
@@ -538,6 +560,9 @@ def expand(
if components or notes:
result.components = [Component(**item) for item in components]
result.notes.extend(notes)
billing = _observed_billing(type_id, structure, observed)
if billing is not None:
result.billing_unit, result.billing_quantity = billing
return result
from B08_Quantity.B08_Quantity_Wording import type_label
@@ -601,6 +626,9 @@ def build_table(
"height_m": item.height_m,
"start_m": item.start_m,
"end_m": item.end_m,
# 내역 줄이 설 단위·수량 — 관측 원단위가 「개소당」인 종류는 연장으로 못 센다.
"billing_unit": item.billing_unit,
"billing_quantity": item.billing_quantity,
# 저장된 제원 — 형식(반중력식…)처럼 **뒤 단계가 읽어야 하는** 값이 여기 있다.
"options": item.options,
"notes": item.notes,