"""채집석 공제 — 캐서 쓴 돌만큼 사토가 준다 (2026-09-09 사용자 확정 ②). ⚠⚠ **공제는 사토에서 한 번만 한다.** B08 은 소요량(㎥ **양수**)을 내기만 하고 빼지 않는다. 빼는 자리는 유토곡선의 사토뿐이다 — 실어 내는 몫에서 먼저 빼고 모자라면 자연방토에서. ⚠ 겨누는 것 다섯 ① 기본은 「캔다」 — 정한 적 없으면 공제가 선다(법이 「가급적 현장 채취」) ② 「구입」으로 둔 구조물은 **공제 없음** — 구조물마다 갈린다 ③ ⚠ **부호를 안 넘긴다** — 양수로 준다(음수로 주면 받는 쪽에서 두 번 뒤집힌다) ④ 자재총괄에 **안 섞인다** — 자재는 `material` 만 모은다 ⑤ 인계 맨 위에 **한 값으로** 실린다(구조물마다 갈리므로 합산해서 하나로) """ 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_Handoff import build_handoff # noqa: E402 from B08_Quantity.B08_Quantity_Engine_MaterialSummary import ( # noqa: E402 build_table as build_material_table, ) from B08_Quantity.B08_Quantity_Engine_UnitQuantity import ( # noqa: E402 COLLECTED_STONE_KEY, build_table, is_collected_stone, stone_masonry, ) def 구조물(**options: object) -> dict: return { "structure_id": "s1", "type_id": "masonry_wet", "start_m": 0.0, "end_m": 10.0, "options": {"height_m": 2.5, "length_m": 10.0, "back_len_cm": 45, **options}, } def 성분(**options: object) -> dict: components, _ = stone_masonry( 2.5, 10.0, {"height_m": 2.5, "length_m": 10.0, "back_len_cm": 45, **options}, wet=True ) return {c.name: c for c in components} def test_기본은_캔다() -> None: """법이 그쪽을 권한다 — 별표2 「야면석 등은 **가급적 현장에서 채취·사용**」.""" assert is_collected_stone({}) is True assert "채집석" in 성분() def test_구입으로_두면_공제가_없다() -> None: for value in ("구입", "purchase", "buy"): assert is_collected_stone({"stone_supply": value}) is False, value assert "채집석" not in 성분(stone_supply="구입") def test_양수로_낸다() -> None: """⚠ 실무 시트가 `−274.66` 이라 부호를 넘기면 **두 번 뒤집힌다.**""" got = 성분()["채집석"] assert got.unit == "㎥" assert got.amount > 0 def test_여기서_빼지_않는다는_것이_근거에_적힌다() -> None: basis = 성분()["채집석"].basis assert "사토에서 한 번만" in basis def test_자재총괄에_안_섞인다() -> None: """⚠ 자재는 `material` 만 모은다 — 채집석이 자재로 서면 같은 돌을 두 번 센다.""" unit = build_table([구조물()]) material = build_material_table(unit) assert all(row["name"] != "채집석" for row in material["rows"]) def test_구조물마다_갈리고_합산해서_하나로_간다() -> None: unit = build_table( [ 구조물(), { "structure_id": "s2", "type_id": "masonry_dry", "start_m": 20.0, "end_m": 30.0, "options": { "height_m": 2.0, "length_m": 10.0, "back_len_cm": 35, "stone_supply": "구입", }, }, ] ) only_collected = 성분()["채집석"].amount assert unit[COLLECTED_STONE_KEY] == round(only_collected, 3) assert build_handoff(unit_quantity_table=unit)["collected_stone_deduction_m3"] == round( only_collected, 3 )