# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 """M02 단가산출(SK) 엔진 입력 덧칸(PLAN 715-1b · `master_pick`) — 고친 줄 값 = 손셈. 줄 단가 덮개 = 손 단가 · 로직 줄 노 · 재 · 경 덮개 = 값묶음(일부면 엔진 값 위) · 덧줄 % 가 덮인 값에서 다시 셈 · 더한줄 참조(SK · SC) · 고정 = 1 단위 묶음 · 덧칸 없는 줄 = 엔진 값. """ from decimal import Decimal import pytest from M01_MasterData import M01_MasterData_Store as m01 from M02_MasterTemplete import M02_Labor_Doc as labor from M02_MasterTemplete import M02_Labor_Ref as refs from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Sangeun_Calc as calc from resources.tester.test_m02_labor_ref import MINE, world # noqa: F401 — 같은 임시 층 PCT = "GC001109" # 원/㎡ · 인력 둘 + 「노무비 * 3 / 100」 SUB = "GC000999" # 원/주 · 인력 둘 + 로직 줄(GC000275) def _whole(): return m01.loaded()[1] def _run(**doc): from M02_MasterTemplete import M02_Sangeun_Doc as sd whole = _whole() clean = sd.clean({"키": "SK-000001", "이름": "시험", "단위": "㎡", **doc}, None, whole) return labor._json(calc.run(clean, whole)) def test_price_cover_is_hand_price_and_pct_follows(): bare = labor.run_line(_whole(), PCT, {}) first, second = bare["줄"][0], bare["줄"][1] got = _run(바탕={"로직": PCT}, 덮개={"labor:LB000036#0": {"labor": 200000}}) line = got["줄"][0] assert line["단가"] == 200000 and line["출처"] == "손 단가" cut = labor.mcut.down # 751-3 — 줄 0.1 원 · 비목 합 1 원 버림 want = cut(Decimal(str(first["수량"])) * 200000, 1) + Decimal(str(second["금액"])) assert got["기준"]["노무비"] == pytest.approx(float(cut(want, 0))) assert got["기준"]["경비"] == pytest.approx(float(want * 3 / 100), abs=1) def test_bundle_cover_on_logic_line_partial(): bare = labor.run_line(_whole(), SUB, {}) sub = bare["줄"][2] got = _run(바탕={"로직": SUB}, 단위="주", 덮개={"logic:GC000275#0": {"expense": 1000}}) line = got["줄"][2] qty = Decimal(str(sub["수량"])) assert line["비목"]["경비"] == pytest.approx(float(qty * 1000)) assert line["비목"]["노무비"] == pytest.approx( float(sub["비목"]["노무비"]) ) # 안 덮은 칸 = 엔진 값 def test_untouched_lines_same_as_engine(): bare = labor.run_line(_whole(), SUB, {}) got = _run(바탕={"로직": SUB}, 단위="주") # 바탕 단위(728-2 — 성격 다르면 계수 칸) assert got["기준"]["계"] == pytest.approx(float(bare["기준"]["계"])) def test_added_ref_and_fixed_lines(world): """더한줄 = SK 참조 2 · 고정 줄 1 — 값 = 2 × 그 SK 1 단위 + 고정 단가.""" with store.at(world["personal"]): sk = refs.create( "sangeun", {"이름": "밑", "단위": "㎡", "바탕": {"로직": PCT}, "더한줄": []} )["이름"] unit = calc.calc_doc(store.read("sangeun", sk)["문서"])["1단위"]["계"] top = refs.create( "sangeun", { "이름": "위", "단위": "식", "바탕": None, "더한줄": [ {"참조": {**MINE, "키": sk}, "수량": 2}, {"고정": {"이름": "운반", "단위": "식", "단가": {"경비": 5000}}, "수량": 1}, ], }, )["이름"] got = calc.calc_doc(store.read("sangeun", top)["문서"]) assert got["기준"]["계"] == pytest.approx(unit * 2 + 5000, abs=1) expense = labor.run_line(_whole(), PCT, {})["기준"]["경비"] assert got["기준"]["경비"] == pytest.approx(5000 + 2 * float(expense), abs=1) def test_added_material_counts_unless_apart(): """사용자가 더한 재료 줄 = 기본 계 안(재료비 · 계에 더함) · 줄 「별도계상」 칸만 계 밖(재료 목록만).""" one = {"종류": "재료", "요소": "MT000010", "수량": 3, "단가": 1200} bare = _run(바탕={"로직": PCT}) inside = _run(바탕={"로직": PCT}, 더한줄=[one]) assert inside["기준"]["재료비"] == pytest.approx(bare["기준"]["재료비"] + 3600) assert inside["기준"]["계"] == pytest.approx(bare["기준"]["계"] + 3600, abs=1) assert any(x["cid"] == "add:#0" for x in inside["줄"]) apart = _run(바탕={"로직": PCT}, 더한줄=[{**one, "별도계상": True}]) assert apart["기준"]["계"] == pytest.approx(bare["기준"]["계"]) assert not any(x["cid"] == "add:#0" for x in apart["줄"]) assert any(m["금액"] == pytest.approx(3600) for m in apart["재료"])