tmp/ 가 창끼리 안 건너가는 것이 확정돼(랩탑이 시간 두고 두 번 확인) 시험·예외가 저절로 건너가도록 git 안으로 옮김. 사용자 확정. - 내용은 하나도 안 고침 — 자리만 옮김. tmp/tests 는 남겨 둠. - 같은 이름이 이미 있던 64 개는 랩탑 것을 그대로 두고 건너뜀. - helper_b05_*.js 둘은 랩탑이 .cjs 로 이미 올린 것과 **줄바꿈만 다른 같은 내용**이라 복사본을 도로 뺌(시험이 .cjs 를 부름). - resources/tester/ 에서 전체 1176 통과 · 29 건너뜀 · 실패 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
"""채집석 공제 — 캐서 쓴 돌만큼 사토가 준다 (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
|
||
)
|