feat(b08): 노체다짐 칸 — 기본 꺼짐 · 켜면 토공집계 별도 줄(수량 = 성토량)
- 산출 조건 subgrade_compaction_enabled · 기본 False(9-16-2 [주]⑤ 「대규모 성토지로서 층다짐이 필요한 경우」 조건부) - 토공집계 「노체다짐」 줄 · 규격 진동롤러(자주식 10ton) · 매핑 FP-09-16-02 · 갈래 없음 - [주]③ 굴착기+진동롤러 조합 중 굴착기는 성토(포설) 줄이 셈 — 두 번 안 셈(#조합 철회) - 칸 밑에 [주]⑤ 근거 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
@@ -72,6 +72,20 @@ class SummaryInput:
|
||||
rock_classes: list[str] = field(default_factory=list)
|
||||
rock_ratios_pct: dict[str, float] = field(default_factory=dict)
|
||||
application_ratios: dict[str, float] = field(default_factory=dict)
|
||||
# 노체다짐 — 기본 꺼짐(2026-09-13 판정). 켜면 성토 밑에 별도 줄이 선다.
|
||||
subgrade_compaction_enabled: bool = False
|
||||
|
||||
|
||||
#: 노체다짐 줄 — ⭐ 2026-09-13 판정 「별도 줄 · 칸으로 켜고 끔 · 기본 꺼짐」.
|
||||
#: 성토 단가(포설)에 묻지 않는다 — 묻으면 왜 그 금액인지 설명이 안 되고 켜고 끈 것이 안 보인다.
|
||||
#: ⚠ [주]③ 「굴착기와 진동롤러 조합」은 작업 방식 설명 — 굴착기는 성토(포설 9-16-1) 줄이 이미 셈.
|
||||
#: 이 줄에 굴착기를 또 넣으면 두 번 셈(2026-09-14 「#조합」 철회).
|
||||
SUBGRADE_COMPACTION_SPEC = "진동롤러(자주식 10ton)"
|
||||
SUBGRADE_COMPACTION_NOTE = (
|
||||
"산출 조건에서 켬 — 품셈 9-16-2 [주]⑤ 「대규모 성토지로서 층다짐이 필요한 경우 적용」"
|
||||
" · 수량 = 성토량(포설과 같은 밑수 ㎥)"
|
||||
" · [주]③ 굴착기+진동롤러 조합 중 굴착기는 성토(포설) 줄이 셈"
|
||||
)
|
||||
|
||||
|
||||
def _ratio(source: SummaryInput, key: str) -> float:
|
||||
@@ -121,6 +135,16 @@ def build_rows(source: SummaryInput) -> list[SummaryRow]:
|
||||
|
||||
rows.append(SummaryRow(group="보정량계", amount=earth.get("adjusted_total_m3", 0.0)))
|
||||
rows.append(SummaryRow(group="성토", amount=earth.get("fill_volume_m3", 0.0)))
|
||||
if source.subgrade_compaction_enabled:
|
||||
# ⚠ 끄면 줄 자체를 안 낸다 — 빈 줄은 「세야 함」으로 읽힌다(임목파쇄와 같은 자리).
|
||||
rows.append(
|
||||
SummaryRow(
|
||||
group="노체다짐",
|
||||
spec=SUBGRADE_COMPACTION_SPEC,
|
||||
amount=earth.get("fill_volume_m3", 0.0),
|
||||
note=SUBGRADE_COMPACTION_NOTE,
|
||||
)
|
||||
)
|
||||
|
||||
# ── 운반 — 수단별. 무대는 집계에 오르되 내역 줄이 아니다 ────────
|
||||
rows.extend(_haul_rows(source))
|
||||
|
||||
@@ -149,6 +149,8 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
|
||||
key: application_ratio(settings, key)
|
||||
for key in (settings.get("application_ratios_pct") or {})
|
||||
},
|
||||
# 노체다짐 — 기본 꺼짐. 켠 프로젝트에서만 줄이 선다(2026-09-13 판정).
|
||||
subgrade_compaction_enabled=bool(settings.get("subgrade_compaction_enabled")),
|
||||
)
|
||||
)
|
||||
# 준비공·사방공 — 못 서는 줄도 사유와 함께 남긴다(빈 표는 「빠뜨림」과 구별이 안 됨).
|
||||
@@ -452,6 +454,8 @@ class QuantitySettingsBody(BaseModel):
|
||||
# 임목파쇄 — 기본 꺼짐(확정 5차 5번). 켜면 줄이 서고, 부피를 넣으면 값이 선다.
|
||||
wood_chipping_enabled: bool | None = None
|
||||
wood_chipping_volume_m3: float | None = None
|
||||
# 노체다짐 — 기본 꺼짐(9-16-2 [주]⑤ 조건부). 켜면 토공집계에 별도 줄.
|
||||
subgrade_compaction_enabled: bool | None = None
|
||||
# 토량환산계수(다짐) — `{갈래: {"compacted": C, "reason": 사유}}`.
|
||||
# ⚠ **기본값을 복사해 넣지 않는다** — 안 고른 갈래는 키가 없어야 정본이 선다.
|
||||
# 빈 dict 는 「전부 기본값으로 되돌림」이라 통째로 갈아 끼운다.
|
||||
|
||||
@@ -106,6 +106,8 @@ export interface QuantitySettings {
|
||||
wood_chipping_enabled?: boolean | null;
|
||||
/** 파쇄 부피(㎥) — 켜도 이 값이 없으면 줄만 서고 사유가 남는다. */
|
||||
wood_chipping_volume_m3?: number | null;
|
||||
/** 노체다짐 — 기본 꺼짐(9-16-2 [주]⑤ 조건부). 켜면 토공집계에 별도 줄. */
|
||||
subgrade_compaction_enabled?: boolean | null;
|
||||
}
|
||||
|
||||
/** 갈래 하나의 「무엇을 골랐나」. 서버 `earthwork_conversion_choices` 와 짝이다. */
|
||||
|
||||
@@ -108,6 +108,7 @@ async function saveQuantitySettings(projectId: string, draft: DraftSettings): Pr
|
||||
frame_material: draft.frame_material,
|
||||
wood_chipping_enabled: draft.wood_chipping_enabled,
|
||||
wood_chipping_volume_m3: draft.wood_chipping_volume_m3,
|
||||
subgrade_compaction_enabled: draft.subgrade_compaction_enabled,
|
||||
// 개소는 **통째로** 보낸다 — 지운 항목까지 그대로 가야 되돌릴 길이 있다.
|
||||
ancillary_counts: draft.ancillary_counts,
|
||||
// 토량환산계수 — 고른 갈래만 담긴다. 빈 dict 는 「전부 기본값으로 되돌림」이다.
|
||||
@@ -297,6 +298,8 @@ interface DraftSettings {
|
||||
// 임목파쇄 — **기본 꺼짐**(확정 5차 5번). 켜야 줄이 선다. 근주이식은 칸 자체가 없다.
|
||||
wood_chipping_enabled: boolean;
|
||||
wood_chipping_volume_m3: number | null;
|
||||
// 노체다짐 — **기본 꺼짐**(9-16-2 [주]⑤ 조건부). 켜면 토공집계에 별도 줄(수량 = 성토량).
|
||||
subgrade_compaction_enabled: boolean;
|
||||
// 자재별 관급/사급 — 표 안에서 줄마다 고른 값.
|
||||
material_supply: Record<string, SupplyChoice>;
|
||||
// 갈래별 토량환산계수(다짐) — `compacted` 가 `null` 이면 「안 고름」이라 기본값이 선다.
|
||||
@@ -682,6 +685,24 @@ function buildQuantitySidePanel(
|
||||
);
|
||||
panel.append(hintRow(L("B08_Quantity_Side_Chipping_Hint")));
|
||||
|
||||
// ── 노체다짐 — ⚠ **기본 꺼짐**(2026-09-13 판정). 9-16-2 [주]⑤ 조건부라 켜야 토공집계에 줄이 선다.
|
||||
panel.append(field(L("B08_Quantity_Side_SubgradeCompaction"), ""));
|
||||
panel.append(
|
||||
selectField(
|
||||
L("B08_Quantity_SubgradeCompaction_Label"),
|
||||
draft.subgrade_compaction_enabled ? "on" : "",
|
||||
[
|
||||
{ value: "", label: L("B08_Quantity_Chipping_Off") },
|
||||
{ value: "on", label: L("B08_Quantity_Chipping_On") },
|
||||
],
|
||||
(value) => {
|
||||
draft.subgrade_compaction_enabled = value === "on";
|
||||
draft.dirty = true;
|
||||
},
|
||||
),
|
||||
);
|
||||
panel.append(hintRow(L("B08_Quantity_Side_SubgradeCompaction_Hint")));
|
||||
|
||||
// ── 콘크리트 타설 방식 — ⚠ **금액에 바로 걸리는 값**이라 잠정임을 조용히 두지 않는다 ──
|
||||
panel.append(field(L("B08_Quantity_Side_Placing"), ""));
|
||||
panel.append(
|
||||
@@ -979,6 +1000,7 @@ export async function renderB08Quantity(root: HTMLElement): Promise<void> {
|
||||
frame_material: { ...((stored.frame_material ?? {}) as Record<string, number | null>) },
|
||||
wood_chipping_enabled: Boolean(stored.wood_chipping_enabled),
|
||||
wood_chipping_volume_m3: (stored.wood_chipping_volume_m3 as number | null) ?? null,
|
||||
subgrade_compaction_enabled: Boolean(stored.subgrade_compaction_enabled),
|
||||
ancillary_counts: {
|
||||
...((stored.ancillary_counts ?? {}) as Record<string, number | null>),
|
||||
},
|
||||
|
||||
@@ -157,6 +157,9 @@ def default_settings() -> dict[str, Any]:
|
||||
# 물을 일도 없다. 켜도 **부피는 지어내지 않는다**(아래 칸이 비면 줄만 서고 사유).
|
||||
"wood_chipping_enabled": False,
|
||||
"wood_chipping_volume_m3": None,
|
||||
# 노체다짐(9-16-2) — **기본 꺼짐**(2026-09-13 판정). [주]⑤ 「대규모 성토지로서
|
||||
# 층다짐이 필요한 경우 적용」 조건부라 늘 서지 않는다. 켜면 토공집계에 별도 줄.
|
||||
"subgrade_compaction_enabled": False,
|
||||
"dataset_versions": {},
|
||||
},
|
||||
"estimation": {
|
||||
|
||||
@@ -70,6 +70,14 @@
|
||||
"master_name": "노체 > 노체포설",
|
||||
"note": "실무 내역 셋을 다 봤더니 「노체포설·노체다짐」으로 가른 줄이 **어디에도 없다**(2026-09-08 데스크탑 보조). 영월은 「암성토 BACK-HOE(0.7㎥)」 한 줄이고 그 단가산출 안에 적사→성토→다짐 3단계가 들어가며, 울진은 「유용성토」·「사토 및 다짐공」, 봉화는 성토사면다짐만 따로다. ⇒ **성토 본체 한 줄(포설) + 성토면다짐(㎡) 따로**가 실무 양식이라 부모(FP-09-16)에서 자식 포설로 내렸다. 다짐은 그 단가 안 단계로 둔다."
|
||||
},
|
||||
{
|
||||
"group": "노체다짐",
|
||||
"work_item_code": "FP-09-16-02",
|
||||
"basis_unit": "㎥",
|
||||
"basis_source": "산림사업 표준품셈(고시 2025-82) 원문 L5336 「Q=1000×V×W×E×D×f/N= ㎥/시간」 — 포설(9-16-1)과 같은 체적 축이라 성토량을 그대로 밑수로 씀.",
|
||||
"master_name": "노체 > 노체다짐",
|
||||
"note": "2026-09-13 브레인 판정 — 별도 줄 · 산출 조건 칸으로 켜고 끔 · 기본 꺼짐([주]⑤ 「대규모 성토지로서 층다짐이 필요한 경우」 조건부). [주]③ 「굴착기와 진동롤러 조합」은 작업 방식 설명 — 굴착기는 포설(9-16-1) 줄, 롤러는 이 줄(B09 B-FP-09-16-02 롤러만). 갈래 없음(2026-09-14 「#조합」 철회 — X-<기계>#조합 잡재료 16% 층과 다른 뜻)."
|
||||
},
|
||||
{
|
||||
"group": "성토면다짐",
|
||||
"work_item_code": "FP-09-17-01",
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""노체다짐 칸 — 기본 꺼짐 · 켜면 토공집계 별도 줄 · 수량 = 성토량 (2026-09-13 판정)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import SummaryInput, build_rows # noqa: E402
|
||||
|
||||
TOTALS = {"fill_volume_m3": 1234.5}
|
||||
|
||||
|
||||
def _groups(enabled: bool) -> dict:
|
||||
rows = build_rows(SummaryInput(earthwork_totals=TOTALS, subgrade_compaction_enabled=enabled))
|
||||
return {row.group: row for row in rows}
|
||||
|
||||
|
||||
def test_끄면_줄_자체가_없다() -> None:
|
||||
assert "노체다짐" not in _groups(False)
|
||||
|
||||
|
||||
def test_켜면_성토량으로_선다_근거가_비고에() -> None:
|
||||
row = _groups(True)["노체다짐"]
|
||||
assert (row.unit, row.amount, row.in_bill) == ("㎥", 1234.5, True)
|
||||
assert "[주]⑤" in row.note and "층다짐이 필요한 경우" in row.note
|
||||
assert "굴착기" not in row.spec # 굴착기는 성토(포설) 줄이 셈 — 두 번 안 셈
|
||||
|
||||
|
||||
def test_인계가_9_16_2_로_잇고_갈래를_안_싣는다() -> None:
|
||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import build_table
|
||||
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff
|
||||
|
||||
table = build_table(SummaryInput(earthwork_totals=TOTALS, subgrade_compaction_enabled=True))
|
||||
handed = {r["name"]: r for r in build_handoff(summary_table=table)["work_items"]}
|
||||
row = handed["노체다짐"]
|
||||
assert (row["work_item_code"], row["quantity"], row["in_bill"]) == ("FP-09-16-02", 1234.5, True)
|
||||
assert row["variant_value"] is None and row["blocked_kind"] is None
|
||||
|
||||
|
||||
def test_설정_기본은_꺼짐() -> None:
|
||||
from common_util.common_util_project_settings import default_settings
|
||||
|
||||
assert default_settings()["quantity"]["subgrade_compaction_enabled"] is False
|
||||
@@ -711,6 +711,12 @@ export const ui_locales_b2 = {
|
||||
"기본은 안 셉니다(확정 5차 5번) — 현장에 따라 필요하면 켜세요. ⚠ 켜도 부피를 넣어야 값이 섭니다(실무는 부피로 셈)",
|
||||
"Off by default — turn on per site. Volume must be entered for the row to carry a quantity",
|
||||
],
|
||||
B08_Quantity_Side_SubgradeCompaction: ["노체다짐", "Subgrade Compaction"],
|
||||
B08_Quantity_SubgradeCompaction_Label: ["노체다짐을 셀 것인가", "Count subgrade compaction"],
|
||||
B08_Quantity_Side_SubgradeCompaction_Hint: [
|
||||
"기본은 안 셉니다 — 근거: 산림품셈 9-16-2 [주]⑤ 「대규모 성토지로서 층다짐이 필요한 경우 적용한다」. 켜면 토공집계에 「노체다짐」 줄이 성토량(㎥)으로 섭니다. [주]③ 굴착기+진동롤러 조합 중 굴착기는 성토(포설) 줄이 이미 셉니다",
|
||||
"Off by default — forest standard 9-16-2 note ⑤ applies it only to large fills needing layer compaction. When on, a compaction row equal to the fill volume is added; the excavator of the note ③ pairing is already in the fill (spreading) row",
|
||||
],
|
||||
B08_Quantity_Side_Frame: ["규준틀 개소당 재료", "Batter Board Materials (per unit)"],
|
||||
B08_Quantity_Side_Frame_Hint: [
|
||||
"⚠ 채워진 값은 실무 관측값(제안값)이고 법정 기준이 아닙니다 — 품셈 11-2·11-3 [주]④ 는 「재료량은 설계수량에 따른다」로만 둡니다. 손율은 원문값(비탈 50% · 수평 80%)",
|
||||
|
||||
Reference in New Issue
Block a user