diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 5b9e2317..aa7b62c5 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -157,6 +157,19 @@ STONE_MASONRY = { # `earthwork` = 토공 대분류로 합산(울진 토적집계 D12~D14 실증) # `material` = 자재총괄로 감(할증은 거기서 한 번만) # `unit_price` = 일위대가 재료비 구성으로 감(B09 가 배합을 분해) +#: 버림 콘크리트 — **빠뜨리고 있던 줄**이다(2026-09-09 사용자 확정 ⑭). +#: 두께 근거: KDS 44 90 00 도로암거구조설계기준 「기초시공시 기초지반 다짐을 시행하고 +#: 구조물 시공이 원활하도록 **100 mm 두께의 버림콘크리트**를 타설하도록 한다」. +#: 폭 근거: KCS 34 50 05 「버림 콘크리트의 두께는 설계도서에 따르며, **폭은 잡석다짐의 +#: 폭과 동일**하게 한다」. +#: ⚠ 우리에게 **잡석다짐 폭이 아직 없다**(기초잡석은 품셈에 공종만 있고 두께·폭이 없음). +#: 그래서 **터파기 폭(평균두께 + 여유 0.2m)을 잠정으로** 쓰고 근거 문구에 그 사실을 적는다. +#: 잡석다짐 폭이 정해지면 이 한 줄만 바꾸면 된다. +BLINDING_THICKNESS_M = 0.10 +#: 버림을 뺄 수 있는 칸 — 「기본은 넣고, 빼고 싶으면 뺀다」(사용자 확정 ⑭). +#: 저장 제원에 이 칸이 없으면 **넣는 쪽**이 기본이다. +BLINDING_OPTION_KEYS = ("blinding_concrete", "base_blinding") + DESTINATION = { "터파기": "earthwork", "되메우기": "earthwork", @@ -169,6 +182,7 @@ DESTINATION = { "막자갈": "material", "콘크리트": "unit_price", "채움콘크리트": "unit_price", + "버림콘크리트": "unit_price", "모르터": "unit_price", "거푸집": "unit_price", "물구멍관": "material", # ⚠ 자재 카탈로그가 이름으로 찾는다 — 공백 없는 한 낱말(B09 규약) @@ -221,6 +235,37 @@ class StructureQuantity: billing_quantity: float = 0.0 +def wants_blinding(options: dict[str, Any]) -> bool: + """버림 콘크리트를 넣을지. **정한 적이 없으면 넣는다**(사용자 확정 ⑭). + + 「안 넣음」·「제외」·`false` 로 적혀 있을 때만 뺀다 — 빈 칸을 「빼기」로 읽으면 + 저장해 둔 적 없는 프로젝트에서 줄이 통째로 사라진다. + """ + for key in BLINDING_OPTION_KEYS: + raw = options.get(key) + if raw is None: + continue + text = str(raw).strip().lower() + if text in {"false", "0", "no", "제외", "안 넣음", "안넣음", "빼기"}: + return False + return True + + +def _blinding_component(base_width_m: float, length_m: float, options: dict[str, Any]): + """버림 콘크리트 한 줄. 넣지 않기로 했으면 `None`.""" + if not wants_blinding(options) or base_width_m <= 0 or length_m <= 0: + return None + return Component( + "버림콘크리트", + "㎥", + base_width_m * length_m * BLINDING_THICKNESS_M, + DESTINATION["버림콘크리트"], + f"기초 폭 {base_width_m:.2f}m × 연장 × 두께 {BLINDING_THICKNESS_M:.2f}m" + " · 두께는 KDS 44 90 00(100㎜) · ⚠ 폭은 잡석다짐 폭(KCS 34 50 05)이라야 하나" + " 그 값이 아직 없어 **터파기 폭을 잠정**으로 씀", + ) + + #: ⚠ **저장 제원의 실제 칸 이름**은 `back_len_cm` 이다(레지스트리 확인). #: 앞서 `stone_back_length_cm` 을 읽고 있어 **저장값이 영영 안 닿고 늘 기본 45㎝ 로 돌았다** #: — 뒷길이를 75 로 골라도 45 계수가 붙던 자리다. 값이 나오므로 아무 시험도 안 잡았다. @@ -325,6 +370,10 @@ def boulder_masonry( # 낸다. 근거 문구에 그 사실을 적어 되짚을 수 있게 한다. upper_cm = float(str(diameter).split("~")[-1]) thickness = upper_cm / 100.0 + # 버림 콘크리트 — 돌쌓기와 같은 자리(사용자 확정 ⑭). 빼려면 저장 제원에서 「안 넣음」. + blinding = _blinding_component(thickness + constants["excavation_extra_m"], length_m, options) + if blinding is not None: + components.append(blinding) excavation = height_m * (thickness + constants["excavation_extra_m"]) * length_m backfill = height_m * constants["backfill_thickness_m"] * length_m components.extend( @@ -527,6 +576,12 @@ def stone_masonry( ) ) + # 버림 콘크리트 — 기초 바닥에 까는 얇은 층. 빼려면 저장 제원에서 「안 넣음」으로 둔다. + base_width = thickness + constants["excavation_extra_m"] + blinding = _blinding_component(base_width, length_m, options) + if blinding is not None: + components.append(blinding) + # 터파기·되메우기·잔토 — 토공으로 합산되는 값이다(내역 줄이 아니다). excavation = height_m * (thickness + constants["excavation_extra_m"]) * length_m backfill = height_m * constants["backfill_thickness_m"] * length_m